1 package org.bouncycastle.asn1; 2 3 import java.io.ByteArrayOutputStream; 4 import java.io.EOFException; 5 import java.io.IOException; 6 import java.io.InputStream; 7 8 import org.bouncycastle.util.Arrays; 9 import org.bouncycastle.util.io.Streams; 10 11 /** 12 * Base class for BIT STRING objects 13 */ 14 public abstract class ASN1BitString 15 extends ASN1Primitive 16 implements ASN1String 17 { 18 private static final char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 19 20 protected final byte[] data; 21 protected final int padBits; 22 23 /** 24 * @param bitString an int containing the BIT STRING 25 * @return the correct number of pad bits for a bit string defined in 26 * a 32 bit constant 27 */ 28 static protected int getPadBits( 29 int bitString) 30 { 31 int val = 0; 32 for (int i = 3; i >= 0; i--) 33 { 34 // 35 // this may look a little odd, but if it isn't done like this pre jdk1.2 36 // JVM's break! 37 // 38 if (i != 0) 39 { 40 if ((bitString >> (i * 8)) != 0) 41 { 42 val = (bitString >> (i * 8)) & 0xFF; 43 break; 44 } 45 } 46 else 47 { 48 if (bitString != 0) 49 { 50 val = bitString & 0xFF; 51 break; 52 } 53 } 54 } 55 56 if (val == 0) 57 { 58 return 0; 59 } 60 61 62 int bits = 1; 63 64 while (((val <<= 1) & 0xFF) != 0) 65 { 66 bits++; 67 } 68 69 return 8 - bits; 70 } 71 72 /** 73 * @param bitString an int containing the BIT STRING 74 * @return the correct number of bytes for a bit string defined in 75 * a 32 bit constant 76 */ 77 static protected byte[] getBytes(int bitString) 78 { 79 if (bitString == 0) 80 { 81 return new byte[0]; 82 } 83 84 int bytes = 4; 85 for (int i = 3; i >= 1; i--) 86 { 87 if ((bitString & (0xFF << (i * 8))) != 0) 88 { 89 break; 90 } 91 bytes--; 92 } 93 94 byte[] result = new byte[bytes]; 95 for (int i = 0; i < bytes; i++) 96 { 97 result[i] = (byte) ((bitString >> (i * 8)) & 0xFF); 98 } 99 100 return result; 101 } 102 103 /** 104 * Base constructor. 105 * 106 * @param data the octets making up the bit string. 107 * @param padBits the number of extra bits at the end of the string. 108 */ 109 public ASN1BitString( 110 byte[] data, 111 int padBits) 112 { 113 if (data == null) 114 { 115 throw new NullPointerException("data cannot be null"); 116 } 117 if (data.length == 0 && padBits != 0) 118 { 119 throw new IllegalArgumentException("zero length data with non-zero pad bits"); 120 } 121 if (padBits > 7 || padBits < 0) 122 { 123 throw new IllegalArgumentException("pad bits cannot be greater than 7 or less than 0"); 124 } 125 126 this.data = Arrays.clone(data); 127 this.padBits = padBits; 128 } 129 130 /** 131 * Return a String representation of this BIT STRING 132 * 133 * @return a String representation. 134 */ 135 public String getString() 136 { 137 StringBuffer buf = new StringBuffer("#"); 138 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 139 ASN1OutputStream aOut = new ASN1OutputStream(bOut); 140 141 try 142 { 143 aOut.writeObject(this); 144 } 145 catch (IOException e) 146 { 147 throw new ASN1ParsingException("Internal error encoding BitString: " + e.getMessage(), e); 148 } 149 150 byte[] string = bOut.toByteArray(); 151 152 for (int i = 0; i != string.length; i++) 153 { 154 buf.append(table[(string[i] >>> 4) & 0xf]); 155 buf.append(table[string[i] & 0xf]); 156 } 157 158 return buf.toString(); 159 } 160 161 /** 162 * @return the value of the bit string as an int (truncating if necessary) 163 */ 164 public int intValue() 165 { 166 int value = 0; 167 byte[] string = data; 168 169 if (padBits > 0 && data.length <= 4) 170 { 171 string = derForm(data, padBits); 172 } 173 174 for (int i = 0; i != string.length && i != 4; i++) 175 { 176 value |= (string[i] & 0xff) << (8 * i); 177 } 178 179 return value; 180 } 181 182 /** 183 * Return the octets contained in this BIT STRING, checking that this BIT STRING really 184 * does represent an octet aligned string. Only use this method when the standard you are 185 * following dictates that the BIT STRING will be octet aligned. 186 * 187 * @return a copy of the octet aligned data. 188 */ 189 public byte[] getOctets() 190 { 191 if (padBits != 0) 192 { 193 throw new IllegalStateException("attempt to get non-octet aligned data from BIT STRING"); 194 } 195 196 return Arrays.clone(data); 197 } 198 199 public byte[] getBytes() 200 { 201 return derForm(data, padBits); 202 } 203 204 public int getPadBits() 205 { 206 return padBits; 207 } 208 209 public String toString() 210 { 211 return getString(); 212 } 213 214 public int hashCode() 215 { 216 return padBits ^ Arrays.hashCode(this.getBytes()); 217 } 218 219 protected boolean asn1Equals( 220 ASN1Primitive o) 221 { 222 if (!(o instanceof ASN1BitString)) 223 { 224 return false; 225 } 226 227 ASN1BitString other = (ASN1BitString)o; 228 229 return this.padBits == other.padBits 230 && Arrays.areEqual(this.getBytes(), other.getBytes()); 231 } 232 233 protected static byte[] derForm(byte[] data, int padBits) 234 { 235 byte[] rv = Arrays.clone(data); 236 // DER requires pad bits be zero 237 if (padBits > 0) 238 { 239 rv[data.length - 1] &= 0xff << padBits; 240 } 241 242 return rv; 243 } 244 245 static ASN1BitString fromInputStream(int length, InputStream stream) 246 throws IOException 247 { 248 if (length < 1) 249 { 250 throw new IllegalArgumentException("truncated BIT STRING detected"); 251 } 252 253 int padBits = stream.read(); 254 byte[] data = new byte[length - 1]; 255 256 if (data.length != 0) 257 { 258 if (Streams.readFully(stream, data) != data.length) 259 { 260 throw new EOFException("EOF encountered in middle of BIT STRING"); 261 } 262 263 if (padBits > 0 && padBits < 8) 264 { 265 if (data[data.length - 1] != (byte)(data[data.length - 1] & (0xff << padBits))) 266 { 267 return new DLBitString(data, padBits); 268 } 269 } 270 } 271 272 return new DERBitString(data, padBits); 273 } 274 275 public ASN1Primitive getLoadedObject() 276 { 277 return this.toASN1Primitive(); 278 } 279 280 ASN1Primitive toDERObject() 281 { 282 return new DERBitString(data, padBits); 283 } 284 285 ASN1Primitive toDLObject() 286 { 287 return new DLBitString(data, padBits); 288 } 289 290 abstract void encode(ASN1OutputStream out) 291 throws IOException; 292 } 293