    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}