1package org.bouncycastle.asn1;
2
3import java.io.ByteArrayOutputStream;
4import java.io.IOException;
5import java.io.OutputStream;
6import java.math.BigInteger;
7
8public class DERObjectIdentifier
9    extends DERObject
10{
11    String      identifier;
12
13    /**
14     * return an OID from the passed in object
15     *
16     * @exception IllegalArgumentException if the object cannot be converted.
17     */
18    public static DERObjectIdentifier getInstance(
19        Object  obj)
20    {
21        if (obj == null || obj instanceof DERObjectIdentifier)
22        {
23            return (DERObjectIdentifier)obj;
24        }
25
26        if (obj instanceof ASN1OctetString)
27        {
28            return new DERObjectIdentifier(((ASN1OctetString)obj).getOctets());
29        }
30
31        if (obj instanceof ASN1TaggedObject)
32        {
33            return getInstance(((ASN1TaggedObject)obj).getObject());
34        }
35
36        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
37    }
38
39    /**
40     * return an Object Identifier from a tagged object.
41     *
42     * @param obj the tagged object holding the object we want
43     * @param explicit true if the object is meant to be explicitly
44     *              tagged false otherwise.
45     * @exception IllegalArgumentException if the tagged object cannot
46     *               be converted.
47     */
48    public static DERObjectIdentifier getInstance(
49        ASN1TaggedObject obj,
50        boolean          explicit)
51    {
52        return getInstance(obj.getObject());
53    }
54
55
56    DERObjectIdentifier(
57        byte[]  bytes)
58    {
59        StringBuffer    objId = new StringBuffer();
60        long            value = 0;
61        BigInteger      bigValue = null;
62        boolean         first = true;
63
64        for (int i = 0; i != bytes.length; i++)
65        {
66            int b = bytes[i] & 0xff;
67
68            if (value < 0x80000000000000L)
69            {
70                value = value * 128 + (b & 0x7f);
71                if ((b & 0x80) == 0)             // end of number reached
72                {
73                    if (first)
74                    {
75                        switch ((int)value / 40)
76                        {
77                        case 0:
78                            objId.append('0');
79                            break;
80                        case 1:
81                            objId.append('1');
82                            value -= 40;
83                            break;
84                        default:
85                            objId.append('2');
86                            value -= 80;
87                        }
88                        first = false;
89                    }
90
91                    objId.append('.');
92                    objId.append(value);
93                    value = 0;
94                }
95            }
96            else
97            {
98                if (bigValue == null)
99                {
100                    bigValue = BigInteger.valueOf(value);
101                }
102                bigValue = bigValue.shiftLeft(7);
103                bigValue = bigValue.or(BigInteger.valueOf(b & 0x7f));
104                if ((b & 0x80) == 0)
105                {
106                    objId.append('.');
107                    objId.append(bigValue);
108                    bigValue = null;
109                    value = 0;
110                }
111            }
112        }
113
114        // BEGIN android-changed
115        /*
116         * Intern the identifier so there aren't hundreds of duplicates
117         * (in practice).
118         */
119        this.identifier = objId.toString().intern();
120        // END android-changed
121    }
122
123    public DERObjectIdentifier(
124        String  identifier)
125    {
126        if (!isValidIdentifier(identifier))
127        {
128            throw new IllegalArgumentException("string " + identifier + " not an OID");
129        }
130
131        // BEGIN android-changed
132        /*
133         * Intern the identifier so there aren't hundreds of duplicates
134         * (in practice).
135         */
136        this.identifier = identifier.intern();
137        // END android-changed
138    }
139
140    public String getId()
141    {
142        return identifier;
143    }
144
145    private void writeField(
146        OutputStream    out,
147        long            fieldValue)
148        throws IOException
149    {
150        if (fieldValue >= (1L << 7))
151        {
152            if (fieldValue >= (1L << 14))
153            {
154                if (fieldValue >= (1L << 21))
155                {
156                    if (fieldValue >= (1L << 28))
157                    {
158                        if (fieldValue >= (1L << 35))
159                        {
160                            if (fieldValue >= (1L << 42))
161                            {
162                                if (fieldValue >= (1L << 49))
163                                {
164                                    if (fieldValue >= (1L << 56))
165                                    {
166                                        out.write((int)(fieldValue >> 56) | 0x80);
167                                    }
168                                    out.write((int)(fieldValue >> 49) | 0x80);
169                                }
170                                out.write((int)(fieldValue >> 42) | 0x80);
171                            }
172                            out.write((int)(fieldValue >> 35) | 0x80);
173                        }
174                        out.write((int)(fieldValue >> 28) | 0x80);
175                    }
176                    out.write((int)(fieldValue >> 21) | 0x80);
177                }
178                out.write((int)(fieldValue >> 14) | 0x80);
179            }
180            out.write((int)(fieldValue >> 7) | 0x80);
181        }
182        out.write((int)fieldValue & 0x7f);
183    }
184
185    private void writeField(
186        OutputStream    out,
187        BigInteger      fieldValue)
188        throws IOException
189    {
190        int byteCount = (fieldValue.bitLength()+6)/7;
191        if (byteCount == 0)
192        {
193            out.write(0);
194        }
195        else
196        {
197            BigInteger tmpValue = fieldValue;
198            byte[] tmp = new byte[byteCount];
199            for (int i = byteCount-1; i >= 0; i--)
200            {
201                tmp[i] = (byte) ((tmpValue.intValue() & 0x7f) | 0x80);
202                tmpValue = tmpValue.shiftRight(7);
203            }
204            tmp[byteCount-1] &= 0x7f;
205            out.write(tmp);
206        }
207
208    }
209
210    void encode(
211        DEROutputStream out)
212        throws IOException
213    {
214        OIDTokenizer            tok = new OIDTokenizer(identifier);
215        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
216        DEROutputStream         dOut = new DEROutputStream(bOut);
217
218        writeField(bOut,
219                    Integer.parseInt(tok.nextToken()) * 40
220                    + Integer.parseInt(tok.nextToken()));
221
222        while (tok.hasMoreTokens())
223        {
224            String token = tok.nextToken();
225            if (token.length() < 18)
226            {
227                writeField(bOut, Long.parseLong(token));
228            }
229            else
230            {
231                writeField(bOut, new BigInteger(token));
232            }
233        }
234
235        dOut.close();
236
237        byte[]  bytes = bOut.toByteArray();
238
239        out.writeEncoded(OBJECT_IDENTIFIER, bytes);
240    }
241
242    public int hashCode()
243    {
244        return identifier.hashCode();
245    }
246
247    public boolean equals(
248        Object  o)
249    {
250        if ((o == null) || !(o instanceof DERObjectIdentifier))
251        {
252            return false;
253        }
254
255        return identifier.equals(((DERObjectIdentifier)o).identifier);
256    }
257
258    public String toString()
259    {
260        return getId();
261    }
262
263    private static boolean isValidIdentifier(
264        String identifier)
265    {
266        boolean periodAllowed = false;
267        for (int i = identifier.length() - 1; i >= 0; i--)
268        {
269            char ch = identifier.charAt(i);
270
271            if ('0' <= ch && ch <= '9')
272            {
273                periodAllowed = true;
274                continue;
275            }
276
277            if (ch == '.')
278            {
279                if (!periodAllowed)
280                {
281                    return false;
282                }
283
284                periodAllowed = false;
285                continue;
286            }
287
288            return false;
289        }
290
291        return periodAllowed;
292    }
293}
294