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 */ getPadBits( int bitString)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 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 102 /** 103 * Base constructor. 104 * 105 * @param data the octets making up the bit string. 106 * @param padBits the number of extra bits at the end of the string. 107 */ ASN1BitString( byte[] data, int padBits)108 public ASN1BitString( 109 byte[] data, 110 int padBits) 111 { 112 if (data == null) 113 { 114 throw new NullPointerException("data cannot be null"); 115 } 116 if (data.length == 0 && padBits != 0) 117 { 118 throw new IllegalArgumentException("zero length data with non-zero pad bits"); 119 } 120 if (padBits > 7 || padBits < 0) 121 { 122 throw new IllegalArgumentException("pad bits cannot be greater than 7 or less than 0"); 123 } 124 125 this.data = Arrays.clone(data); 126 this.padBits = padBits; 127 } 128 129 /** 130 * Return a String representation of this BIT STRING 131 * 132 * @return a String representation. 133 */ getString()134 public String getString() 135 { 136 StringBuffer buf = new StringBuffer("#"); 137 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 138 ASN1OutputStream aOut = new ASN1OutputStream(bOut); 139 140 try 141 { 142 aOut.writeObject(this); 143 } 144 catch (IOException e) 145 { 146 throw new ASN1ParsingException("Internal error encoding BitString: " + e.getMessage(), e); 147 } 148 149 byte[] string = bOut.toByteArray(); 150 151 for (int i = 0; i != string.length; i++) 152 { 153 buf.append(table[(string[i] >>> 4) & 0xf]); 154 buf.append(table[string[i] & 0xf]); 155 } 156 157 return buf.toString(); 158 } 159 160 /** 161 * @return the value of the bit string as an int (truncating if necessary) 162 */ intValue()163 public int intValue() 164 { 165 int value = 0; 166 byte[] string = data; 167 168 if (padBits > 0 && data.length <= 4) 169 { 170 string = derForm(data, padBits); 171 } 172 173 for (int i = 0; i != string.length && i != 4; i++) 174 { 175 value |= (string[i] & 0xff) << (8 * i); 176 } 177 178 return value; 179 } 180 181 /** 182 * Return the octets contained in this BIT STRING, checking that this BIT STRING really 183 * does represent an octet aligned string. Only use this method when the standard you are 184 * following dictates that the BIT STRING will be octet aligned. 185 * 186 * @return a copy of the octet aligned data. 187 */ getOctets()188 public byte[] getOctets() 189 { 190 if (padBits != 0) 191 { 192 throw new IllegalStateException("attempt to get non-octet aligned data from BIT STRING"); 193 } 194 195 return Arrays.clone(data); 196 } 197 getBytes()198 public byte[] getBytes() 199 { 200 return derForm(data, padBits); 201 } 202 getPadBits()203 public int getPadBits() 204 { 205 return padBits; 206 } 207 toString()208 public String toString() 209 { 210 return getString(); 211 } 212 hashCode()213 public int hashCode() 214 { 215 return padBits ^ Arrays.hashCode(this.getBytes()); 216 } 217 asn1Equals( ASN1Primitive o)218 protected boolean asn1Equals( 219 ASN1Primitive o) 220 { 221 if (!(o instanceof ASN1BitString)) 222 { 223 return false; 224 } 225 226 ASN1BitString other = (ASN1BitString)o; 227 228 return this.padBits == other.padBits 229 && Arrays.areEqual(this.getBytes(), other.getBytes()); 230 } 231 derForm(byte[] data, int padBits)232 protected static byte[] derForm(byte[] data, int padBits) 233 { 234 byte[] rv = Arrays.clone(data); 235 // DER requires pad bits be zero 236 if (padBits > 0) 237 { 238 rv[data.length - 1] &= 0xff << padBits; 239 } 240 241 return rv; 242 } 243 fromInputStream(int length, InputStream stream)244 static ASN1BitString fromInputStream(int length, InputStream stream) 245 throws IOException 246 { 247 if (length < 1) 248 { 249 throw new IllegalArgumentException("truncated BIT STRING detected"); 250 } 251 252 int padBits = stream.read(); 253 byte[] data = new byte[length - 1]; 254 255 if (data.length != 0) 256 { 257 if (Streams.readFully(stream, data) != data.length) 258 { 259 throw new EOFException("EOF encountered in middle of BIT STRING"); 260 } 261 262 if (padBits > 0 && padBits < 8) 263 { 264 if (data[data.length - 1] != (byte)(data[data.length - 1] & (0xff << padBits))) 265 { 266 return new DLBitString(data, padBits); 267 } 268 } 269 } 270 271 return new DERBitString(data, padBits); 272 } 273 getLoadedObject()274 public ASN1Primitive getLoadedObject() 275 { 276 return this.toASN1Primitive(); 277 } 278 toDERObject()279 ASN1Primitive toDERObject() 280 { 281 return new DERBitString(data, padBits); 282 } 283 toDLObject()284 ASN1Primitive toDLObject() 285 { 286 return new DLBitString(data, padBits); 287 } 288 encode(ASN1OutputStream out)289 abstract void encode(ASN1OutputStream out) 290 throws IOException; 291 } 292