1/*
2 * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.security.util;
27
28import java.io.IOException;
29import java.util.ArrayList;
30
31/**
32 * A package private utility class to convert indefinite length DER
33 * encoded byte arrays to definite length DER encoded byte arrays.
34 *
35 * This assumes that the basic data structure is "tag, length, value"
36 * triplet. In the case where the length is "indefinite", terminating
37 * end-of-contents bytes are expected.
38 *
39 * @author Hemma Prafullchandra
40 */
41class DerIndefLenConverter {
42
43    private static final int TAG_MASK            = 0x1f; // bits 5-1
44    private static final int FORM_MASK           = 0x20; // bits 6
45    private static final int CLASS_MASK          = 0xC0; // bits 8 and 7
46
47    private static final int LEN_LONG            = 0x80; // bit 8 set
48    private static final int LEN_MASK            = 0x7f; // bits 7 - 1
49    private static final int SKIP_EOC_BYTES      = 2;
50
51    private byte[] data, newData;
52    private int newDataPos, dataPos, dataSize, index;
53    private int unresolved = 0;
54
55    private ArrayList<Object> ndefsList = new ArrayList<Object>();
56
57    private int numOfTotalLenBytes = 0;
58
59    private boolean isEOC(int tag) {
60        return (((tag & TAG_MASK) == 0x00) &&  // EOC
61                ((tag & FORM_MASK) == 0x00) && // primitive
62                ((tag & CLASS_MASK) == 0x00)); // universal
63    }
64
65    // if bit 8 is set then it implies either indefinite length or long form
66    static boolean isLongForm(int lengthByte) {
67        return ((lengthByte & LEN_LONG) == LEN_LONG);
68    }
69
70    /*
71     * Default package private constructor
72     */
73    DerIndefLenConverter() { }
74
75    /**
76     * Checks whether the given length byte is of the form
77     * <em>Indefinite</em>.
78     *
79     * @param lengthByte the length byte from a DER encoded
80     *        object.
81     * @return true if the byte is of Indefinite form otherwise
82     *         returns false.
83     */
84    static boolean isIndefinite(int lengthByte) {
85        return (isLongForm(lengthByte) && ((lengthByte & LEN_MASK) == 0));
86    }
87
88    /**
89     * Parse the tag and if it is an end-of-contents tag then
90     * add the current position to the <code>eocList</code> vector.
91     */
92    private void parseTag() throws IOException {
93        if (dataPos == dataSize)
94            return;
95        if (isEOC(data[dataPos]) && (data[dataPos + 1] == 0)) {
96            int numOfEncapsulatedLenBytes = 0;
97            Object elem = null;
98            int index;
99            for (index = ndefsList.size()-1; index >= 0; index--) {
100                // Determine the first element in the vector that does not
101                // have a matching EOC
102                elem = ndefsList.get(index);
103                if (elem instanceof Integer) {
104                    break;
105                } else {
106                    numOfEncapsulatedLenBytes += ((byte[])elem).length - 3;
107                }
108            }
109            if (index < 0) {
110                throw new IOException("EOC does not have matching " +
111                                      "indefinite-length tag");
112            }
113            int sectionLen = dataPos - ((Integer)elem).intValue() +
114                             numOfEncapsulatedLenBytes;
115            byte[] sectionLenBytes = getLengthBytes(sectionLen);
116            ndefsList.set(index, sectionLenBytes);
117            unresolved--;
118
119            // Add the number of bytes required to represent this section
120            // to the total number of length bytes,
121            // and subtract the indefinite-length tag (1 byte) and
122            // EOC bytes (2 bytes) for this section
123            numOfTotalLenBytes += (sectionLenBytes.length - 3);
124        }
125        dataPos++;
126    }
127
128    /**
129     * Write the tag and if it is an end-of-contents tag
130     * then skip the tag and its 1 byte length of zero.
131     */
132    private void writeTag() {
133        if (dataPos == dataSize)
134            return;
135        int tag = data[dataPos++];
136        if (isEOC(tag) && (data[dataPos] == 0)) {
137            dataPos++;  // skip length
138            writeTag();
139        } else
140            newData[newDataPos++] = (byte)tag;
141    }
142
143    /**
144     * Parse the length and if it is an indefinite length then add
145     * the current position to the <code>ndefsList</code> vector.
146     */
147    private int parseLength() throws IOException {
148        int curLen = 0;
149        if (dataPos == dataSize)
150            return curLen;
151        int lenByte = data[dataPos++] & 0xff;
152        if (isIndefinite(lenByte)) {
153            ndefsList.add(dataPos);
154            unresolved++;
155            return curLen;
156        }
157        if (isLongForm(lenByte)) {
158            lenByte &= LEN_MASK;
159            if (lenByte > 4) {
160                throw new IOException("Too much data");
161            }
162            if ((dataSize - dataPos) < (lenByte + 1)) {
163                throw new IOException("Too little data");
164            }
165            for (int i = 0; i < lenByte; i++) {
166                curLen = (curLen << 8) + (data[dataPos++] & 0xff);
167            }
168            if (curLen < 0) {
169                throw new IOException("Invalid length bytes");
170            }
171        } else {
172           curLen = (lenByte & LEN_MASK);
173        }
174        return curLen;
175    }
176
177    /**
178     * Write the length and if it is an indefinite length
179     * then calculate the definite length from the positions
180     * of the indefinite length and its matching EOC terminator.
181     * Then, write the value.
182     */
183    private void writeLengthAndValue() throws IOException {
184        if (dataPos == dataSize)
185           return;
186        int curLen = 0;
187        int lenByte = data[dataPos++] & 0xff;
188        if (isIndefinite(lenByte)) {
189            byte[] lenBytes = (byte[])ndefsList.get(index++);
190            System.arraycopy(lenBytes, 0, newData, newDataPos,
191                             lenBytes.length);
192            newDataPos += lenBytes.length;
193            return;
194        }
195        if (isLongForm(lenByte)) {
196            lenByte &= LEN_MASK;
197            for (int i = 0; i < lenByte; i++) {
198                curLen = (curLen << 8) + (data[dataPos++] & 0xff);
199            }
200            if (curLen < 0) {
201                throw new IOException("Invalid length bytes");
202            }
203        } else {
204            curLen = (lenByte & LEN_MASK);
205        }
206        writeLength(curLen);
207        writeValue(curLen);
208    }
209
210    private void writeLength(int curLen) {
211        if (curLen < 128) {
212            newData[newDataPos++] = (byte)curLen;
213
214        } else if (curLen < (1 << 8)) {
215            newData[newDataPos++] = (byte)0x81;
216            newData[newDataPos++] = (byte)curLen;
217
218        } else if (curLen < (1 << 16)) {
219            newData[newDataPos++] = (byte)0x82;
220            newData[newDataPos++] = (byte)(curLen >> 8);
221            newData[newDataPos++] = (byte)curLen;
222
223        } else if (curLen < (1 << 24)) {
224            newData[newDataPos++] = (byte)0x83;
225            newData[newDataPos++] = (byte)(curLen >> 16);
226            newData[newDataPos++] = (byte)(curLen >> 8);
227            newData[newDataPos++] = (byte)curLen;
228
229        } else {
230            newData[newDataPos++] = (byte)0x84;
231            newData[newDataPos++] = (byte)(curLen >> 24);
232            newData[newDataPos++] = (byte)(curLen >> 16);
233            newData[newDataPos++] = (byte)(curLen >> 8);
234            newData[newDataPos++] = (byte)curLen;
235        }
236    }
237
238    private byte[] getLengthBytes(int curLen) {
239        byte[] lenBytes;
240        int index = 0;
241
242        if (curLen < 128) {
243            lenBytes = new byte[1];
244            lenBytes[index++] = (byte)curLen;
245
246        } else if (curLen < (1 << 8)) {
247            lenBytes = new byte[2];
248            lenBytes[index++] = (byte)0x81;
249            lenBytes[index++] = (byte)curLen;
250
251        } else if (curLen < (1 << 16)) {
252            lenBytes = new byte[3];
253            lenBytes[index++] = (byte)0x82;
254            lenBytes[index++] = (byte)(curLen >> 8);
255            lenBytes[index++] = (byte)curLen;
256
257        } else if (curLen < (1 << 24)) {
258            lenBytes = new byte[4];
259            lenBytes[index++] = (byte)0x83;
260            lenBytes[index++] = (byte)(curLen >> 16);
261            lenBytes[index++] = (byte)(curLen >> 8);
262            lenBytes[index++] = (byte)curLen;
263
264        } else {
265            lenBytes = new byte[5];
266            lenBytes[index++] = (byte)0x84;
267            lenBytes[index++] = (byte)(curLen >> 24);
268            lenBytes[index++] = (byte)(curLen >> 16);
269            lenBytes[index++] = (byte)(curLen >> 8);
270            lenBytes[index++] = (byte)curLen;
271        }
272
273        return lenBytes;
274    }
275
276    // Returns the number of bytes needed to represent the given length
277    // in ASN.1 notation
278    private int getNumOfLenBytes(int len) {
279        int numOfLenBytes = 0;
280
281        if (len < 128) {
282            numOfLenBytes = 1;
283        } else if (len < (1 << 8)) {
284            numOfLenBytes = 2;
285        } else if (len < (1 << 16)) {
286            numOfLenBytes = 3;
287        } else if (len < (1 << 24)) {
288            numOfLenBytes = 4;
289        } else {
290            numOfLenBytes = 5;
291        }
292        return numOfLenBytes;
293    }
294
295    /**
296     * Parse the value;
297     */
298    private void parseValue(int curLen) {
299        dataPos += curLen;
300    }
301
302    /**
303     * Write the value;
304     */
305    private void writeValue(int curLen) {
306        for (int i=0; i < curLen; i++)
307            newData[newDataPos++] = data[dataPos++];
308    }
309
310    /**
311     * Converts a indefinite length DER encoded byte array to
312     * a definte length DER encoding.
313     *
314     * @param indefData the byte array holding the indefinite
315     *        length encoding.
316     * @return the byte array containing the definite length
317     *         DER encoding.
318     * @exception IOException on parsing or re-writing errors.
319     */
320    byte[] convert(byte[] indefData) throws IOException {
321        data = indefData;
322        dataPos=0; index=0;
323        dataSize = data.length;
324        int len=0;
325        int unused = 0;
326
327        // parse and set up the vectors of all the indefinite-lengths
328        while (dataPos < dataSize) {
329            parseTag();
330            len = parseLength();
331            parseValue(len);
332            if (unresolved == 0) {
333                unused = dataSize - dataPos;
334                dataSize = dataPos;
335                break;
336            }
337        }
338
339        if (unresolved != 0) {
340            throw new IOException("not all indef len BER resolved");
341        }
342
343        newData = new byte[dataSize + numOfTotalLenBytes + unused];
344        dataPos=0; newDataPos=0; index=0;
345
346        // write out the new byte array replacing all the indefinite-lengths
347        // and EOCs
348        while (dataPos < dataSize) {
349           writeTag();
350           writeLengthAndValue();
351        }
352        System.arraycopy(indefData, dataSize,
353                         newData, dataSize + numOfTotalLenBytes, unused);
354
355        return newData;
356    }
357}
358