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 libcore.util.Objects;
26
27
28/**
29 * Encodes ASN.1 types 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 DerOutputStream extends BerOutputStream {
34    private static final int initSize = 32;
35    private int index;
36    private int[][] len = new int[initSize][];
37    private Object[][] val = new Object[initSize][];
38
39    public DerOutputStream(ASN1Type asn1, Object object) {
40        content = object;
41        index = -1;
42        asn1.setEncodingContent(this);
43        encoded = new byte[asn1.getEncodedLength(this)];
44        index = 0;
45        asn1.encodeASN(this);
46    }
47
48    @Override public void encodeChoice(ASN1Choice choice) {
49        ASN1Type type = (ASN1Type) val[index][0];
50        content = val[index][1];
51        index++;
52        type.encodeASN(this);
53    }
54
55    @Override public void encodeExplicit(ASN1Explicit explicit) {
56        content = val[index][0];
57        length = len[index][0];
58        index++;
59        explicit.type.encodeASN(this);
60    }
61
62    @Override public void encodeSequence(ASN1Sequence sequence) {
63        ASN1Type[] type = sequence.type;
64
65        Object[] values = val[index];
66        int[] compLens = len[index];
67
68        index++;
69        for (int i = 0; i < type.length; i++) {
70            if (values[i] == null) {
71                continue;
72            }
73
74            content = values[i];
75            length = compLens[i];
76
77            type[i].encodeASN(this);
78        }
79    }
80
81    @Override public void encodeSequenceOf(ASN1SequenceOf sequenceOf) {
82        encodeValueCollection(sequenceOf);
83    }
84
85    @Override public void encodeSetOf(ASN1SetOf setOf) {
86        encodeValueCollection(setOf);
87    }
88
89    private void encodeValueCollection(ASN1ValueCollection collection) {
90        Object[] values = val[index];
91        int[] compLens = len[index];
92
93        index++;
94        for (int i = 0; i < values.length; i++) {
95            content = values[i];
96            length = compLens[i];
97            collection.type.encodeASN(this);
98        }
99    }
100
101    private void push(int[] lengths, Object[] values) {
102        index++;
103        if (index == val.length) {
104
105            int[][] newLen = new int[val.length * 2][];
106            System.arraycopy(len, 0, newLen, 0, val.length);
107            len = newLen;
108
109            Object[][] newVal = new Object[val.length * 2][];
110            System.arraycopy(val, 0, newVal, 0, val.length);
111            val = newVal;
112        }
113        len[index] = lengths;
114        val[index] = values;
115    }
116
117    @Override public void getChoiceLength(ASN1Choice choice) {
118        int i = choice.getIndex(content);
119        content = choice.getObjectToEncode(content);
120
121        Object[] values = new Object[] { choice.type[i], content };
122
123        push(null, values);
124
125        choice.type[i].setEncodingContent(this);
126
127        // in case if we get content bytes while getting its length
128        // FIXME what about remove it: need redesign
129        values[1] = content;
130    }
131
132    @Override public void getExplicitLength(ASN1Explicit explicit) {
133        Object[] values = new Object[1];
134        int[] compLens = new int[1];
135
136        values[0] = content;
137
138        push(compLens, values);
139
140        explicit.type.setEncodingContent(this);
141
142        // in case if we get content bytes while getting its length
143        // FIXME what about remove it: need redesign
144        values[0] = content;
145        compLens[0] = length;
146
147        length = explicit.type.getEncodedLength(this);
148    }
149
150    @Override public void getSequenceLength(ASN1Sequence sequence) {
151        ASN1Type[] type = sequence.type;
152
153        Object[] values = new Object[type.length];
154        int[] compLens = new int[type.length];
155
156        sequence.getValues(content, values);
157
158        push(compLens, values);
159
160        int seqLen = 0;
161        for (int i = 0; i < type.length; i++) {
162            // check optional types
163            if (values[i] == null) {
164                if (sequence.OPTIONAL[i]) {
165                    continue;
166                } else {
167                    throw new RuntimeException();//FIXME type & message
168                }
169            }
170
171            if (Objects.equal(sequence.DEFAULT[i], values[i])) {
172                values[i] = null;
173                continue;
174            }
175
176            content = values[i];
177
178            type[i].setEncodingContent(this);
179
180            compLens[i] = length;
181
182            // in case if we get content bytes while getting its length
183            // FIXME what about remove it: need redesign
184            values[i] = content;
185
186            seqLen += type[i].getEncodedLength(this);
187        }
188        length = seqLen;
189    }
190
191    @Override public void getSequenceOfLength(ASN1SequenceOf sequence) {
192        getValueOfLength(sequence);
193    }
194
195    @Override public void getSetOfLength(ASN1SetOf setOf) {
196        getValueOfLength(setOf);
197    }
198
199    private void getValueOfLength(ASN1ValueCollection collection) {
200        //FIXME what about another way?
201        Object[] cv = collection.getValues(content).toArray();
202
203        Object[] values = new Object[cv.length];
204        int[] compLens = new int[values.length];
205
206        push(compLens, values);
207        int seqLen = 0;
208        for (int i = 0; i < values.length; i++) {
209
210            content = cv[i];
211
212            collection.type.setEncodingContent(this);
213
214            compLens[i] = length;
215
216            // in case if we get content bytes while getting its length
217            // FIXME what about remove it: need redesign
218            values[i] = content;
219
220            seqLen += collection.type.getEncodedLength(this);
221        }
222        length = seqLen;
223    }
224}
225