001package org.apache.commons.ssl.asn1;
002
003import java.io.ByteArrayOutputStream;
004import java.io.IOException;
005
006public class DERBitString
007    extends ASN1Object
008    implements DERString {
009    private static final char[] table = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
010
011    protected byte[] data;
012    protected int padBits;
013
014    /**
015     * return the correct number of pad bits for a bit string defined in
016     * a 32 bit constant
017     */
018    static protected int getPadBits(
019        int bitString) {
020        int val = 0;
021        for (int i = 3; i >= 0; i--) {
022            //
023            // this may look a little odd, but if it isn't done like this pre jdk1.2
024            // JVM's break!
025            //
026            if (i != 0) {
027                if ((bitString >> (i * 8)) != 0) {
028                    val = (bitString >> (i * 8)) & 0xFF;
029                    break;
030                }
031            } else {
032                if (bitString != 0) {
033                    val = bitString & 0xFF;
034                    break;
035                }
036            }
037        }
038
039        if (val == 0) {
040            return 7;
041        }
042
043
044        int bits = 1;
045
046        while (((val <<= 1) & 0xFF) != 0) {
047            bits++;
048        }
049
050        return 8 - bits;
051    }
052
053    /**
054     * return the correct number of bytes for a bit string defined in
055     * a 32 bit constant
056     */
057    static protected byte[] getBytes(int bitString) {
058        int bytes = 4;
059        for (int i = 3; i >= 1; i--) {
060            if ((bitString & (0xFF << (i * 8))) != 0) {
061                break;
062            }
063            bytes--;
064        }
065
066        byte[] result = new byte[bytes];
067        for (int i = 0; i < bytes; i++) {
068            result[i] = (byte) ((bitString >> (i * 8)) & 0xFF);
069        }
070
071        return result;
072    }
073
074    /**
075     * return a Bit String from the passed in object
076     *
077     * @throws IllegalArgumentException if the object cannot be converted.
078     */
079    public static DERBitString getInstance(
080        Object obj) {
081        if (obj == null || obj instanceof DERBitString) {
082            return (DERBitString) obj;
083        }
084
085        if (obj instanceof ASN1OctetString) {
086            byte[] bytes = ((ASN1OctetString) obj).getOctets();
087            int padBits = bytes[0];
088            byte[] data = new byte[bytes.length - 1];
089
090            System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
091
092            return new DERBitString(data, padBits);
093        }
094
095        if (obj instanceof ASN1TaggedObject) {
096            return getInstance(((ASN1TaggedObject) obj).getObject());
097        }
098
099        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
100    }
101
102    /**
103     * return a Bit String from a tagged object.
104     *
105     * @param obj      the tagged object holding the object we want
106     * @param explicit true if the object is meant to be explicitly
107     *                 tagged false otherwise.
108     * @throws IllegalArgumentException if the tagged object cannot
109     *                                  be converted.
110     */
111    public static DERBitString getInstance(
112        ASN1TaggedObject obj,
113        boolean explicit) {
114        return getInstance(obj.getObject());
115    }
116
117    protected DERBitString(
118        byte data,
119        int padBits) {
120        this.data = new byte[1];
121        this.data[0] = data;
122        this.padBits = padBits;
123    }
124
125    /**
126     * @param data    the octets making up the bit string.
127     * @param padBits the number of extra bits at the end of the string.
128     */
129    public DERBitString(
130        byte[] data,
131        int padBits) {
132        this.data = data;
133        this.padBits = padBits;
134    }
135
136    public DERBitString(
137        byte[] data) {
138        this(data, 0);
139    }
140
141    public DERBitString(
142        DEREncodable obj) {
143        try {
144            ByteArrayOutputStream bOut = new ByteArrayOutputStream();
145            DEROutputStream dOut = new DEROutputStream(bOut);
146
147            dOut.writeObject(obj);
148            dOut.close();
149
150            this.data = bOut.toByteArray();
151            this.padBits = 0;
152        }
153        catch (IOException e) {
154            throw new IllegalArgumentException("Error processing object : " + e.toString());
155        }
156    }
157
158    public byte[] getBytes() {
159        return data;
160    }
161
162    public int getPadBits() {
163        return padBits;
164    }
165
166
167    /** @return the value of the bit string as an int (truncating if necessary) */
168    public int intValue() {
169        int value = 0;
170
171        for (int i = 0; i != data.length && i != 4; i++) {
172            value |= (data[i] & 0xff) << (8 * i);
173        }
174
175        return value;
176    }
177
178    void encode(
179        DEROutputStream out)
180        throws IOException {
181        byte[] bytes = new byte[getBytes().length + 1];
182
183        bytes[0] = (byte) getPadBits();
184        System.arraycopy(getBytes(), 0, bytes, 1, bytes.length - 1);
185
186        out.writeEncoded(BIT_STRING, bytes);
187    }
188
189    public int hashCode() {
190        int value = 0;
191
192        for (int i = 0; i != data.length; i++) {
193            value ^= (data[i] & 0xff) << (i % 4);
194        }
195
196        return value;
197    }
198
199    protected boolean asn1Equals(
200        DERObject o) {
201        if (!(o instanceof DERBitString)) {
202            return false;
203        }
204
205        DERBitString other = (DERBitString) o;
206
207        if (data.length != other.data.length) {
208            return false;
209        }
210
211        for (int i = 0; i != data.length; i++) {
212            if (data[i] != other.data[i]) {
213                return false;
214            }
215        }
216
217        return (padBits == other.padBits);
218    }
219
220    public String getString() {
221        StringBuffer buf = new StringBuffer("#");
222        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
223        ASN1OutputStream aOut = new ASN1OutputStream(bOut);
224
225        try {
226            aOut.writeObject(this);
227        }
228        catch (IOException e) {
229            throw new RuntimeException("internal error encoding BitString");
230        }
231
232        byte[] string = bOut.toByteArray();
233
234        for (int i = 0; i != string.length; i++) {
235            buf.append(table[(string[i] >>> 4) & 0xf]);
236            buf.append(table[string[i] & 0xf]);
237        }
238
239        return buf.toString();
240    }
241
242    public String toString() {
243        return getString();
244    }
245}