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
23package org.apache.harmony.security.asn1;
24
25import java.io.IOException;
26import 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 */
33public 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