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 public class DERBitString
12     extends ASN1Primitive
13     implements ASN1String
14 {
15     private static final char[]  table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
16 
17     protected byte[]      data;
18     protected int         padBits;
19 
20     /**
21      * @param bitString an int containing the BIT STRING
22      * @return the correct number of pad bits for a bit string defined in
23      * a 32 bit constant
24      */
getPadBits( int bitString)25     static protected int getPadBits(
26         int bitString)
27     {
28         int val = 0;
29         for (int i = 3; i >= 0; i--)
30         {
31             //
32             // this may look a little odd, but if it isn't done like this pre jdk1.2
33             // JVM's break!
34             //
35             if (i != 0)
36             {
37                 if ((bitString >> (i * 8)) != 0)
38                 {
39                     val = (bitString >> (i * 8)) & 0xFF;
40                     break;
41                 }
42             }
43             else
44             {
45                 if (bitString != 0)
46                 {
47                     val = bitString & 0xFF;
48                     break;
49                 }
50             }
51         }
52 
53         if (val == 0)
54         {
55             return 7;
56         }
57 
58 
59         int bits = 1;
60 
61         while (((val <<= 1) & 0xFF) != 0)
62         {
63             bits++;
64         }
65 
66         return 8 - bits;
67     }
68 
69     /**
70      * @param bitString an int containing the BIT STRING
71      * @return the correct number of bytes for a bit string defined in
72      * a 32 bit constant
73      */
getBytes(int bitString)74     static protected byte[] getBytes(int bitString)
75     {
76         int bytes = 4;
77         for (int i = 3; i >= 1; i--)
78         {
79             if ((bitString & (0xFF << (i * 8))) != 0)
80             {
81                 break;
82             }
83             bytes--;
84         }
85 
86         byte[] result = new byte[bytes];
87         for (int i = 0; i < bytes; i++)
88         {
89             result[i] = (byte) ((bitString >> (i * 8)) & 0xFF);
90         }
91 
92         return result;
93     }
94 
95     /**
96      * return a Bit String from the passed in object
97      *
98      * @param obj a DERBitString or an object that can be converted into one.
99      * @exception IllegalArgumentException if the object cannot be converted.
100      * @return a DERBitString instance, or null.
101      */
getInstance( Object obj)102     public static DERBitString getInstance(
103         Object  obj)
104     {
105         if (obj == null || obj instanceof DERBitString)
106         {
107             return (DERBitString)obj;
108         }
109 
110         throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
111     }
112 
113     /**
114      * return a Bit String from a tagged object.
115      *
116      * @param obj the tagged object holding the object we want
117      * @param explicit true if the object is meant to be explicitly
118      *              tagged false otherwise.
119      * @exception IllegalArgumentException if the tagged object cannot
120      *               be converted.
121      * @return a DERBitString instance, or null.
122      */
getInstance( ASN1TaggedObject obj, boolean explicit)123     public static DERBitString getInstance(
124         ASN1TaggedObject obj,
125         boolean          explicit)
126     {
127         ASN1Primitive o = obj.getObject();
128 
129         if (explicit || o instanceof DERBitString)
130         {
131             return getInstance(o);
132         }
133         else
134         {
135             return fromOctetString(((ASN1OctetString)o).getOctets());
136         }
137     }
138 
DERBitString( byte data, int padBits)139     protected DERBitString(
140         byte    data,
141         int     padBits)
142     {
143         this.data = new byte[1];
144         this.data[0] = data;
145         this.padBits = padBits;
146     }
147 
148     /**
149      * @param data the octets making up the bit string.
150      * @param padBits the number of extra bits at the end of the string.
151      */
DERBitString( byte[] data, int padBits)152     public DERBitString(
153         byte[]  data,
154         int     padBits)
155     {
156         this.data = data;
157         this.padBits = padBits;
158     }
159 
DERBitString( byte[] data)160     public DERBitString(
161         byte[]  data)
162     {
163         this(data, 0);
164     }
165 
DERBitString( int value)166     public DERBitString(
167         int value)
168     {
169         this.data = getBytes(value);
170         this.padBits = getPadBits(value);
171     }
172 
DERBitString( ASN1Encodable obj)173     public DERBitString(
174         ASN1Encodable obj)
175         throws IOException
176     {
177         this.data = obj.toASN1Primitive().getEncoded(ASN1Encoding.DER);
178         this.padBits = 0;
179     }
180 
getBytes()181     public byte[] getBytes()
182     {
183         return data;
184     }
185 
getPadBits()186     public int getPadBits()
187     {
188         return padBits;
189     }
190 
191 
192     /**
193      * @return the value of the bit string as an int (truncating if necessary)
194      */
intValue()195     public int intValue()
196     {
197         int value = 0;
198 
199         for (int i = 0; i != data.length && i != 4; i++)
200         {
201             value |= (data[i] & 0xff) << (8 * i);
202         }
203 
204         return value;
205     }
206 
isConstructed()207     boolean isConstructed()
208     {
209         return false;
210     }
211 
encodedLength()212     int encodedLength()
213     {
214         return 1 + StreamUtil.calculateBodyLength(data.length + 1) + data.length + 1;
215     }
216 
encode( ASN1OutputStream out)217     void encode(
218         ASN1OutputStream  out)
219         throws IOException
220     {
221         byte[]  bytes = new byte[getBytes().length + 1];
222 
223         bytes[0] = (byte)getPadBits();
224         System.arraycopy(getBytes(), 0, bytes, 1, bytes.length - 1);
225 
226         out.writeEncoded(BERTags.BIT_STRING, bytes);
227     }
228 
hashCode()229     public int hashCode()
230     {
231         return padBits ^ Arrays.hashCode(data);
232     }
233 
asn1Equals( ASN1Primitive o)234     protected boolean asn1Equals(
235         ASN1Primitive  o)
236     {
237         if (!(o instanceof DERBitString))
238         {
239             return false;
240         }
241 
242         DERBitString other = (DERBitString)o;
243 
244         return this.padBits == other.padBits
245             && Arrays.areEqual(this.data, other.data);
246     }
247 
getString()248     public String getString()
249     {
250         StringBuffer          buf = new StringBuffer("#");
251         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
252         ASN1OutputStream      aOut = new ASN1OutputStream(bOut);
253 
254         try
255         {
256             aOut.writeObject(this);
257         }
258         catch (IOException e)
259         {
260            throw new RuntimeException("internal error encoding BitString");
261         }
262 
263         byte[]    string = bOut.toByteArray();
264 
265         for (int i = 0; i != string.length; i++)
266         {
267             buf.append(table[(string[i] >>> 4) & 0xf]);
268             buf.append(table[string[i] & 0xf]);
269         }
270 
271         return buf.toString();
272     }
273 
toString()274     public String toString()
275     {
276         return getString();
277     }
278 
fromOctetString(byte[] bytes)279     static DERBitString fromOctetString(byte[] bytes)
280     {
281         if (bytes.length < 1)
282         {
283             throw new IllegalArgumentException("truncated BIT STRING detected");
284         }
285 
286         int padBits = bytes[0];
287         byte[] data = new byte[bytes.length - 1];
288 
289         if (data.length != 0)
290         {
291             System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
292         }
293 
294         return new DERBitString(data, padBits);
295     }
296 
fromInputStream(int length, InputStream stream)297     static DERBitString fromInputStream(int length, InputStream stream)
298         throws IOException
299     {
300         if (length < 1)
301         {
302             throw new IllegalArgumentException("truncated BIT STRING detected");
303         }
304 
305         int padBits = stream.read();
306         byte[] data = new byte[length - 1];
307 
308         if (data.length != 0)
309         {
310             if (Streams.readFully(stream, data) != data.length)
311             {
312                 throw new EOFException("EOF encountered in middle of BIT STRING");
313             }
314         }
315 
316         return new DERBitString(data, padBits);
317     }
318 }
319