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 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 
     28 /**
     29  * Decodes ASN.1 types encoded with DER (X.690)
     30  *
     31  * @see <a href="http://asn1.elibel.tm.fr/en/standards/index.htm">ASN.1</a>
     32  */
     33 public final class DerInputStream extends BerInputStream {
     34 
     35     /** mask for verifying unused bits for ASN.1 bitstring */
     36     private static final byte[] UNUSED_BITS_MASK = new byte[] { 0x01, 0x03,
     37             0x07, 0x0F, 0x1F, 0x3F, 0x7F };
     38 
     39     public DerInputStream(byte[] encoded) throws IOException {
     40         super(encoded, 0, encoded.length);
     41     }
     42 
     43     public DerInputStream(byte[] encoded, int offset, int encodingLen) throws IOException {
     44         super(encoded, offset, encodingLen);
     45     }
     46 
     47     public DerInputStream(InputStream in) throws IOException {
     48         super(in);
     49     }
     50 
     51     public int next() throws IOException {
     52         int tag = super.next();
     53 
     54         if (length == INDEFINIT_LENGTH) {
     55             throw new ASN1Exception("DER: only definite length encoding MUST be used");
     56         }
     57 
     58         // FIXME add check: length encoding uses minimum number of octets
     59 
     60         return tag;
     61     }
     62 
     63     public void readBitString() throws IOException {
     64         if (tag == ASN1Constants.TAG_C_BITSTRING) {
     65             throw new ASN1Exception("ASN.1 bitstring: constructed identifier at [" + tagOffset
     66                     + "]. Not valid for DER.");
     67         }
     68 
     69         super.readBitString();
     70 
     71         //check: unused bits values - MUST be 0
     72         if (length > 1
     73                 && buffer[contentOffset] != 0
     74                 && (buffer[offset - 1] & UNUSED_BITS_MASK[buffer[contentOffset] - 1]) != 0) {
     75             throw new ASN1Exception("ASN.1 bitstring: wrong content at [" + contentOffset
     76                     + "]. DER requires zero unused bits in final octet.");
     77         }
     78     }
     79 
     80     public void readBoolean() throws IOException {
     81         super.readBoolean();
     82 
     83         // check encoded content
     84         if (buffer[contentOffset] != 0 && buffer[contentOffset] != (byte) 0xFF) {
     85             throw new ASN1Exception("ASN.1 boolean: wrong content at [" + contentOffset
     86                     + "]. DER allows only 0x00 or 0xFF values");
     87         }
     88     }
     89 
     90     public void readOctetString() throws IOException {
     91         if (tag == ASN1Constants.TAG_C_OCTETSTRING) {
     92             throw new ASN1Exception("ASN.1 octetstring: constructed identifier at [" + tagOffset
     93                     + "]. Not valid for DER.");
     94         }
     95         super.readOctetString();
     96     }
     97 
     98     public void readSequence(ASN1Sequence sequence) throws IOException {
     99         //
    100         // According to ASN.1 DER spec. sequence MUST not include
    101         // any encoding which value is equal to its default value
    102         //
    103         // Verification of this assertion is not implemented
    104         //
    105         super.readSequence(sequence);
    106     }
    107 
    108     public void readSetOf(ASN1SetOf setOf) throws IOException {
    109         //
    110         // According to ASN.1 DER spec. set of MUST appear in
    111         // ascending order (short component are padded for comparison)
    112         //
    113         // Verification of this assertion is not implemented
    114         //
    115         super.readSetOf(setOf);
    116     }
    117 
    118     public void readString(ASN1StringType type) throws IOException {
    119         if (tag == type.constrId) {
    120             throw new ASN1Exception("ASN.1 string: constructed identifier at [" + tagOffset
    121                     + "]. Not valid for DER.");
    122         }
    123         super.readString(type);
    124     }
    125 
    126     public void readUTCTime() throws IOException {
    127         if (tag == ASN1Constants.TAG_C_UTCTIME) {
    128             // It is a string type and it can be encoded as primitive or constructed.
    129             throw new ASN1Exception("ASN.1 UTCTime: constructed identifier at [" + tagOffset
    130                     + "]. Not valid for DER.");
    131         }
    132 
    133         // check format: DER uses YYMMDDHHMMSS'Z' only
    134         if (length != ASN1UTCTime.UTC_HMS) {
    135             throw new ASN1Exception("ASN.1 UTCTime: wrong format for DER, identifier at ["
    136                     + tagOffset + "]");
    137         }
    138 
    139         super.readUTCTime();
    140     }
    141 
    142     public void readGeneralizedTime() throws IOException {
    143         if (tag == ASN1Constants.TAG_C_GENERALIZEDTIME) {
    144             // It is a string type and it can be encoded as primitive or constructed.
    145             throw new ASN1Exception("ASN.1 GeneralizedTime: constructed identifier at ["
    146                     + tagOffset + "]. Not valid for DER.");
    147         }
    148 
    149         super.readGeneralizedTime();
    150     }
    151 }
    152