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