001    package org.apache.commons.ssl.asn1;
002    
003    import java.io.ByteArrayOutputStream;
004    import java.io.IOException;
005    
006    public 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    }