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