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
23package org.apache.harmony.security.asn1;
24
25import java.io.IOException;
26
27
28/**
29 * Implicitly tagged ASN.1 type.
30 *
31 * @see <a href="http://asn1.elibel.tm.fr/en/standards/index.htm">ASN.1</a>
32 */
33public final class ASN1Implicit extends ASN1Type {
34
35    /** primitive type of tagging */
36    private static final int TAGGING_PRIMITIVE = 0;
37
38    /** constructed type of tagging */
39    private static final int TAGGING_CONSTRUCTED = 1;
40
41    /** string type of tagging */
42    private static final int TAGGING_STRING = 2;
43
44    /** tagged ASN.1 type */
45    private final ASN1Type type;
46
47    /**
48     * type of tagging. There are three of them
49     * 1) primitive: only primitive identifier is valid
50     * 2) constructed: only constructed identifier is valid
51     * 3) string: both identifiers are valid
52     */
53    private final int taggingType;
54
55    /**
56     * Constructs implicitly tagged ASN.1 type
57     * with context-specific tag class and specified tag number.
58     *
59     * @param tagNumber - ASN.1 tag number
60     * @param type - ASN.1 type to be tagged
61     * @throws IllegalArgumentException - if tagNumber or type is invalid
62     */
63    public ASN1Implicit(int tagNumber, ASN1Type type) {
64        super(CLASS_CONTEXTSPECIFIC, tagNumber);
65
66        if ((type instanceof ASN1Choice) || (type instanceof ASN1Any)) {
67            // According to X.680:
68            // 'The IMPLICIT alternative shall not be used if the type
69            // defined by "Type" is an untagged choice type or an
70            // untagged open type'
71            throw new IllegalArgumentException("Implicit tagging can not be used for ASN.1 ANY or CHOICE type");
72        }
73
74        this.type = type;
75
76        if (type.checkTag(type.id)) {
77            if (type.checkTag(type.constrId)) {
78                // the base encoding can be primitive ot constructed
79                // use both encodings
80                taggingType = TAGGING_STRING;
81            } else {
82                // if the base encoding is primitive use primitive encoding
83                taggingType = TAGGING_PRIMITIVE;
84            }
85        } else {
86            // if the base encoding is constructed use constructed encoding
87            taggingType = TAGGING_CONSTRUCTED;
88        }
89    }
90
91    public final boolean checkTag(int identifier) {
92        switch (taggingType) {
93        case TAGGING_PRIMITIVE:
94            return id == identifier;
95        case TAGGING_CONSTRUCTED:
96            return constrId == identifier;
97        default: // TAGGING_STRING
98            return id == identifier || constrId == identifier;
99        }
100    }
101
102    public Object decode(BerInputStream in) throws IOException {
103        if (!checkTag(in.tag)) {
104            // FIXME need look for tagging type
105            throw new ASN1Exception("ASN.1 implicitly tagged type expected at " +
106                    "[" + in.tagOffset + "]. Expected tag: " + Integer.toHexString(id) + ", " +
107                    "but got " + Integer.toHexString(in.tag));
108        }
109
110        // substitute identifier for further decoding
111        if (id == in.tag) {
112            in.tag = type.id;
113        } else {
114            in.tag = type.constrId;
115        }
116        in.content = type.decode(in);
117
118        if (in.isVerify) {
119            return null;
120        }
121        return getDecodedObject(in);
122    }
123
124    public void encodeASN(BerOutputStream out) {
125        //FIXME need another way for specifying identifier to be encoded
126        if (taggingType == TAGGING_CONSTRUCTED) {
127            out.encodeTag(constrId);
128        } else {
129            out.encodeTag(id);
130        }
131        encodeContent(out);
132    }
133
134    public void encodeContent(BerOutputStream out) {
135        type.encodeContent(out);
136    }
137
138    public void setEncodingContent(BerOutputStream out) {
139        type.setEncodingContent(out);
140    }
141}
142