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