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