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