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 
26 package sun.security.util;
27 
28 import java.io.IOException;
29 import 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  */
41 class 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 
isEOC(int tag)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
isLongForm(int lengthByte)66     static boolean isLongForm(int lengthByte) {
67         return ((lengthByte & LEN_LONG) == LEN_LONG);
68     }
69 
70     /*
71      * Default package private constructor
72      */
DerIndefLenConverter()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      */
isIndefinite(int lengthByte)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      */
parseTag()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      */
writeTag()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      */
parseLength()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(new Integer(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      */
writeLengthAndValue()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 
writeLength(int curLen)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 
getLengthBytes(int curLen)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
getNumOfLenBytes(int len)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      */
parseValue(int curLen)298     private void parseValue(int curLen) {
299         dataPos += curLen;
300     }
301 
302     /**
303      * Write the value;
304      */
writeValue(int curLen)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      */
convert(byte[] indefData)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