001package org.apache.commons.ssl.org.bouncycastle.asn1;
002
003import java.io.ByteArrayOutputStream;
004import java.io.EOFException;
005import java.io.IOException;
006import java.io.InputStream;
007
008import org.bouncycastle.util.Arrays;
009import org.bouncycastle.util.io.Streams;
010
011public class DERBitString
012    extends ASN1Primitive
013    implements ASN1String
014{
015    private static final char[]  table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
016    
017    protected byte[]      data;
018    protected int         padBits;
019
020    /**
021     * @param bitString an int containing the BIT STRING
022     * @return the correct number of pad bits for a bit string defined in
023     * a 32 bit constant
024     */
025    static protected int getPadBits(
026        int bitString)
027    {
028        int val = 0;
029        for (int i = 3; i >= 0; i--) 
030        {
031            //
032            // this may look a little odd, but if it isn't done like this pre jdk1.2
033            // JVM's break!
034            //
035            if (i != 0)
036            {
037                if ((bitString >> (i * 8)) != 0) 
038                {
039                    val = (bitString >> (i * 8)) & 0xFF;
040                    break;
041                }
042            }
043            else
044            {
045                if (bitString != 0)
046                {
047                    val = bitString & 0xFF;
048                    break;
049                }
050            }
051        }
052 
053        if (val == 0)
054        {
055            return 7;
056        }
057
058
059        int bits = 1;
060
061        while (((val <<= 1) & 0xFF) != 0)
062        {
063            bits++;
064        }
065
066        return 8 - bits;
067    }
068
069    /**
070     * @param bitString an int containing the BIT STRING
071     * @return the correct number of bytes for a bit string defined in
072     * a 32 bit constant
073     */
074    static protected byte[] getBytes(int bitString)
075    {
076        int bytes = 4;
077        for (int i = 3; i >= 1; i--)
078        {
079            if ((bitString & (0xFF << (i * 8))) != 0)
080            {
081                break;
082            }
083            bytes--;
084        }
085        
086        byte[] result = new byte[bytes];
087        for (int i = 0; i < bytes; i++)
088        {
089            result[i] = (byte) ((bitString >> (i * 8)) & 0xFF);
090        }
091
092        return result;
093    }
094
095    /**
096     * return a Bit String from the passed in object
097     *
098     * @param obj a DERBitString or an object that can be converted into one.
099     * @exception IllegalArgumentException if the object cannot be converted.
100     * @return a DERBitString instance, or null.
101     */
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     */
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    
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     */
152    public DERBitString(
153        byte[]  data,
154        int     padBits)
155    {
156        this.data = data;
157        this.padBits = padBits;
158    }
159
160    public DERBitString(
161        byte[]  data)
162    {
163        this(data, 0);
164    }
165
166    public DERBitString(
167        int value)
168    {
169        this.data = getBytes(value);
170        this.padBits = getPadBits(value);
171    }
172
173    public DERBitString(
174        ASN1Encodable obj)
175        throws IOException
176    {
177        this.data = obj.toASN1Primitive().getEncoded(ASN1Encoding.DER);
178        this.padBits = 0;
179    }
180
181    public byte[] getBytes()
182    {
183        return data;
184    }
185
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     */
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
207    boolean isConstructed()
208    {
209        return false;
210    }
211
212    int encodedLength()
213    {
214        return 1 + StreamUtil.calculateBodyLength(data.length + 1) + data.length + 1;
215    }
216
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
229    public int hashCode()
230    {
231        return padBits ^ Arrays.hashCode(data);
232    }
233
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
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
274    public String toString()
275    {
276        return getString();
277    }
278
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
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}