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