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