Home | History | Annotate | Download | only in asn1
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 /**
     19 * @author Vladimir N. Molotkov, Stepan M. Mishura
     20 * @version $Revision$
     21 */
     22 
     23 package org.apache.harmony.security.asn1;
     24 
     25 import java.io.IOException;
     26 import java.io.InputStream;
     27 import java.util.ArrayList;
     28 
     29 
     30 /**
     31  * Decodes ASN.1 types encoded with BER (X.690)
     32  *
     33  * @see <a href="http://asn1.elibel.tm.fr/en/standards/index.htm">ASN.1</a>
     34  */
     35 
     36 public class BerInputStream {
     37 
     38     /**
     39      * Associated <code>InputStream</code>
     40      */
     41     protected InputStream in;
     42 
     43     /**
     44      * Internal buffer for storing encoded array
     45      */
     46     protected byte[] buffer;
     47 
     48     /**
     49      * The position in the buffer.
     50      *
     51      * Next read must place data into the buffer from this offset
     52      */
     53     protected int offset = 0;
     54 
     55     // The buffer increment size.
     56     // Must be reasonable big to reallocate memory not to often.
     57     // Primary is used for decoding indefinite length encoding
     58     private static final int BUF_INCREASE_SIZE = 1024 * 16;
     59 
     60     /**
     61      * Indicates indefinite length of the current type
     62      */
     63     protected static final int INDEFINIT_LENGTH = -1;
     64 
     65     /**
     66      * Creates stream for decoding.
     67      *
     68      * @param encoded - bytes array to be decoded
     69      * @throws IOException - if an error occurs
     70      */
     71     public BerInputStream(byte[] encoded) throws IOException {
     72         this(encoded, 0, encoded.length);
     73     }
     74 
     75     /**
     76      * Creates stream for decoding.
     77      *
     78      * @param encoded -
     79      *            bytes array to be decoded
     80      * @param offset -
     81      *            the encoding offset
     82      * @param expectedLength -
     83      *            expected length of full encoding, this includes identifier,
     84      *            length an content octets
     85      * @throws IOException -
     86      *             if an error occurs
     87      */
     88     public BerInputStream(byte[] encoded, int offset, int expectedLength)
     89             throws IOException {
     90 
     91         this.buffer = encoded;
     92         this.offset = offset;
     93 
     94         next();
     95 
     96         // compare expected and decoded length
     97         if (length != INDEFINIT_LENGTH
     98                 && (offset + expectedLength) != (this.offset + this.length)) {
     99             throw new ASN1Exception("Wrong content length");
    100         }
    101     }
    102 
    103     /**
    104      * Creates stream for decoding.
    105      *
    106      * Allocates initial buffer of default size
    107      *
    108      * @param is associated <code>InputStream</code>
    109      */
    110     public BerInputStream(InputStream in) throws IOException {
    111         this(in, BUF_INCREASE_SIZE);
    112     }
    113 
    114     /**
    115      * Creates stream for decoding.
    116      *
    117      * Allocates initial buffer of <code>initialSize</code> size
    118      *
    119      * @param initialSize the internal buffer initial size
    120      * @param is associated <code>InputStream</code>
    121      */
    122     public BerInputStream(InputStream in, int initialSize) throws IOException {
    123 
    124         this.in = in;
    125         buffer = new byte[initialSize];
    126 
    127         next();
    128 
    129         if (length != INDEFINIT_LENGTH) {
    130             // input stream has definite length encoding
    131             // check allocated length to avoid further reallocations
    132             if (buffer.length < (length + offset)) {
    133                 byte[] newBuffer = new byte[length + offset];
    134                 System.arraycopy(buffer, 0, newBuffer, 0, offset);
    135                 buffer = newBuffer;
    136             }
    137         } else {
    138             isIndefinedLength = true;
    139             throw new ASN1Exception("Decoding indefinite length encoding is not supported");
    140         }
    141     }
    142 
    143     /**
    144      * Resets this stream to initial state.
    145      *
    146      * @param encoded - a new bytes array to be decoded
    147      * @throws IOException - if an error occurs
    148      */
    149     public final void reset(byte[] encoded) throws IOException {
    150         buffer = encoded;
    151 
    152         next();
    153     }
    154 
    155     /**
    156      * Current decoded tag
    157      */
    158     public int tag;
    159 
    160     /**
    161      * Current decoded length
    162      */
    163     protected int length;
    164 
    165     /**
    166      * Current decoded content
    167      */
    168     public Object content;
    169 
    170     /**
    171      * Current decoded tag offset
    172      */
    173     protected int tagOffset;
    174 
    175     /**
    176      * Current decoded content offset
    177      */
    178     protected int contentOffset;
    179 
    180     /**
    181      * Decodes next encoded type.
    182      * Initializes tag, length, tagOffset and contentOffset variables
    183      *
    184      * @return next decoded tag
    185      * @throws IOException - if error occured
    186      */
    187     public int next() throws IOException {
    188 
    189         tagOffset = offset;
    190 
    191         // read tag
    192         tag = read();
    193 
    194         // read length
    195         length = read();
    196         if (length != 0x80) { // definite form
    197             // long or short length form
    198             if ((length & 0x80) != 0) { // long form
    199                 int numOctets = length & 0x7F;
    200 
    201                 if (numOctets > 5) {
    202                     throw new ASN1Exception("Too long encoding at [" + tagOffset + "]"); //FIXME message
    203                 }
    204 
    205                 // collect this value length
    206                 length = read();
    207                 for (int i = 1; i < numOctets; i++) {
    208                     int ch = read();
    209                     length = (length << 8) + ch;//read();
    210                 }
    211 
    212                 if (length > 0xFFFFFF) {
    213                     throw new ASN1Exception("Too long encoding at [" + tagOffset + "]"); //FIXME message
    214                 }
    215             }
    216         } else { //indefinite form
    217             length = INDEFINIT_LENGTH;
    218         }
    219         contentOffset = offset;
    220 
    221         return tag;
    222     }
    223 
    224     /**
    225      * Returns the length of the encoding
    226      */
    227     public static int getLength(byte[] encoding) {
    228         int length = encoding[1] & 0xFF;
    229         int numOctets = 0;
    230         if ((length & 0x80) != 0) { // long form
    231             numOctets = length & 0x7F;
    232 
    233             // collect this value length
    234             length = encoding[2] & 0xFF;
    235             for (int i = 3; i < numOctets + 2; i++) {
    236                 length = (length << 8) + (encoding[i] & 0xFF);
    237             }
    238         }
    239         //    tag length long_form content
    240         return 1 + 1 + numOctets + length;
    241     }
    242 
    243     /**
    244      * Decodes ASN.1 bitstring type
    245      *
    246      * @throws IOException - if error occured
    247      */
    248     public void readBitString() throws IOException {
    249 
    250         if (tag == ASN1Constants.TAG_BITSTRING) {
    251 
    252             if (length == 0) {
    253                 throw new ASN1Exception("ASN.1 Bitstring: wrong length. Tag at [" + tagOffset + "]");
    254             }
    255 
    256             readContent();
    257 
    258             // content: check unused bits
    259             if (buffer[contentOffset] > 7) {
    260                 throw new ASN1Exception("ASN.1 Bitstring: wrong content at [" + contentOffset + "]. A number of unused bits MUST be in range 0 to 7");
    261             }
    262 
    263             if (length == 1 && buffer[contentOffset] != 0) {
    264                 throw new ASN1Exception("ASN.1 Bitstring: wrong content at [" + contentOffset + "]. For empty string unused bits MUST be 0");
    265             }
    266 
    267         } else if (tag == ASN1Constants.TAG_C_BITSTRING) {
    268             throw new ASN1Exception("Decoding constructed ASN.1 bitstring  type is not provided");
    269         } else {
    270             throw expected("bitstring");
    271         }
    272     }
    273 
    274     /**
    275      * Decodes ASN.1 Enumerated type
    276      *
    277      * @throws IOException - if error occured
    278      */
    279     public void readEnumerated() throws IOException {
    280 
    281         if (tag != ASN1Constants.TAG_ENUM) {
    282             throw expected("enumerated");
    283         }
    284 
    285         //
    286         // all checks are the same as for ASN.1 integer type
    287         //
    288 
    289         // check encoded length
    290         if (length == 0) {
    291             throw new ASN1Exception("ASN.1 enumerated: wrong length for identifier at [" + tagOffset + "]");
    292         }
    293 
    294         readContent();
    295 
    296         // check encoded content
    297         if (length > 1) {
    298 
    299             int bits = buffer[contentOffset] & 0xFF;
    300             if (buffer[contentOffset + 1] < 0) {
    301                 bits += 0x100;
    302             }
    303 
    304             if (bits == 0 || bits == 0x1FF) {
    305                 throw new ASN1Exception("ASN.1 enumerated: wrong content at [" + contentOffset + "]. An integer MUST be encoded in minimum number of octets");
    306             }
    307         }
    308     }
    309 
    310     /**
    311      * Decodes ASN.1 boolean type
    312      *
    313      * @throws IOException - if error occured
    314      */
    315     public void readBoolean() throws IOException {
    316 
    317         if (tag != ASN1Constants.TAG_BOOLEAN) {
    318             throw expected("boolean");
    319         }
    320 
    321         // check encoded length
    322         if (length != 1) {
    323             throw new ASN1Exception("Wrong length for ASN.1 boolean at [" + tagOffset + "]");
    324         }
    325 
    326         readContent();
    327     }
    328 
    329     /**
    330      * The last choice index
    331      */
    332     public int choiceIndex;
    333 
    334     /**
    335      * Keeps last decoded: year, month, day, hour, minute, second, millisecond
    336      */
    337     public int[] times;
    338 
    339     /**
    340      * Decodes ASN.1 GeneralizedTime type
    341      *
    342      * @throws IOException - if error occured
    343      */
    344     public void readGeneralizedTime() throws IOException {
    345 
    346         if (tag == ASN1Constants.TAG_GENERALIZEDTIME) {
    347 
    348             // FIXME: any other optimizations?
    349             readContent();
    350             // FIXME store string somewhere to allow a custom time type perform
    351             // additional checks
    352 
    353             // check syntax: the last char MUST be Z
    354             if (buffer[offset - 1] != 'Z') {
    355                 // FIXME support only format that is acceptable for DER
    356                 throw new ASN1Exception("ASN.1 GeneralizedTime: encoded format is not implemented");
    357             }
    358 
    359             // check syntax: MUST be YYYYMMDDHHMMSS[(./,)DDD]'Z'
    360             if (length != 15 && (length < 17 || length > 19)) // invalid
    361                                                                 // length
    362             {
    363                 throw new ASN1Exception("ASN.1 GeneralizedTime wrongly encoded at [" + contentOffset + "]");
    364             }
    365 
    366             // check content: milliseconds
    367             if (length > 16) {
    368                 byte char14 = buffer[contentOffset + 14];
    369                 if (char14 != '.' && char14 != ',') {
    370                     throw new ASN1Exception("ASN.1 GeneralizedTime wrongly encoded at [" + contentOffset + "]");
    371                 }
    372             }
    373 
    374             if (times == null) {
    375                 times = new int[7];
    376             }
    377             times[0] = strToInt(contentOffset, 4); // year
    378             times[1] = strToInt(contentOffset + 4, 2); // month
    379             times[2] = strToInt(contentOffset + 6, 2); // day
    380             times[3] = strToInt(contentOffset + 8, 2); // hour
    381             times[4] = strToInt(contentOffset + 10, 2); // minute
    382             times[5] = strToInt(contentOffset + 12, 2); // second
    383 
    384             if (length > 16) {
    385                 // FIXME optimize me
    386                 times[6] = strToInt(contentOffset + 15, length - 16);
    387 
    388                 if (length == 17) {
    389                     times[6] = times[6] * 100;
    390                 } else if (length == 18) {
    391                     times[6] = times[6] * 10;
    392                 }
    393             }
    394 
    395             // FIXME check all values for valid numbers!!!
    396         } else if (tag == ASN1Constants.TAG_C_GENERALIZEDTIME) {
    397             throw new ASN1Exception("Decoding constructed ASN.1 GeneralizedTime type is not supported");
    398         } else {
    399             throw expected("GeneralizedTime");
    400         }
    401     }
    402 
    403     /**
    404      * Decodes ASN.1 UTCTime type
    405      *
    406      * @throws IOException - if an I/O error occurs or the end of the stream is reached
    407      */
    408     public void readUTCTime() throws IOException {
    409 
    410         if (tag == ASN1Constants.TAG_UTCTIME) {
    411 
    412             switch (length) {
    413             case ASN1UTCTime.UTC_HM:
    414             case ASN1UTCTime.UTC_HMS:
    415                 break;
    416             case ASN1UTCTime.UTC_LOCAL_HM:
    417             case ASN1UTCTime.UTC_LOCAL_HMS:
    418                 // FIXME only coordinated universal time formats are supported
    419                 throw new ASN1Exception("ASN.1 UTCTime: local time format is not supported");
    420             default:
    421                 throw new ASN1Exception("ASN.1 UTCTime: wrong length, identifier at " + tagOffset);
    422             }
    423 
    424             // FIXME: any other optimizations?
    425             readContent();
    426 
    427             // FIXME store string somewhere to allow a custom time type perform
    428             // additional checks
    429 
    430             // check syntax: the last char MUST be Z
    431             if (buffer[offset - 1] != 'Z') {
    432                 throw new ASN1Exception("ASN.1 UTCTime wrongly encoded at ["
    433                         + contentOffset + ']');
    434             }
    435 
    436             if (times == null) {
    437                 times = new int[7];
    438             }
    439 
    440             times[0] = strToInt(contentOffset, 2); // year
    441             if (times[0] > 49) {
    442                 times[0] += 1900;
    443             } else {
    444                 times[0] += 2000;
    445             }
    446 
    447             times[1] = strToInt(contentOffset + 2, 2); // month
    448             times[2] = strToInt(contentOffset + 4, 2); // day
    449             times[3] = strToInt(contentOffset + 6, 2); // hour
    450             times[4] = strToInt(contentOffset + 8, 2); // minute
    451 
    452             if (length == ASN1UTCTime.UTC_HMS) {
    453                 times[5] = strToInt(contentOffset + 10, 2); // second
    454             }
    455 
    456             // FIXME check all time values for valid numbers!!!
    457         } else if (tag == ASN1Constants.TAG_C_UTCTIME) {
    458             throw new ASN1Exception("Decoding constructed ASN.1 UTCTime type is not supported");
    459         } else {
    460             throw expected("UTCTime");
    461         }
    462     }
    463 
    464     //TODO comment me
    465     private int strToInt(int off, int count) throws ASN1Exception {
    466 
    467         //FIXME works only with buffer
    468 
    469         int c;
    470         int result = 0;
    471         for (int i = off, end = off + count; i < end; i++) {
    472             c = buffer[i] - 48;
    473             if (c < 0 || c > 9) {
    474                 throw new ASN1Exception("Time encoding has invalid char");
    475             }
    476             result = result * 10 + c;
    477         }
    478         return result;
    479     }
    480 
    481     /**
    482      * Decodes ASN.1 Integer type
    483      *
    484      * @throws IOException - if error occured
    485      */
    486     public void readInteger() throws IOException {
    487 
    488         if (tag != ASN1Constants.TAG_INTEGER) {
    489             throw expected("integer");
    490         }
    491 
    492         // check encoded length
    493         if (length < 1) {
    494             throw new ASN1Exception("Wrong length for ASN.1 integer at [" + tagOffset + "]");
    495         }
    496 
    497         readContent();
    498 
    499         // check encoded content
    500         if (length > 1) {
    501 
    502             byte firstByte = buffer[offset - length];
    503             byte secondByte = (byte) (buffer[offset - length + 1] & 0x80);
    504 
    505             if (firstByte == 0 && secondByte == 0 || firstByte == (byte) 0xFF
    506                     && secondByte == (byte) 0x80) {
    507                 throw new ASN1Exception("Wrong content for ASN.1 integer at [" + (offset - length) + "]. An integer MUST be encoded in minimum number of octets");
    508             }
    509         }
    510     }
    511 
    512     /**
    513      * Decodes ASN.1 Octetstring type
    514      *
    515      * @throws IOException - if error occured
    516      */
    517     public void readOctetString() throws IOException {
    518 
    519         if (tag == ASN1Constants.TAG_OCTETSTRING) {
    520             readContent();
    521         } else if (tag == ASN1Constants.TAG_C_OCTETSTRING) {
    522             throw new ASN1Exception("Decoding constructed ASN.1 octet string type is not supported");
    523         } else {
    524             throw expected("octetstring");
    525         }
    526     }
    527 
    528     private ASN1Exception expected(String what) throws ASN1Exception {
    529         throw new ASN1Exception("ASN.1 " + what + " identifier expected at [" + tagOffset + "], got " + Integer.toHexString(tag));
    530     }
    531 
    532     //FIXME comment me
    533     public int oidElement;
    534 
    535     /**
    536      * Decodes ASN.1 ObjectIdentifier type
    537      *
    538      * @throws IOException - if error occured
    539      */
    540     public void readOID() throws IOException {
    541 
    542         if (tag != ASN1Constants.TAG_OID) {
    543             throw expected("OID");
    544         }
    545 
    546         // check encoded length
    547         if (length < 1) {
    548             throw new ASN1Exception("Wrong length for ASN.1 object identifier at [" + tagOffset + "]");
    549         }
    550 
    551         readContent();
    552 
    553         // check content: last encoded byte (8th bit MUST be zero)
    554         if ((buffer[offset - 1] & 0x80) != 0) {
    555             throw new ASN1Exception("Wrong encoding at [" + (offset - 1) + "]");
    556         }
    557 
    558         oidElement = 1;
    559         for (int i = 0; i < length; i++, ++oidElement) {
    560 
    561             // According to ASN.1 BER spec:
    562             //    leading octet of subidentifier MUST not be 0x80
    563             // This assertion is not verified
    564             //
    565             //if (buffer[contentOffset + i] == (byte)0x80) {
    566             //    throw new ASN1Exception(
    567             //            "Wrong content for ASN.1 object identifier at ["
    568             //                    + contentOffset
    569             //                    + "]. Subidentifier MUST be encoded in minimum number of octets");
    570             //}
    571 
    572             while ((buffer[contentOffset + i] & 0x80) == 0x80) {
    573                 i++;
    574             }
    575         }
    576     }
    577 
    578     /**
    579      * Decodes ASN.1 Sequence type
    580      *
    581      * @param sequence - ASN.1 sequence to be decoded
    582      * @throws IOException - if error occured
    583      */
    584     public void readSequence(ASN1Sequence sequence) throws IOException {
    585 
    586         if (tag != ASN1Constants.TAG_C_SEQUENCE) {
    587             throw expected("sequence");
    588         }
    589 
    590         int begOffset = offset;
    591         int endOffset = begOffset + length;
    592 
    593         ASN1Type[] type = sequence.type;
    594 
    595         int i = 0;
    596 
    597         if (isVerify) {
    598 
    599             for (; (offset < endOffset) && (i < type.length); i++) {
    600 
    601                 next();
    602                 while (!type[i].checkTag(tag)) {
    603                     // check whether it is optional component or not
    604                     if (!sequence.OPTIONAL[i] || (i == type.length - 1)) {
    605                         throw new ASN1Exception("ASN.1 Sequence: mandatory value is missing at [" + tagOffset + "]");
    606                     }
    607                     i++;
    608                 }
    609 
    610                 type[i].decode(this);
    611             }
    612 
    613             // check the rest of components
    614             for (; i < type.length; i++) {
    615                 if (!sequence.OPTIONAL[i]) {
    616                     throw new ASN1Exception("ASN.1 Sequence: mandatory value is missing at [" + tagOffset + "]");
    617                 }
    618             }
    619 
    620         } else {
    621 
    622             int seqTagOffset = tagOffset; //store tag offset
    623 
    624             Object[] values = new Object[type.length];
    625             for (; (offset < endOffset) && (i < type.length); i++) {
    626 
    627                 next();
    628                 while (!type[i].checkTag(tag)) {
    629                     // check whether it is optional component or not
    630                     if (!sequence.OPTIONAL[i] || (i == type.length - 1)) {
    631                         throw new ASN1Exception("ASN.1 Sequence: mandatory value is missing at [" + tagOffset + "]");
    632                     }
    633 
    634                     // sets default value
    635                     if (sequence.DEFAULT[i] != null) {
    636                         values[i] = sequence.DEFAULT[i];
    637                     }
    638                     i++;
    639                 }
    640                 values[i] = type[i].decode(this);
    641             }
    642 
    643             // check the rest of components
    644             for (; i < type.length; i++) {
    645                 if (!sequence.OPTIONAL[i]) {
    646                     throw new ASN1Exception("ASN.1 Sequence: mandatory value is missing at [" + tagOffset + "]");
    647                 }
    648                 if (sequence.DEFAULT[i] != null) {
    649                     values[i] = sequence.DEFAULT[i];
    650                 }
    651             }
    652             content = values;
    653 
    654             tagOffset = seqTagOffset; //retrieve tag offset
    655         }
    656 
    657         if (offset != endOffset) {
    658             throw new ASN1Exception("Wrong encoding at [" + begOffset + "]. Content's length and encoded length are not the same");
    659         }
    660     }
    661 
    662     /**
    663      * Decodes ASN.1 SequenceOf type
    664      *
    665      * @param sequenceOf - ASN.1 sequence to be decoded
    666      * @throws IOException - if error occured
    667      */
    668     public void readSequenceOf(ASN1SequenceOf sequenceOf) throws IOException {
    669 
    670         if (tag != ASN1Constants.TAG_C_SEQUENCEOF) {
    671             throw expected("sequenceOf");
    672         }
    673 
    674         decodeValueCollection(sequenceOf);
    675     }
    676 
    677     /**
    678      * Decodes ASN.1 Set type
    679      *
    680      * @param set - ASN.1 set to be decoded
    681      * @throws IOException - if error occured
    682      */
    683     public void readSet(ASN1Set set) throws IOException {
    684 
    685         if (tag != ASN1Constants.TAG_C_SET) {
    686             throw expected("set");
    687         }
    688 
    689         throw new ASN1Exception("Decoding ASN.1 Set type is not supported");
    690     }
    691 
    692     /**
    693      * Decodes ASN.1 SetOf type
    694      *
    695      * @param set - ASN.1 set to be decoded
    696      * @throws IOException - if error occured
    697      */
    698     public void readSetOf(ASN1SetOf setOf) throws IOException {
    699 
    700         if (tag != ASN1Constants.TAG_C_SETOF) {
    701             throw expected("setOf");
    702         }
    703 
    704         decodeValueCollection(setOf);
    705     }
    706 
    707     private final void decodeValueCollection(ASN1ValueCollection collection)
    708             throws IOException {
    709 
    710         int begOffset = offset;
    711         int endOffset = begOffset + length;
    712 
    713         ASN1Type type = collection.type;
    714 
    715         if (isVerify) {
    716             while (endOffset > offset) {
    717                 next();
    718                 type.decode(this);
    719             }
    720         } else {
    721 
    722             int seqTagOffset = tagOffset; //store tag offset
    723 
    724             ArrayList values = new ArrayList();
    725             while (endOffset > offset) {
    726                 next();
    727                 values.add(type.decode(this));
    728             }
    729 
    730             content = values;
    731 
    732             tagOffset = seqTagOffset; //retrieve tag offset
    733         }
    734 
    735         if (offset != endOffset) {
    736             throw new ASN1Exception("Wrong encoding at [" + begOffset + "]. Content's length and encoded length are not the same");
    737         }
    738     }
    739 
    740     /**
    741      * Decodes ASN.1 String type
    742      *
    743      * @throws IOException - if an I/O error occurs or the end of the stream is reached
    744      */
    745     public void readString(ASN1StringType type) throws IOException {
    746 
    747         //FIXME check string content
    748         if (tag == type.id) {
    749             readContent();
    750         } else if (tag == type.constrId) {
    751             throw new ASN1Exception("Decoding constructed ASN.1 string type is not provided");
    752         } else {
    753             throw expected("string");
    754         }
    755     }
    756 
    757     /**
    758      * Returns encoded array.
    759      *
    760      * MUST be invoked after decoding corresponding ASN.1 notation
    761      */
    762     public byte[] getEncoded() {
    763         byte[] encoded = new byte[offset - tagOffset];
    764         System.arraycopy(buffer, tagOffset, encoded, 0, encoded.length);
    765         return encoded;
    766     }
    767 
    768     /**
    769      * Returns internal buffer used for decoding
    770      *
    771      * @return - buffer
    772      */
    773     public final byte[] getBuffer() {
    774         return buffer;
    775     }
    776 
    777     /**
    778      * Returns length of the current content for decoding
    779      *
    780      * @return - length of content
    781      */
    782     public final int getLength() {
    783         return length;
    784     }
    785 
    786     /**
    787      * Returns the current offset
    788      *
    789      * @return - offset
    790      */
    791     public final int getOffset() {
    792         return offset;
    793     }
    794 
    795     /**
    796      * Returns end offset for the current encoded type
    797      *
    798      * @return - offset
    799      */
    800     public final int getEndOffset() {
    801         return offset + length;
    802     }
    803 
    804     /**
    805      * Returns start offset for the current encoded type
    806      *
    807      * @return - offset
    808      */
    809     public final int getTagOffset() {
    810         return tagOffset;
    811     }
    812 
    813     public final int getContentOffset() {
    814         return contentOffset;
    815     }
    816 
    817     /**
    818      * Indicates verify or store mode.
    819      *
    820      * In store mode a decoded content is stored in a newly allocated
    821      * appropriate object. The <code>content</code> variable holds
    822      * a reference to the last created object.
    823      *
    824      * In verify mode a decoded content is not stored.
    825      */
    826     // FIXME it is used only for one case
    827     // decoding PCKS#8 Private Key Info notation
    828     // remove this option because it does decoding more complex
    829     protected boolean isVerify;
    830 
    831     /**
    832      * Sets verify mode.
    833      */
    834     public final void setVerify() {
    835         isVerify = true;
    836     }
    837 
    838     /**
    839      * Indicates defined or indefined reading mode for associated InputStream.
    840      *
    841      * This mode is defined by reading a length
    842      * for a first ASN.1 type from InputStream.
    843      */
    844     protected boolean isIndefinedLength;
    845 
    846     /**
    847      * Reads the next encoded byte from the encoded input stream.
    848      *
    849      * @return the next encoded byte
    850      * @throws IOException - if error occured
    851      */
    852     protected int read() throws IOException {
    853 
    854         if (offset == buffer.length) {
    855             throw new ASN1Exception("Unexpected end of encoding");
    856         }
    857 
    858         if (in == null) {
    859             return buffer[offset++] & 0xFF;
    860         } else {
    861             int octet = in.read();
    862             if (octet == -1) {
    863                 throw new ASN1Exception("Unexpected end of encoding");
    864             }
    865 
    866             buffer[offset++] = (byte) octet;
    867 
    868             return octet;
    869         }
    870     }
    871 
    872     /**
    873      * Reads the next encoded content from the encoded input stream.
    874      * The method MUST be used for reading a primitive encoded content.
    875      *
    876      * @throws IOException - if error occured
    877      */
    878     public void readContent() throws IOException {
    879         if (offset + length > buffer.length) {
    880             throw new ASN1Exception("Unexpected end of encoding");
    881         }
    882 
    883         if (in == null) {
    884             offset += length;
    885         } else {
    886             int bytesRead = in.read(buffer, offset, length);
    887 
    888             if (bytesRead != length) {
    889                 // if input stream didn't return all data at once
    890                 // try to read it in several blocks
    891                 int c = bytesRead;
    892                 do {
    893                     if (c < 1 || bytesRead > length) {
    894                         throw new ASN1Exception("Failed to read encoded content");
    895                     }
    896                     c = in.read(buffer, offset + bytesRead, length - bytesRead);
    897                     bytesRead += c;
    898                 } while (bytesRead != length);
    899             }
    900 
    901             offset += length;
    902         }
    903     }
    904 
    905     //    // reallocates internal buffer for indefined reading mode
    906     //    private void reallocateBuffer(int n) {
    907     //        int newSize;
    908     //        for (newSize = buffer.length * 2; newSize < buffer.length + n; newSize = newSize * 2)
    909     //            ;
    910     //        byte[] newBuffer = new byte[newSize];
    911     //        System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
    912     //        buffer = newBuffer;
    913     //    }
    914 
    915     /**
    916      * Reallocates the buffer in order to make it
    917      * exactly the size of data it contains
    918      */
    919     public void compactBuffer() {
    920         if (offset != buffer.length) {
    921             byte[] newBuffer = new byte[offset];
    922             // restore buffer content
    923             System.arraycopy(buffer, 0, newBuffer, 0, offset);
    924             // set new buffer
    925             buffer = newBuffer;
    926         }
    927     }
    928 
    929     //
    930     //
    931     //
    932     //
    933     //
    934 
    935     private Object[][] pool;
    936 
    937     public void put(Object key, Object entry) {
    938 
    939         if (pool == null) {
    940             pool = new Object[2][10];
    941         }
    942 
    943         int i = 0;
    944         for (; i < pool[0].length && pool[0][i] != null; i++) {
    945             if (pool[0][i] == key) {
    946                 pool[1][i] = entry;
    947                 return;
    948             }
    949         }
    950 
    951         if (i == pool[0].length) {
    952             Object[][] newPool = new Object[pool[0].length * 2][2];
    953             System.arraycopy(pool[0], 0, newPool[0], 0, pool[0].length);
    954             System.arraycopy(pool[1], 0, newPool[1], 0, pool[0].length);
    955             pool = newPool;
    956         } else {
    957             pool[0][i] = key;
    958             pool[1][i] = entry;
    959         }
    960     }
    961 
    962     public Object get(Object key) {
    963 
    964         if (pool == null) {
    965             return null;
    966         }
    967 
    968         for (int i = 0; i < pool[0].length; i++) {
    969             if (pool[0][i] == key) {
    970                 return pool[1][i];
    971             }
    972         }
    973         return null;
    974     }
    975 }
    976