Home | History | Annotate | Download | only in asn1
      1 package org.bouncycastle.asn1;
      2 
      3 import java.io.ByteArrayOutputStream;
      4 import java.io.IOException;
      5 import java.math.BigInteger;
      6 
      7 import org.bouncycastle.util.Arrays;
      8 
      9 /**
     10  * Use ASN1ObjectIdentifier instead of this,
     11  */
     12 public class DERObjectIdentifier
     13     extends ASN1Primitive
     14 {
     15     String identifier;
     16 
     17     private byte[] body;
     18 
     19     /**
     20      * return an OID from the passed in object
     21      *
     22      * @throws IllegalArgumentException if the object cannot be converted.
     23      */
     24     public static ASN1ObjectIdentifier getInstance(
     25         Object obj)
     26     {
     27         if (obj == null || obj instanceof ASN1ObjectIdentifier)
     28         {
     29             return (ASN1ObjectIdentifier)obj;
     30         }
     31 
     32         if (obj instanceof DERObjectIdentifier)
     33         {
     34             return new ASN1ObjectIdentifier(((DERObjectIdentifier)obj).getId());
     35         }
     36 
     37         if (obj instanceof ASN1Encodable && ((ASN1Encodable)obj).toASN1Primitive() instanceof ASN1ObjectIdentifier)
     38         {
     39             return (ASN1ObjectIdentifier)((ASN1Encodable)obj).toASN1Primitive();
     40         }
     41 
     42         if (obj instanceof byte[])
     43         {
     44             byte[] enc = (byte[])obj;
     45             if (enc[0] == BERTags.OBJECT_IDENTIFIER)
     46             {
     47                 try
     48                 {
     49                     return (ASN1ObjectIdentifier)fromByteArray(enc);
     50                 }
     51                 catch (IOException e)
     52                 {
     53                     throw new IllegalArgumentException("failed to construct sequence from byte[]: " + e.getMessage());
     54                 }
     55             }
     56             else
     57             {    // TODO: this really shouldn't be supported here...
     58                 return ASN1ObjectIdentifier.fromOctetString((byte[])obj);
     59             }
     60         }
     61 
     62         throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
     63     }
     64 
     65     /**
     66      * return an Object Identifier from a tagged object.
     67      *
     68      * @param obj      the tagged object holding the object we want
     69      * @param explicit true if the object is meant to be explicitly
     70      *                 tagged false otherwise.
     71      * @throws IllegalArgumentException if the tagged object cannot
     72      * be converted.
     73      */
     74     public static ASN1ObjectIdentifier getInstance(
     75         ASN1TaggedObject obj,
     76         boolean explicit)
     77     {
     78         ASN1Primitive o = obj.getObject();
     79 
     80         if (explicit || o instanceof DERObjectIdentifier)
     81         {
     82             return getInstance(o);
     83         }
     84         else
     85         {
     86             return ASN1ObjectIdentifier.fromOctetString(ASN1OctetString.getInstance(obj.getObject()).getOctets());
     87         }
     88     }
     89 
     90     private static final long LONG_LIMIT = (Long.MAX_VALUE >> 7) - 0x7f;
     91 
     92     DERObjectIdentifier(
     93         byte[] bytes)
     94     {
     95         StringBuffer objId = new StringBuffer();
     96         long value = 0;
     97         BigInteger bigValue = null;
     98         boolean first = true;
     99 
    100         for (int i = 0; i != bytes.length; i++)
    101         {
    102             int b = bytes[i] & 0xff;
    103 
    104             if (value <= LONG_LIMIT)
    105             {
    106                 value += (b & 0x7f);
    107                 if ((b & 0x80) == 0)             // end of number reached
    108                 {
    109                     if (first)
    110                     {
    111                         if (value < 40)
    112                         {
    113                             objId.append('0');
    114                         }
    115                         else if (value < 80)
    116                         {
    117                             objId.append('1');
    118                             value -= 40;
    119                         }
    120                         else
    121                         {
    122                             objId.append('2');
    123                             value -= 80;
    124                         }
    125                         first = false;
    126                     }
    127 
    128                     objId.append('.');
    129                     objId.append(value);
    130                     value = 0;
    131                 }
    132                 else
    133                 {
    134                     value <<= 7;
    135                 }
    136             }
    137             else
    138             {
    139                 if (bigValue == null)
    140                 {
    141                     bigValue = BigInteger.valueOf(value);
    142                 }
    143                 bigValue = bigValue.or(BigInteger.valueOf(b & 0x7f));
    144                 if ((b & 0x80) == 0)
    145                 {
    146                     if (first)
    147                     {
    148                         objId.append('2');
    149                         bigValue = bigValue.subtract(BigInteger.valueOf(80));
    150                         first = false;
    151                     }
    152 
    153                     objId.append('.');
    154                     objId.append(bigValue);
    155                     bigValue = null;
    156                     value = 0;
    157                 }
    158                 else
    159                 {
    160                     bigValue = bigValue.shiftLeft(7);
    161                 }
    162             }
    163         }
    164 
    165         // BEGIN android-changed
    166         /*
    167          * Intern the identifier so there aren't hundreds of duplicates
    168          * (in practice).
    169          */
    170         this.identifier = objId.toString().intern();
    171         // END android-changed
    172         this.body = Arrays.clone(bytes);
    173     }
    174 
    175     /**
    176      * @deprecated use ASN1ObjectIdentifier constructor.
    177      */
    178     public DERObjectIdentifier(
    179         String identifier)
    180     {
    181         if (identifier == null)
    182         {
    183             throw new IllegalArgumentException("'identifier' cannot be null");
    184         }
    185         if (!isValidIdentifier(identifier))
    186         {
    187             throw new IllegalArgumentException("string " + identifier + " not an OID");
    188         }
    189 
    190         // BEGIN android-changed
    191         /*
    192          * Intern the identifier so there aren't hundreds of duplicates
    193          * (in practice).
    194          */
    195         this.identifier = identifier.intern();
    196         // END android-changed
    197     }
    198 
    199     DERObjectIdentifier(DERObjectIdentifier oid, String branchID)
    200     {
    201         if (!isValidBranchID(branchID, 0))
    202         {
    203             throw new IllegalArgumentException("string " + branchID + " not a valid OID branch");
    204         }
    205 
    206         this.identifier = oid.getId() + "." + branchID;
    207     }
    208 
    209     public String getId()
    210     {
    211         return identifier;
    212     }
    213 
    214     private void writeField(
    215         ByteArrayOutputStream out,
    216         long fieldValue)
    217     {
    218         byte[] result = new byte[9];
    219         int pos = 8;
    220         result[pos] = (byte)((int)fieldValue & 0x7f);
    221         while (fieldValue >= (1L << 7))
    222         {
    223             fieldValue >>= 7;
    224             result[--pos] = (byte)((int)fieldValue & 0x7f | 0x80);
    225         }
    226         out.write(result, pos, 9 - pos);
    227     }
    228 
    229     private void writeField(
    230         ByteArrayOutputStream out,
    231         BigInteger fieldValue)
    232     {
    233         int byteCount = (fieldValue.bitLength() + 6) / 7;
    234         if (byteCount == 0)
    235         {
    236             out.write(0);
    237         }
    238         else
    239         {
    240             BigInteger tmpValue = fieldValue;
    241             byte[] tmp = new byte[byteCount];
    242             for (int i = byteCount - 1; i >= 0; i--)
    243             {
    244                 tmp[i] = (byte)((tmpValue.intValue() & 0x7f) | 0x80);
    245                 tmpValue = tmpValue.shiftRight(7);
    246             }
    247             tmp[byteCount - 1] &= 0x7f;
    248             out.write(tmp, 0, tmp.length);
    249         }
    250     }
    251 
    252     private void doOutput(ByteArrayOutputStream aOut)
    253     {
    254         OIDTokenizer tok = new OIDTokenizer(identifier);
    255         int first = Integer.parseInt(tok.nextToken()) * 40;
    256 
    257         String secondToken = tok.nextToken();
    258         if (secondToken.length() <= 18)
    259         {
    260             writeField(aOut, first + Long.parseLong(secondToken));
    261         }
    262         else
    263         {
    264             writeField(aOut, new BigInteger(secondToken).add(BigInteger.valueOf(first)));
    265         }
    266 
    267         while (tok.hasMoreTokens())
    268         {
    269             String token = tok.nextToken();
    270             if (token.length() <= 18)
    271             {
    272                 writeField(aOut, Long.parseLong(token));
    273             }
    274             else
    275             {
    276                 writeField(aOut, new BigInteger(token));
    277             }
    278         }
    279     }
    280 
    281     protected synchronized byte[] getBody()
    282     {
    283         if (body == null)
    284         {
    285             ByteArrayOutputStream bOut = new ByteArrayOutputStream();
    286 
    287             doOutput(bOut);
    288 
    289             body = bOut.toByteArray();
    290         }
    291 
    292         return body;
    293     }
    294 
    295     boolean isConstructed()
    296     {
    297         return false;
    298     }
    299 
    300     int encodedLength()
    301         throws IOException
    302     {
    303         int length = getBody().length;
    304 
    305         return 1 + StreamUtil.calculateBodyLength(length) + length;
    306     }
    307 
    308     void encode(
    309         ASN1OutputStream out)
    310         throws IOException
    311     {
    312         byte[] enc = getBody();
    313 
    314         out.write(BERTags.OBJECT_IDENTIFIER);
    315         out.writeLength(enc.length);
    316         out.write(enc);
    317     }
    318 
    319     public int hashCode()
    320     {
    321         return identifier.hashCode();
    322     }
    323 
    324     boolean asn1Equals(
    325         ASN1Primitive o)
    326     {
    327         if (!(o instanceof DERObjectIdentifier))
    328         {
    329             return false;
    330         }
    331 
    332         return identifier.equals(((DERObjectIdentifier)o).identifier);
    333     }
    334 
    335     public String toString()
    336     {
    337         return getId();
    338     }
    339 
    340     private static boolean isValidBranchID(
    341         String branchID, int start)
    342     {
    343         boolean periodAllowed = false;
    344 
    345         int pos = branchID.length();
    346         while (--pos >= start)
    347         {
    348             char ch = branchID.charAt(pos);
    349 
    350             // TODO Leading zeroes?
    351             if ('0' <= ch && ch <= '9')
    352             {
    353                 periodAllowed = true;
    354                 continue;
    355             }
    356 
    357             if (ch == '.')
    358             {
    359                 if (!periodAllowed)
    360                 {
    361                     return false;
    362                 }
    363 
    364                 periodAllowed = false;
    365                 continue;
    366             }
    367 
    368             return false;
    369         }
    370 
    371         return periodAllowed;
    372     }
    373 
    374     private static boolean isValidIdentifier(
    375         String identifier)
    376     {
    377         if (identifier.length() < 3 || identifier.charAt(1) != '.')
    378         {
    379             return false;
    380         }
    381 
    382         char first = identifier.charAt(0);
    383         if (first < '0' || first > '2')
    384         {
    385             return false;
    386         }
    387 
    388         return isValidBranchID(identifier, 2);
    389     }
    390 
    391     private static ASN1ObjectIdentifier[][] cache = new ASN1ObjectIdentifier[256][];
    392 
    393     static ASN1ObjectIdentifier fromOctetString(byte[] enc)
    394     {
    395         if (enc.length < 3)
    396         {
    397             return new ASN1ObjectIdentifier(enc);
    398         }
    399 
    400         int idx1 = enc[enc.length - 2] & 0xff;
    401         // in this case top bit is always zero
    402         int idx2 = enc[enc.length - 1] & 0x7f;
    403 
    404         ASN1ObjectIdentifier possibleMatch;
    405 
    406         synchronized (cache)
    407         {
    408             ASN1ObjectIdentifier[] first = cache[idx1];
    409             if (first == null)
    410             {
    411                 first = cache[idx1] = new ASN1ObjectIdentifier[128];
    412             }
    413 
    414             possibleMatch = first[idx2];
    415             if (possibleMatch == null)
    416             {
    417                 return first[idx2] = new ASN1ObjectIdentifier(enc);
    418             }
    419 
    420             if (Arrays.areEqual(enc, possibleMatch.getBody()))
    421             {
    422                 return possibleMatch;
    423             }
    424 
    425             idx1 = (idx1 + 1) & 0xff;
    426             first = cache[idx1];
    427             if (first == null)
    428             {
    429                 first = cache[idx1] = new ASN1ObjectIdentifier[128];
    430             }
    431 
    432             possibleMatch = first[idx2];
    433             if (possibleMatch == null)
    434             {
    435                 return first[idx2] = new ASN1ObjectIdentifier(enc);
    436             }
    437 
    438             if (Arrays.areEqual(enc, possibleMatch.getBody()))
    439             {
    440                 return possibleMatch;
    441             }
    442 
    443             idx2 = (idx2 + 1) & 0x7f;
    444             possibleMatch = first[idx2];
    445             if (possibleMatch == null)
    446             {
    447                 return first[idx2] = new ASN1ObjectIdentifier(enc);
    448             }
    449         }
    450 
    451         if (Arrays.areEqual(enc, possibleMatch.getBody()))
    452         {
    453             return possibleMatch;
    454         }
    455 
    456         return new ASN1ObjectIdentifier(enc);
    457     }
    458 }
    459