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 }