diff --git a/asn1.js b/asn1.js index d242cd1..b787c8b 100644 --- a/asn1.js +++ b/asn1.js @@ -94,13 +94,13 @@ Stream.prototype.isASCII = function (start, end) { } return true; }; -Stream.prototype.parseStringISO = function (start, end) { +Stream.prototype.parseStringISO = function (start, end, maxLength) { var s = ""; for (var i = start; i < end; ++i) s += String.fromCharCode(this.get(i)); - return s; + return { size: s.length, str: stringCut(s, maxLength) }; }; -Stream.prototype.parseStringUTF = function (start, end) { +Stream.prototype.parseStringUTF = function (start, end, maxLength) { function ex(c) { // must be 10xxxxxx if ((c < 0x80) || (c >= 0xC0)) throw new Error('Invalid UTF-8 continuation byte: ' + c); @@ -129,19 +129,19 @@ Stream.prototype.parseStringUTF = function (start, end) { else throw new Error('Invalid UTF-8 starting byte (since 2003 it is restricted to 4 bytes): ' + c); } - return s; + return { size: s.length, str: stringCut(s, maxLength) }; }; Stream.prototype.parseStringBMP = function (start, end) { - var str = "", hi, lo; + var s = "", hi, lo; for (var i = start; i < end; ) { hi = this.get(i++); lo = this.get(i++); - str += String.fromCharCode((hi << 8) | lo); + s += String.fromCharCode((hi << 8) | lo); } - return str; + return { size: s.length, str: stringCut(s, maxLength) }; }; Stream.prototype.parseTime = function (start, end, shortYear) { - var s = this.parseStringISO(start, end), + var s = this.parseStringISO(start, end).str, m = (shortYear ? reTimeS : reTimeL).exec(s); if (!m) return "Unrecognized time: " + s; @@ -215,18 +215,21 @@ Stream.prototype.parseBitString = function (start, end, maxLength) { } return { size: lenBit, str: s }; }; +function checkPrintable(s) { + var i, v; + for (i = 0; i < s.length; ++i) { + v = s.charCodeAt(i); + if (v < 32 && v != 9 && v != 10 && v != 13) // [\t\r\n] are (kinda) printable + throw new Error('Unprintable character at index ' + i + ' (code ' + s.str.charCodeAt(i) + ")"); + } +} Stream.prototype.parseOctetString = function (start, end, maxLength) { var len = end - start, s; try { - s = this.parseStringUTF(start, end); - var v; - for (i = 0; i < s.length; ++i) { - v = s.charCodeAt(i); - if (v < 32 && v != 9 && v != 10 && v != 13) // [\t\r\n] are (kinda) printable - throw new Error('Unprintable character at index ' + i + ' (code ' + s.charCodeAt(i) + ")"); - } - return { size: len, str: s }; + s = this.parseStringUTF(start, end, maxLength); + checkPrintable(s.str); + return { size: end - start, str: s.str }; } catch (e) { // ignore } @@ -386,7 +389,7 @@ ASN1.prototype.content = function (maxLength) { else return "(no elem)"; case 0x0C: // UTF8String - return stringCut(this.stream.parseStringUTF(content, content + len), maxLength); + return recurse(this, 'parseStringUTF', maxLength).str; case 0x12: // NumericString case 0x13: // PrintableString case 0x14: // TeletexString @@ -396,9 +399,9 @@ ASN1.prototype.content = function (maxLength) { case 0x1B: // GeneralString //case 0x19: // GraphicString //case 0x1C: // UniversalString - return stringCut(this.stream.parseStringISO(content, content + len), maxLength); + return recurse(this, 'parseStringISO', maxLength).str; case 0x1E: // BMPString - return stringCut(this.stream.parseStringBMP(content, content + len), maxLength); + return recurse(this, 'parseStringBMP', maxLength).str; case 0x17: // UTCTime case 0x18: // GeneralizedTime return this.stream.parseTime(content, content + len, (this.tag.tagNumber == 0x17)); diff --git a/test.js b/test.js index cfafe25..a4e8b5b 100755 --- a/test.js +++ b/test.js @@ -14,7 +14,7 @@ const tests = [ ['23090303006E5D030206C0', '(18 bit)\n011011100101110111', 'ntop, bit string (constructed encoding): "0110111001011101" + "11"'], ['160D7465737431407273612E636F6D', 'test1@rsa.com', 'ntop, ia5string: DER encoding'], ['16810D7465737431407273612E636F6D', 'test1@rsa.com', 'ntop, ia5string: long form of length octets'], - ['36131605746573743116014016077273612E636F6D', 'test1@rsa.com', 'ntop, ia5string: constructed encoding: "test1" + "@" + "rsa.com"', 'constructed strings are currently unsupported'], + ['36131605746573743116014016077273612E636F6D', 'test1@rsa.com', 'ntop, ia5string: constructed encoding: "test1" + "@" + "rsa.com"'], ['020100', '0', 'ntop, integer: 0'], ['02017F', '127', 'ntop, integer: 127'], ['02020080', '128', 'ntop, integer: 128'], @@ -29,7 +29,7 @@ const tests = [ ['240C040401234567040489ABCDEF', '(8 byte)\n0123456789ABCDEF', 'ntop, octet string (constructed encoding): 01…67 + 89…ef'], ['130B5465737420557365722031', 'Test User 1', 'ntop, printable string: DER encoding'], ['13810B5465737420557365722031', 'Test User 1', 'ntop, printable string: long form of length octets'], - ['330F130554657374201306557365722031', 'Test User 1', 'ntop, printable string: constructed encoding: "Test " + "User 1"', 'constructed strings are currently unsupported'], + ['330F130554657374201306557365722031', 'Test User 1', 'ntop, printable string: constructed encoding: "Test " + "User 1"'], ['140F636CC26573207075626C6971756573', 'clés publiques', 'ntop, t61string: DER encoding', 'T61 strings are currently not converted'], ['14810F636CC26573207075626C6971756573', 'clés publiques', 'ntop, t61string: long form of length octets', 'T61 strings are currently not converted'], ['34151405636CC2657314012014097075626C6971756573', 'clés publiques', 'ntop, t61string: constructed encoding: "clés" + " " + "publiques"', 'T61 strings are currently not converted'], @@ -104,7 +104,7 @@ tests.forEach(function (t) { if (all) console.log('\x1B[1m\x1B[32mOK \x1B[39m\x1B[22m ' + comment); } else if (errorReason) { ++expErr; - console.log('\x1B[1m\x1B[33mEXP\x1B[39m\x1B[22m ' + comment + ' (' + errorReason + ')'); + console.log('\x1B[1m\x1B[33mEXP\x1B[39m\x1B[22m ' + comment + ' (' + errorReason + ')' + '\n' + result); } else { ++error; console.log('\x1B[1m\x1B[31mERR\x1B[39m\x1B[22m ' + comment + '\n' + result);