001    /**
002     *
003     *  Licensed to the Apache Software Foundation (ASF) under one or more
004     *  contributor license agreements.  See the NOTICE file distributed with
005     *  this work for additional information regarding copyright ownership.
006     *  The ASF licenses this file to You under the Apache License, Version 2.0
007     *  (the "License"); you may not use this file except in compliance with
008     *  the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     *  Unless required by applicable law or agreed to in writing, software
013     *  distributed under the License is distributed on an "AS IS" BASIS,
014     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     *  See the License for the specific language governing permissions and
016     *  limitations under the License.
017     */
018    
019    package org.apache.geronimo.util.asn1;
020    
021    import java.io.ByteArrayOutputStream;
022    import java.io.IOException;
023    
024    public class DERBitString
025        extends DERObject
026        implements DERString
027    {
028        private static final char[]  table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
029    
030        protected byte[]      data;
031        protected int         padBits;
032    
033        /**
034         * return the correct number of pad bits for a bit string defined in
035         * a 32 bit constant
036         */
037        static protected int getPadBits(
038            int bitString)
039        {
040            int val = 0;
041            for (int i = 3; i >= 0; i--)
042            {
043                //
044                // this may look a little odd, but if it isn't done like this pre jdk1.2
045                // JVM's break!
046                //
047                if (i != 0)
048                {
049                    if ((bitString >> (i * 8)) != 0)
050                    {
051                        val = (bitString >> (i * 8)) & 0xFF;
052                        break;
053                    }
054                }
055                else
056                {
057                    if (bitString != 0)
058                    {
059                        val = bitString & 0xFF;
060                        break;
061                    }
062                }
063            }
064    
065            if (val == 0)
066            {
067                return 7;
068            }
069    
070    
071            int bits = 1;
072    
073            while (((val <<= 1) & 0xFF) != 0)
074            {
075                bits++;
076            }
077    
078            return 8 - bits;
079        }
080    
081        /**
082         * return the correct number of bytes for a bit string defined in
083         * a 32 bit constant
084         */
085        static protected byte[] getBytes(int bitString)
086        {
087            int bytes = 4;
088            for (int i = 3; i >= 1; i--)
089            {
090                if ((bitString & (0xFF << (i * 8))) != 0)
091                {
092                    break;
093                }
094                bytes--;
095            }
096    
097            byte[] result = new byte[bytes];
098            for (int i = 0; i < bytes; i++)
099            {
100                result[i] = (byte) ((bitString >> (i * 8)) & 0xFF);
101            }
102    
103            return result;
104        }
105    
106        /**
107         * return a Bit String from the passed in object
108         *
109         * @exception IllegalArgumentException if the object cannot be converted.
110         */
111        public static DERBitString getInstance(
112            Object  obj)
113        {
114            if (obj == null || obj instanceof DERBitString)
115            {
116                return (DERBitString)obj;
117            }
118    
119            if (obj instanceof ASN1OctetString)
120            {
121                byte[]  bytes = ((ASN1OctetString)obj).getOctets();
122                int     padBits = bytes[0];
123                byte[]  data = new byte[bytes.length - 1];
124    
125                System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
126    
127                return new DERBitString(data, padBits);
128            }
129    
130            if (obj instanceof ASN1TaggedObject)
131            {
132                return getInstance(((ASN1TaggedObject)obj).getObject());
133            }
134    
135            throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
136        }
137    
138        /**
139         * return a Bit String from a tagged object.
140         *
141         * @param obj the tagged object holding the object we want
142         * @param explicit true if the object is meant to be explicitly
143         *              tagged false otherwise.
144         * @exception IllegalArgumentException if the tagged object cannot
145         *               be converted.
146         */
147        public static DERBitString getInstance(
148            ASN1TaggedObject obj,
149            boolean          explicit)
150        {
151            return getInstance(obj.getObject());
152        }
153    
154        protected DERBitString(
155            byte    data,
156            int     padBits)
157        {
158            this.data = new byte[1];
159            this.data[0] = data;
160            this.padBits = padBits;
161        }
162    
163        /**
164         * @param data the octets making up the bit string.
165         * @param padBits the number of extra bits at the end of the string.
166         */
167        public DERBitString(
168            byte[]  data,
169            int     padBits)
170        {
171            this.data = data;
172            this.padBits = padBits;
173        }
174    
175        public DERBitString(
176            byte[]  data)
177        {
178            this(data, 0);
179        }
180    
181        public DERBitString(
182            DEREncodable  obj)
183        {
184            try
185            {
186                ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
187                DEROutputStream         dOut = new DEROutputStream(bOut);
188    
189                dOut.writeObject(obj);
190                dOut.close();
191    
192                this.data = bOut.toByteArray();
193                this.padBits = 0;
194            }
195            catch (IOException e)
196            {
197                throw new IllegalArgumentException("Error processing object : " + e.toString());
198            }
199        }
200    
201        public byte[] getBytes()
202        {
203            return data;
204        }
205    
206        public int getPadBits()
207        {
208            return padBits;
209        }
210    
211    
212        /**
213         * @return the value of the bit string as an int (truncating if necessary)
214         */
215        public int intValue()
216        {
217            int value = 0;
218    
219            for (int i = 0; i != data.length && i != 4; i++)
220            {
221                value |= (data[i] & 0xff) << (8 * i);
222            }
223    
224            return value;
225        }
226    
227        void encode(
228            DEROutputStream  out)
229            throws IOException
230        {
231            byte[]  bytes = new byte[getBytes().length + 1];
232    
233            bytes[0] = (byte)getPadBits();
234            System.arraycopy(getBytes(), 0, bytes, 1, bytes.length - 1);
235    
236            out.writeEncoded(BIT_STRING, bytes);
237        }
238    
239        public int hashCode()
240        {
241            int     value = 0;
242    
243            for (int i = 0; i != data.length; i++)
244            {
245                value ^= (data[i] & 0xff) << (i % 4);
246            }
247    
248            return value;
249        }
250    
251        public boolean equals(
252            Object  o)
253        {
254            if (o == null || !(o instanceof DERBitString))
255            {
256                return false;
257            }
258    
259            DERBitString  other = (DERBitString)o;
260    
261            if (data.length != other.data.length)
262            {
263                return false;
264            }
265    
266            for (int i = 0; i != data.length; i++)
267            {
268                if (data[i] != other.data[i])
269                {
270                    return false;
271                }
272            }
273    
274            return (padBits == other.padBits);
275        }
276    
277        public String getString()
278        {
279            StringBuffer          buf = new StringBuffer("#");
280            ByteArrayOutputStream bOut = new ByteArrayOutputStream();
281            ASN1OutputStream      aOut = new ASN1OutputStream(bOut);
282    
283            try
284            {
285                aOut.writeObject(this);
286            }
287            catch (IOException e)
288            {
289               throw new RuntimeException("internal error encoding BitString");
290            }
291    
292            byte[]    string = bOut.toByteArray();
293    
294            for (int i = 0; i != string.length; i++)
295            {
296                buf.append(table[(string[i] >>> 4) % 0xf]);
297                buf.append(table[string[i] & 0xf]);
298            }
299    
300            return buf.toString();
301        }
302    }