diff --git a/dom.js b/dom.js
index 259e2cd..ba66d87 100644
--- a/dom.js
+++ b/dom.js
@@ -65,11 +65,19 @@ class ASN1DOM extends ASN1 {
let node = DOM.tag('div', 'node');
node.asn1 = this;
let head = DOM.tag('div', 'head');
- head.innerHTML = "" + spaces + '' + this.typeName().replace(/_/g, ' ');
- if (this.def && this.def.id) {
- let name = DOM.tag('span', 'name');
- name.innerText = this.def.id + ' ';
- head.prepend(name);
+ const tn = this.typeName().replace(/_/g, ' ');
+ head.innerHTML = "" + spaces + '' + tn;
+ if (this.def) {
+ if (this.def.name && this.def.name != tn) {
+ let name = DOM.tag('span', 'name type');
+ name.innerText = this.def.name + ' ';
+ head.prepend(name);
+ }
+ if (this.def.id) {
+ let name = DOM.tag('span', 'name id');
+ name.innerText = this.def.id + ' ';
+ head.prepend(name);
+ }
}
let content = this.content(contentLength);
let oid;
--
2.51.2
From b850adf50f1b08c37670486c35edef9ca1600e66 Mon Sep 17 00:00:00 2001
From: Lapo Luchini
Date: Tue, 14 Mar 2023 19:16:39 +0000
Subject: [PATCH 02/14] Fix spaces in copy-paste content. Enhance `DOM.tag`
text content.
---
dom.js | 47 ++++++++++++++++++-----------------------------
1 file changed, 18 insertions(+), 29 deletions(-)
diff --git a/dom.js b/dom.js
index ba66d87..aef4c74 100644
--- a/dom.js
+++ b/dom.js
@@ -26,19 +26,17 @@ const
contentLength = 8 * lineLength,
DOM = {
ellipsis: '\u2026',
- tag: function (tagName, className) {
+ tag: function (tagName, className, text) {
let t = document.createElement(tagName);
t.className = className;
+ if (text) t.innerText = text;
return t;
},
text: function (str) {
return document.createTextNode(str);
},
space: function () {
- let t = document.createElement('span');
- t.className = 'spaces';
- t.innerHTML = ' ';
- return t;
+ return DOM.tag('span', 'spaces', ' ');
},
breakLines: function (str, length) {
let lines = str.split(/\r?\n/),
@@ -65,20 +63,19 @@ class ASN1DOM extends ASN1 {
let node = DOM.tag('div', 'node');
node.asn1 = this;
let head = DOM.tag('div', 'head');
- const tn = this.typeName().replace(/_/g, ' ');
- head.innerHTML = "" + spaces + '' + tn;
+ head.appendChild(DOM.tag('span', 'spaces', spaces));
+ const typeName = this.typeName().replace(/_/g, ' ');
if (this.def) {
- if (this.def.name && this.def.name != tn) {
- let name = DOM.tag('span', 'name type');
- name.innerText = this.def.name + ' ';
- head.prepend(name);
- }
if (this.def.id) {
- let name = DOM.tag('span', 'name id');
- name.innerText = this.def.id + ' ';
- head.prepend(name);
+ head.appendChild(DOM.tag('span', 'name id', this.def.id));
+ head.appendChild(DOM.space());
+ }
+ if (this.def.name && this.def.name != typeName) {
+ head.appendChild(DOM.tag('span', 'name type', this.def.name));
+ head.appendChild(DOM.space());
}
}
+ head.appendChild(DOM.text(typeName));
let content = this.content(contentLength);
let oid;
if (content !== null) {
@@ -94,14 +91,12 @@ class ASN1DOM extends ASN1 {
if (oid) {
if (oid.d) {
preview.appendChild(DOM.space());
- let oidd = DOM.tag('span', 'oid description');
- oidd.appendChild(DOM.text(oid.d));
+ let oidd = DOM.tag('span', 'oid description', oid.d);
preview.appendChild(oidd);
}
if (oid.c) {
preview.appendChild(DOM.space());
- let oidc = DOM.tag('span', 'oid comment');
- oidc.appendChild(DOM.text('(' + oid.c + ')'));
+ let oidc = DOM.tag('span', 'oid comment', '(' + oid.c + ')');
preview.appendChild(oidc);
}
}
@@ -162,9 +157,7 @@ class ASN1DOM extends ASN1 {
toHexDOM_sub(node, className, stream, start, end) {
if (start >= end)
return;
- let sub = DOM.tag('span', className);
- sub.appendChild(DOM.text(
- stream.hexDump(start, end)));
+ let sub = DOM.tag('span', className, stream.hexDump(start, end));
node.appendChild(sub);
}
toHexDOM(root) {
@@ -214,13 +207,9 @@ class ASN1DOM extends ASN1 {
else {
let end1 = start + 5 * 16 - (start & 0xF);
let start2 = end - 16 - (end & 0xF);
- node.appendChild(DOM.text(
- this.stream.hexDump(start, end1)));
- let sub = DOM.tag('span', 'skip');
- sub.appendChild(DOM.text('\u2026 skipping ' + (start2 - end1) + ' bytes \u2026\n'));
- node.appendChild(sub);
- node.appendChild(DOM.text(
- this.stream.hexDump(start2, end)));
+ node.appendChild(DOM.text(this.stream.hexDump(start, end1)));
+ node.appendChild(DOM.tag('span', 'skip', '\u2026 skipping ' + (start2 - end1) + ' bytes \u2026\n'));
+ node.appendChild(DOM.text(this.stream.hexDump(start2, end)));
}
} else if (this.sub.length > 0) {
let first = this.sub[0];
--
2.51.2
From a8fd6a32a27de972120a2a0774302c7a764a7184 Mon Sep 17 00:00:00 2001
From: Lapo Luchini
Date: Tue, 14 Mar 2023 19:17:40 +0000
Subject: [PATCH 03/14] Improve CSS for new definitions a bit.
---
index.css | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/index.css b/index.css
index c572b58..67231e6 100644
--- a/index.css
+++ b/index.css
@@ -143,8 +143,12 @@ header {
display: block;
}
.name {
+ margin-right: 1em;
color: var(--preview-border-color);
}
+.name.id {
+ color: var(--main-text-color);
+}
.value {
display: none;
position: absolute;
--
2.51.2
From 5734cc2f16b9b7b613df703a90a2f51b762f0bc1 Mon Sep 17 00:00:00 2001
From: Lapo Luchini
Date: Tue, 14 Mar 2023 19:18:33 +0000
Subject: [PATCH 04/14] Add support for comments between identified and start
of module definition. Refactor to ES6 class.
---
parseRFC.js | 772 ++++++++++++++++++++++++++--------------------------
1 file changed, 388 insertions(+), 384 deletions(-)
diff --git a/parseRFC.js b/parseRFC.js
index 23690d3..0225cb0 100755
--- a/parseRFC.js
+++ b/parseRFC.js
@@ -24,266 +24,19 @@ const
],
};
-let asn1;
-let currentMod;
-
-function Parser(enc, pos) {
- this.enc = enc;
- this.pos = pos;
- this.start = pos;
-}
-Parser.prototype.getChar = function (pos) {
- if (pos === undefined)
- pos = this.pos++;
- if (pos >= this.enc.length)
- throw 'Requesting byte offset ' + pos + ' on a stream of length ' + this.enc.length;
- return this.enc.charAt(pos);
-};
-Parser.prototype.exception = function (s, pos) {
- if (pos == undefined) pos = this.pos;
- let from = Math.max(pos - 30, this.start);
- let to = Math.min(pos + 30, this.enc.length);
- let ctx = '';
- let arrow = '';
- let i = from;
- for (; i < pos; ++i) {
- ctx += this.getChar(i);
- arrow += ' ';
- }
- ctx += this.getChar(i++);
- arrow += '^';
- for (; i < to; ++i)
- ctx += this.getChar(i);
- throw new Error('[position ' + pos + '] ' + s + '\n' + ctx.replace(/\s/g, ' ') + '\n' + arrow);
-};
-Parser.prototype.peek = function () {
- return (typeof this.enc == 'string') ? this.enc.charCodeAt(this.pos) : this.enc[this.pos];
-};
-Parser.prototype.peekChar = function () {
- return (typeof this.enc == 'string') ? this.enc.charAt(this.pos) : String.fromCharCode(this.enc[this.pos]);
-};
-Parser.prototype.isWhitespace = function () {
- let c = this.peekChar();
- return c == ' ' || c == '\n';
-};
-Parser.prototype.isDigit = function () {
- let c = this.peekChar();
- return c >= '0' && c <= '9';
-};
// const reWhitespace = /(?:\s|--(?:[}-]?[^\n}-])*(?:\n|--))*/y;
const reWhitespace = /(?:\s|--(?:-?[^\n-])*(?:\n|--))*/my;
-Parser.prototype.skipWhitespace = function () {
- reWhitespace.lastIndex = this.pos;
- let s = reWhitespace.exec(this.enc);
- if (s)
- this.pos = reWhitespace.lastIndex;
-};
-// DefStream.prototype.eat = function (str) {
-// for (let i = 0; i < str.length; ++i) {
-// let c = this.getChar();
-// if (c != str.charAt(i))
-// throw new Error("Found '" + c + "', was expecting '" + str.charAt(i) + "'");
-// }
-// };
-Parser.prototype.getRegEx = function (type, re) {
- this.skipWhitespace();
- re.lastIndex = this.pos;
- let s = re.exec(this.enc); //TODO: does not work with typed arrays
- if (!s)
- this.exception("Found '" + this.peekChar() + "', was expecting a " + type);
- s = s[0];
- // console.log('[debug] getRexEx@' + this.pos + ' = ' + s);
- this.pos = re.lastIndex;
- this.skipWhitespace();
- return s;
-};
const reIdentifier = /[a-zA-Z](?:[-]?[a-zA-Z0-9])*/y;
-Parser.prototype.parseIdentifier = function () {
- let id = this.getRegEx('identifier', reIdentifier);
- // console.log('[debug] parseIdentifier = ' + id);
- return id;
-};
const reNumber = /0|[1-9][0-9]*/y;
-Parser.prototype.parseNumber = function () {
- let id = this.getRegEx('number', reNumber);
- // console.log('[debug] parseNumber = ' + id);
- return id;
-};
const reToken = /[(){},[\];]|::=|OPTIONAL|DEFAULT|NULL|TRUE|FALSE|\.\.|OF|SIZE|MIN|MAX|DEFINED BY|DEFINITIONS|TAGS|BEGIN|EXPORTS|IMPORTS|FROM|END/y;
-Parser.prototype.parseToken = function () {
- let tok = this.getRegEx('token', reToken);
- return tok;
-};
-Parser.prototype.tryToken = function (expect) {
- let p = this.pos;
- let t;
- try { t = this.parseToken(); } catch (e) { /*ignore*/ }
- // console.log('[debug] tryToken(' + expect + ') = ' + t);
- if (t == expect)
- return true;
- else {
- this.pos = p;
- return false;
- }
-};
-Parser.prototype.expectToken = function (expect) {
- let p = this.pos;
- let t;
- try { t = this.parseToken(); }
- catch (e) { console.log('[debug] expectToken', e); }
- // console.log('[debug] expectToken(' + expect + ') = ' + t);
- if (t != expect) {
- this.pos = p;
- this.exception("Found '" + t + "', was expecting '" + expect + "'");
- }
-};
-Parser.prototype.parseNumberOrValue = function () {
- if (this.isDigit())
- return +this.parseNumber();
- return this.parseIdentifier();
-};
-Parser.prototype.parseRange = function () {
- let min = this.tryToken('MIN') ? 'MIN' : this.parseNumberOrValue();
- if (this.tryToken('..')) {
- let max = this.tryToken('MAX') ? 'MAX' : this.parseNumberOrValue();
- return [min, max];
- }
- return min;
-};
const reType = /ANY|BOOLEAN|INTEGER|(?:BIT|OCTET)\s+STRING|OBJECT\s+IDENTIFIER|SEQUENCE|SET|CHOICE|ENUMERATED|(?:Generalized|UTC)Time|(?:BMP|General|Graphic|IA5|ISO64|Numeric|Printable|Teletex|T61|Universal|UTF8|Videotex|Visible)String/y;
-Parser.prototype.parseBuiltinType = function () {
- let x = {
- name: this.getRegEx('type', reType),
- type: 'builtin',
- };
- // console.log('[debug] parseType = ' + x.name);
- try {
- switch (x.name) {
- case 'ANY':
- if (this.tryToken('DEFINED BY'))
- x.definedBy = this.parseIdentifier();
- break;
- case 'BOOLEAN':
- case 'OCTET STRING':
- case 'OBJECT IDENTIFIER':
- break;
- case 'CHOICE':
- x.content = this.parseElementTypeList();
- break;
- case 'SEQUENCE':
- case 'SET':
- if (this.peekChar() == '{') {
- x.content = this.parseElementTypeList();
- } else {
- x.typeOf = 1;
- if (this.tryToken('SIZE')) {
- this.expectToken('(');
- x.size = this.parseRange();
- this.expectToken(')');
- }
- this.expectToken('OF');
- x.content = [this.parseType()];
- }
- break;
- case 'INTEGER':
- if (this.tryToken('(')) {
- x.range = this.parseRange();
- this.expectToken(')');
- }
- // falls through
- case 'ENUMERATED':
- case 'BIT STRING':
- if (this.tryToken('{')) {
- x.content = {};
- do {
- let id = this.parseIdentifier();
- this.expectToken('(');
- let val = this.parseNumber(); //TODO: signed
- this.expectToken(')');
- x.content[id] = +val;
- } while (this.tryToken(','));
- this.expectToken('}');
- }
- break;
- case 'BMPString':
- case 'GeneralString':
- case 'GraphicString':
- case 'IA5String':
- case 'ISO646String':
- case 'NumericString':
- case 'PrintableString':
- case 'TeletexString':
- case 'T61String':
- case 'UniversalString':
- case 'UTF8String':
- case 'VideotexString':
- case 'VisibleString':
- if (this.tryToken('(')) {
- if (this.tryToken('SIZE')) {
- this.expectToken('(');
- x.size = this.parseRange();
- this.expectToken(')');
- }
- this.expectToken(')');
- }
- break;
- default:
- x.content = 'TODO:unknown';
- }
- } catch (e) {
- console.log('[debug] parseBuiltinType content', e);
- x.content = 'TODO:exception';
- }
- return x;
-};
const reTagClass = /UNIVERSAL|APPLICATION|PRIVATE|/y;
const reTagType = /IMPLICIT|EXPLICIT|/y;
-Parser.prototype.parseTaggedType = function () {
- this.expectToken('[');
- let tagClass = this.getRegEx('class', reTagClass) || 'CONTEXT'; //TODO: use module defaults
- let t = this.parseNumber();
- this.expectToken(']');
- let plicit = this.getRegEx('explicit/implicit', reTagType);
- if (plicit == '') plicit = currentMod.tagDefault;
- let x = this.parseType();
- return {
- name: '[' + t + ']',
- type: 'tag',
- 'class': tagClass,
- explicit: (plicit == 'EXPLICIT'),
- content: [{ name: '', type: x }],
- };
-};
-Parser.prototype.parseType = function () {
- if (this.peekChar() == '[')
- return this.parseTaggedType();
- let p = this.pos;
- try {
- return this.parseBuiltinType();
- } catch (e) {
- // console.log('[debug] parseAssignment failed on parseType', e);
- this.pos = p;
- let x = {
- name: this.parseIdentifier(),
- type: 'defined',
- };
- // let from = searchImportedType(x.name);
- // if (from)
- // x.module = from;
- return x;
- //TODO "restricted string type"
- }
-};
-Parser.prototype.parseValueBoolean = function () {
- let p = this.pos;
- let t = this.parseToken();
- if (t == 'TRUE')
- return true;
- if (t == 'FALSE')
- return false;
- this.pos = p;
- this.exception("Found '" + t + "', was expecting a boolean");
-};
+const reTagDefault = /(AUTOMATIC|IMPLICIT|EXPLICIT) TAGS|/y;
+
+let asn1;
+let currentMod;
+
function searchImportedValue(id) {
for (let imp of Object.values(currentMod.imports))
for (let name of imp.types)
@@ -296,154 +49,405 @@ function searchImportedValue(id) {
}
throw new Error('Cannot find imported value in any module: ' + id);
}
-Parser.prototype.parseValueOID = function () {
- this.expectToken('{');
- let v = '';
- while (!this.tryToken('}')) {
+
+class Parser {
+ constructor(enc, pos) {
+ this.enc = enc;
+ this.pos = pos;
+ this.start = pos;
+ }
+ getChar(pos) {
+ if (pos === undefined)
+ pos = this.pos++;
+ if (pos >= this.enc.length)
+ throw 'Requesting byte offset ' + pos + ' on a stream of length ' + this.enc.length;
+ return this.enc.charAt(pos);
+ }
+ exception(s, pos) {
+ if (pos == undefined) pos = this.pos;
+ let from = Math.max(pos - 30, this.start);
+ let to = Math.min(pos + 30, this.enc.length);
+ let ctx = '';
+ let arrow = '';
+ let i = from;
+ for (; i < pos; ++i) {
+ ctx += this.getChar(i);
+ arrow += ' ';
+ }
+ ctx += this.getChar(i++);
+ arrow += '^';
+ for (; i < to; ++i)
+ ctx += this.getChar(i);
+ throw new Error('[position ' + pos + '] ' + s + '\n' + ctx.replace(/\s/g, ' ') + '\n' + arrow);
+ }
+ peek() {
+ return (typeof this.enc == 'string') ? this.enc.charCodeAt(this.pos) : this.enc[this.pos];
+ }
+ peekChar() {
+ return (typeof this.enc == 'string') ? this.enc.charAt(this.pos) : String.fromCharCode(this.enc[this.pos]);
+ }
+ isWhitespace() {
+ let c = this.peekChar();
+ return c == ' ' || c == '\n';
+ }
+ isDigit() {
+ let c = this.peekChar();
+ return c >= '0' && c <= '9';
+ }
+ skipWhitespace() {
+ reWhitespace.lastIndex = this.pos;
+ let s = reWhitespace.exec(this.enc);
+ if (s)
+ this.pos = reWhitespace.lastIndex;
+ }
+ // DefStream.prototype.eat = function (str) {
+ // for (let i = 0; i < str.length; ++i) {
+ // let c = this.getChar();
+ // if (c != str.charAt(i))
+ // throw new Error("Found '" + c + "', was expecting '" + str.charAt(i) + "'");
+ // }
+ // };
+ getRegEx(type, re) {
+ this.skipWhitespace();
+ re.lastIndex = this.pos;
+ let s = re.exec(this.enc); //TODO: does not work with typed arrays
+ if (!s)
+ this.exception("Found '" + this.peekChar() + "', was expecting a " + type);
+ s = s[0];
+ // console.log('[debug] getRexEx@' + this.pos + ' = ' + s);
+ this.pos = re.lastIndex;
+ this.skipWhitespace();
+ return s;
+ }
+ parseIdentifier() {
+ let id = this.getRegEx('identifier', reIdentifier);
+ // console.log('[debug] parseIdentifier = ' + id);
+ return id;
+ }
+ parseNumber() {
+ let id = this.getRegEx('number', reNumber);
+ // console.log('[debug] parseNumber = ' + id);
+ return id;
+ }
+ parseToken() {
+ let tok = this.getRegEx('token', reToken);
+ return tok;
+ }
+ tryToken(expect) {
let p = this.pos;
- let val;
- if (this.isDigit())
- val = this.parseNumber();
+ let t;
+ try { t = this.parseToken(); } catch (e) { /*ignore*/ }
+ // console.log('[debug] tryToken(' + expect + ') = ' + t);
+ if (t == expect)
+ return true;
else {
this.pos = p;
- let id = this.parseIdentifier();
- if (this.tryToken('(')) {
- val = this.parseNumber();
- this.expectToken(')');
- } else {
- if (id in currentMod.values) // defined in local module
- val = currentMod.values[id].value;
- else
- val = searchImportedValue(id);
- }
+ return false;
}
- if (v.length) v += '.';
- v += val;
}
- return v;
-};
-Parser.prototype.parseValue = function () {
- let c = this.peekChar();
- if (c == '{')
- return this.parseValueOID();
- if (c >= '0' && c <= '9')
- return +this.parseNumber();
- if (c == '-')
- return -this.parseNumber();
- let p = this.pos;
- try {
- switch (this.parseToken()) {
- case 'TRUE':
- return true;
- case 'FALSE':
- return false;
- case 'NULL':
- return null;
+ expectToken(expect) {
+ let p = this.pos;
+ let t;
+ try { t = this.parseToken(); }
+ catch (e) { console.log('[debug] expectToken', e); }
+ // console.log('[debug] expectToken(' + expect + ') = ' + t);
+ if (t != expect) {
+ this.pos = p;
+ this.exception("Found '" + t + "', was expecting '" + expect + "'");
}
- } catch (e) {
- this.pos = p;
}
- p = this.pos;
- try {
+ parseNumberOrValue() {
+ if (this.isDigit())
+ return +this.parseNumber();
return this.parseIdentifier();
- } catch (e) {
+ }
+ parseRange() {
+ let min = this.tryToken('MIN') ? 'MIN' : this.parseNumberOrValue();
+ if (this.tryToken('..')) {
+ let max = this.tryToken('MAX') ? 'MAX' : this.parseNumberOrValue();
+ return [min, max];
+ }
+ return min;
+ }
+ parseBuiltinType() {
+ let x = {
+ name: this.getRegEx('type', reType),
+ type: 'builtin',
+ };
+ // console.log('[debug] parseType = ' + x.name);
+ try {
+ switch (x.name) {
+ case 'ANY':
+ if (this.tryToken('DEFINED BY'))
+ x.definedBy = this.parseIdentifier();
+ break;
+ case 'BOOLEAN':
+ case 'OCTET STRING':
+ case 'OBJECT IDENTIFIER':
+ break;
+ case 'CHOICE':
+ x.content = this.parseElementTypeList();
+ break;
+ case 'SEQUENCE':
+ case 'SET':
+ if (this.peekChar() == '{') {
+ x.content = this.parseElementTypeList();
+ } else {
+ x.typeOf = 1;
+ if (this.tryToken('SIZE')) {
+ this.expectToken('(');
+ x.size = this.parseRange();
+ this.expectToken(')');
+ }
+ this.expectToken('OF');
+ x.content = [this.parseType()];
+ }
+ break;
+ case 'INTEGER':
+ if (this.tryToken('(')) {
+ x.range = this.parseRange();
+ this.expectToken(')');
+ }
+ // falls through
+ case 'ENUMERATED':
+ case 'BIT STRING':
+ if (this.tryToken('{')) {
+ x.content = {};
+ do {
+ let id = this.parseIdentifier();
+ this.expectToken('(');
+ let val = this.parseNumber(); //TODO: signed
+ this.expectToken(')');
+ x.content[id] = +val;
+ } while (this.tryToken(','));
+ this.expectToken('}');
+ }
+ break;
+ case 'BMPString':
+ case 'GeneralString':
+ case 'GraphicString':
+ case 'IA5String':
+ case 'ISO646String':
+ case 'NumericString':
+ case 'PrintableString':
+ case 'TeletexString':
+ case 'T61String':
+ case 'UniversalString':
+ case 'UTF8String':
+ case 'VideotexString':
+ case 'VisibleString':
+ if (this.tryToken('(')) {
+ if (this.tryToken('SIZE')) {
+ this.expectToken('(');
+ x.size = this.parseRange();
+ this.expectToken(')');
+ }
+ this.expectToken(')');
+ }
+ break;
+ default:
+ x.content = 'TODO:unknown';
+ }
+ } catch (e) {
+ console.log('[debug] parseBuiltinType content', e);
+ x.content = 'TODO:exception';
+ }
+ return x;
+ }
+ parseTaggedType() {
+ this.expectToken('[');
+ let tagClass = this.getRegEx('class', reTagClass) || 'CONTEXT'; //TODO: use module defaults
+ let t = this.parseNumber();
+ this.expectToken(']');
+ let plicit = this.getRegEx('explicit/implicit', reTagType);
+ if (plicit == '') plicit = currentMod.tagDefault;
+ let x = this.parseType();
+ return {
+ name: '[' + t + ']',
+ type: 'tag',
+ 'class': tagClass,
+ explicit: (plicit == 'EXPLICIT'),
+ content: [{ name: '', type: x }],
+ };
+ }
+ parseType() {
+ if (this.peekChar() == '[')
+ return this.parseTaggedType();
+ let p = this.pos;
+ try {
+ return this.parseBuiltinType();
+ } catch (e) {
+ // console.log('[debug] parseAssignment failed on parseType', e);
+ this.pos = p;
+ let x = {
+ name: this.parseIdentifier(),
+ type: 'defined',
+ };
+ // let from = searchImportedType(x.name);
+ // if (from)
+ // x.module = from;
+ return x;
+ //TODO "restricted string type"
+ }
+ }
+ parseValueBoolean() {
+ let p = this.pos;
+ let t = this.parseToken();
+ if (t == 'TRUE')
+ return true;
+ if (t == 'FALSE')
+ return false;
this.pos = p;
+ this.exception("Found '" + t + "', was expecting a boolean");
}
- this.exception('Unknown value type.');
-};
-/*DefStream.prototype.parseValue = function (type) {
- console.log('[debug] parseValue type:', type);
- if (type.type == 'defined') {
- if (!(type.name in types))
- this.exception("Missing type: " + type.name);
- type = types[type.name];
+ parseValueOID() {
+ this.expectToken('{');
+ let v = '';
+ while (!this.tryToken('}')) {
+ let p = this.pos;
+ let val;
+ if (this.isDigit())
+ val = this.parseNumber();
+ else {
+ this.pos = p;
+ let id = this.parseIdentifier();
+ if (this.tryToken('(')) {
+ val = this.parseNumber();
+ this.expectToken(')');
+ } else {
+ if (id in currentMod.values) // defined in local module
+ val = currentMod.values[id].value;
+ else
+ val = searchImportedValue(id);
+ }
+ }
+ if (v.length) v += '.';
+ v += val;
+ }
+ return v;
}
- switch (type.name) {
- case 'BOOLEAN':
- return this.parseValueBoolean();
- case 'OBJECT IDENTIFIER':
+ parseValue() {
+ let c = this.peekChar();
+ if (c == '{')
return this.parseValueOID();
- default:
- console.log('[debug] parseValue unknown:', type);
- return 'TODO:value';
+ if (c >= '0' && c <= '9')
+ return +this.parseNumber();
+ if (c == '-')
+ return -this.parseNumber();
+ let p = this.pos;
+ try {
+ switch (this.parseToken()) {
+ case 'TRUE':
+ return true;
+ case 'FALSE':
+ return false;
+ case 'NULL':
+ return null;
+ }
+ } catch (e) {
+ this.pos = p;
+ }
+ p = this.pos;
+ try {
+ return this.parseIdentifier();
+ } catch (e) {
+ this.pos = p;
+ }
+ this.exception('Unknown value type.');
}
-};*/
-Parser.prototype.parseElementType = function () {
- let x = Object.assign({ id: this.parseIdentifier() }, this.parseType());
- // console.log('[debug] parseElementType 1:', x);
- if (this.tryToken('OPTIONAL'))
- x.optional = true;
- if (this.tryToken('DEFAULT'))
- x.default = this.parseValue(x.type);
- // console.log('[debug] parseElementType 2:', x);
- return x;
-};
-Parser.prototype.parseElementTypeList = function () {
- let v = [];
- this.expectToken('{');
- do {
- v.push(this.parseElementType());
- } while (this.tryToken(','));
- this.expectToken('}');
- return v;
-};
-Parser.prototype.parseAssignment = function () {
- let name = this.parseIdentifier();
- if (this.tryToken('::=')) { // type assignment
- // console.log('type name', name);
- let type = this.parseType();
- currentMod.types[name] = { name, type };
- return currentMod.types[name];
- } else { // value assignment
- // console.log('value name', name);
- let type = this.parseType();
- // console.log('[debug] parseAssignment type:', type);
- this.expectToken('::=');
- let value = this.parseValue(type);
- currentMod.values[name] = { name, type, value };
- return currentMod.values[name];
+ /*DefStream.prototype.parseValue = function (type) {
+ console.log('[debug] parseValue type:', type);
+ if (type.type == 'defined') {
+ if (!(type.name in types))
+ this.exception("Missing type: " + type.name);
+ type = types[type.name];
+ }
+ switch (type.name) {
+ case 'BOOLEAN':
+ return this.parseValueBoolean();
+ case 'OBJECT IDENTIFIER':
+ return this.parseValueOID();
+ default:
+ console.log('[debug] parseValue unknown:', type);
+ return 'TODO:value';
+ }
+ }*/
+ parseElementType() {
+ let x = Object.assign({ id: this.parseIdentifier() }, this.parseType());
+ // console.log('[debug] parseElementType 1:', x);
+ if (this.tryToken('OPTIONAL'))
+ x.optional = true;
+ if (this.tryToken('DEFAULT'))
+ x.default = this.parseValue(x.type);
+ // console.log('[debug] parseElementType 2:', x);
+ return x;
}
-};
-Parser.prototype.parseModuleIdentifier = function () {
- return {
- name: this.parseIdentifier(),
- oid: this.parseValueOID(),
- };
-};
-Parser.prototype.parseSymbolsImported = function () {
- let imports = {};
- do {
- let l = [];
+ parseElementTypeList() {
+ let v = [];
+ this.expectToken('{');
do {
- l.push(this.parseIdentifier());
+ v.push(this.parseElementType());
} while (this.tryToken(','));
- this.expectToken('FROM');
+ this.expectToken('}');
+ return v;
+ }
+ parseAssignment() {
+ let name = this.parseIdentifier();
+ if (this.tryToken('::=')) { // type assignment
+ // console.log('type name', name);
+ let type = this.parseType();
+ currentMod.types[name] = { name, type };
+ return currentMod.types[name];
+ } else { // value assignment
+ // console.log('value name', name);
+ let type = this.parseType();
+ // console.log('[debug] parseAssignment type:', type);
+ this.expectToken('::=');
+ let value = this.parseValue(type);
+ currentMod.values[name] = { name, type, value };
+ return currentMod.values[name];
+ }
+ }
+ parseModuleIdentifier() {
+ return {
+ name: this.parseIdentifier(),
+ oid: this.parseValueOID(),
+ };
+ }
+ parseSymbolsImported() {
+ let imports = {};
+ do {
+ let l = [];
+ do {
+ l.push(this.parseIdentifier());
+ } while (this.tryToken(','));
+ this.expectToken('FROM');
+ let mod = this.parseModuleIdentifier();
+ mod.types = l;
+ imports[mod.oid] = mod;
+ } while (this.peekChar() != ';');
+ return imports;
+ }
+ parseModuleDefinition(file) {
let mod = this.parseModuleIdentifier();
- mod.types = l;
- imports[mod.oid] = mod;
- } while (this.peekChar() != ';');
- return imports;
-};
-const reTagDefault = /(AUTOMATIC|IMPLICIT|EXPLICIT) TAGS|/y;
-Parser.prototype.parseModuleDefinition = function (file) {
- let mod = this.parseModuleIdentifier();
- currentMod = mod; // for deeply nested parsers
- mod.source = file;
- this.expectToken('DEFINITIONS');
- mod.tagDefault = this.getRegEx('tag default', reTagDefault).split(' ')[0];
- this.expectToken('::=');
- this.expectToken('BEGIN');
- //TODO this.tryToken('EXPORTS')
- if (this.tryToken('IMPORTS')) {
- mod.imports = this.parseSymbolsImported();
- this.expectToken(';');
+ currentMod = mod; // for deeply nested parsers
+ mod.source = file;
+ this.expectToken('DEFINITIONS');
+ mod.tagDefault = this.getRegEx('tag default', reTagDefault).split(' ')[0];
+ this.expectToken('::=');
+ this.expectToken('BEGIN');
+ //TODO this.tryToken('EXPORTS')
+ if (this.tryToken('IMPORTS')) {
+ mod.imports = this.parseSymbolsImported();
+ this.expectToken(';');
+ }
+ mod.values = {};
+ mod.types = {};
+ while (!this.tryToken('END'))
+ this.parseAssignment();
+ return mod;
}
- mod.values = {};
- mod.types = {};
- while (!this.tryToken('END'))
- this.parseAssignment();
- return mod;
-};
+}
let s = fs.readFileSync(process.argv[2], 'utf8');
let num = /^Request for Comments: ([0-9]+)/m.exec(s)[1];
@@ -453,10 +457,10 @@ for (let p of patches[0])
if (num in patches)
for (let p of patches[num])
s = s.replace(p[0], p[1]);
-// fs.writeFileSync('rfc3161_patched.txt', s, 'utf8');
+fs.writeFileSync(process.argv[2].replace(/[.]txt$/, '_patched.txt'), s, 'utf8');
// console.log(s);
asn1 = JSON.parse(fs.readFileSync(process.argv[3], 'utf8'));
-const reModuleDefinition = /\s[A-Z](?:[-]?[a-zA-Z0-9])*\s*\{[^}]+\}\s*DEFINITIONS/gm;
+const reModuleDefinition = /\s[A-Z](?:[-]?[a-zA-Z0-9])*\s*\{[^}]+\}\s*(^--.*|\s*)*DEFINITIONS/gm;
let m;
while ((m = reModuleDefinition.exec(s))) {
new Parser(s, m.index).parseModuleDefinition(process.argv[2]);
--
2.51.2
From 4ddc3cf58df25107aa608a8f139902c00836504c Mon Sep 17 00:00:00 2001
From: Lapo Luchini
Date: Tue, 14 Mar 2023 20:42:33 +0000
Subject: [PATCH 05/14] Add PKCS#8 support.
---
defs.js | 3 ++-
parseRFC.js | 9 ++++++++-
updateRFC.sh | 2 +-
3 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/defs.js b/defs.js
index 65ab398..3c3b1cf 100644
--- a/defs.js
+++ b/defs.js
@@ -109,8 +109,9 @@ class Defs {
Defs.RFC = rfc;
Defs.commonTypes = [
- ['X.509 certificate', '1.3.6.1.5.5.7.0.18', 'Certificate' ],
+ [ 'X.509 certificate', '1.3.6.1.5.5.7.0.18', 'Certificate' ],
[ 'CMS / PKCS#7 envelope', '1.2.840.113549.1.9.16.0.14', 'ContentInfo' ],
+ [ 'PKCS#8 encrypted private key', '1.2.840.113549.1.8.1.1', 'EncryptedPrivateKeyInfo' ],
].map(arr => ({ description: arr[0], ...Defs.moduleAndType(rfc[arr[1]], arr[2]) }));
return Defs;
diff --git a/parseRFC.js b/parseRFC.js
index 0225cb0..686458c 100755
--- a/parseRFC.js
+++ b/parseRFC.js
@@ -18,6 +18,13 @@ const
[ /^( +)--.*\n(?:\1 .*\n)+/mg, '' ],
[ /addInfoNotAvailable \(17\)/g, '$&,' ],
],
+ 5208: [ // currently unsupported
+ [ 'FROM InformationFramework informationFramework', 'FROM InformationFramework {joint-iso-itu-t(2) ds(5) module(1) usefulDefinitions(0) 3}' ],
+ [ ' {{PrivateKeyAlgorithms}}', '' ],
+ [ 'Version ::= INTEGER {v1(0)} (v1,...)', 'Version ::= INTEGER {v1(0)}' ],
+ [ ' {{KeyEncryptionAlgorithms}}', '' ],
+ [ /\.\.\. -- For local profiles/g, '' ],
+ ],
5280: [ // currently unsupported
[ 'videotex (8) } (0..ub-integer-options)', 'videotex (8) }' ],
[ /OBJECT IDENTIFIER \( id-qt-cps \| id-qt-unotice \)/g, 'OBJECT IDENTIFIER' ],
@@ -460,7 +467,7 @@ if (num in patches)
fs.writeFileSync(process.argv[2].replace(/[.]txt$/, '_patched.txt'), s, 'utf8');
// console.log(s);
asn1 = JSON.parse(fs.readFileSync(process.argv[3], 'utf8'));
-const reModuleDefinition = /\s[A-Z](?:[-]?[a-zA-Z0-9])*\s*\{[^}]+\}\s*(^--.*|\s*)*DEFINITIONS/gm;
+const reModuleDefinition = /\s[A-Z](?:[-]?[a-zA-Z0-9])*\s*\{[^}]+\}\s*(^--.*|\n)*DEFINITIONS/gm;
let m;
while ((m = reModuleDefinition.exec(s))) {
new Parser(s, m.index).parseModuleDefinition(process.argv[2]);
diff --git a/updateRFC.sh b/updateRFC.sh
index 30305f8..cb9f812 100755
--- a/updateRFC.sh
+++ b/updateRFC.sh
@@ -1,5 +1,5 @@
#/bin/sh
-RFCs="5280 3369 3161"
+RFCs="5280 5208 3369 3161"
downloadRFC() {
URL="https://www.ietf.org/rfc/rfc$1.txt"
if [ -x /usr/bin/fetch ]; then
--
2.51.2
From e495fbc88c4f651939667a24baccd032dd247128 Mon Sep 17 00:00:00 2001
From: Lapo Luchini
Date: Fri, 24 Mar 2023 18:01:05 +0000
Subject: [PATCH 06/14] Improve support for type recognition. Add `[?]` marker
for mismatched types.
---
defs.js | 22 +++++++++++++++-------
dom.js | 4 ++++
dumpASN1.js | 4 +++-
3 files changed, 22 insertions(+), 8 deletions(-)
diff --git a/defs.js b/defs.js
index 3c3b1cf..2a85ef1 100644
--- a/defs.js
+++ b/defs.js
@@ -22,7 +22,6 @@
const rfc = require('./rfcdef');
function translate(def, tn) {
- const id = def?.id;
if (def?.type == 'tag' && !def.explicit)
// def.type = def.content[0].type;
def = def.content[0].type;
@@ -41,6 +40,7 @@ function translate(def, tn) {
}
}
}
+ const id = def?.id;
if (id)
def = Object.assign({}, def, { id });
return def ?? { type: {} };
@@ -68,12 +68,16 @@ class Defs {
static match(value, def, stats = { total: 0, recognized: 0, defs: {} }) {
value.def = {};
- let tn = value.typeName();
+ let tn = value.typeName().replaceAll('_', ' ');
def = translate(def, tn);
++stats.total;
if (def?.type) {
+ // if (def.id || def.name) ++stats.recognized;
+ if (tn == def.type.name || tn == def.name || def.name == 'ANY')
+ ++stats.recognized;
+ else if (def.name)
+ def = Object.assign({ mismatch: 1 }, def);
value.def = def;
- if (def.id || def.name) ++stats.recognized;
}
if (value.sub !== null) {
if (def?.type?.type)
@@ -85,16 +89,19 @@ class Defs {
if (def.typeOf)
type = def.content[0];
else {
- let tn = subval.typeName(); //.replaceAll('_', ' ');
+ let tn = subval.typeName().replaceAll('_', ' ');
do {
type = def.content[j++];
// type = translate(type, tn);
if (type?.type?.type)
type = type.type;
} while (type && ('optional' in type || 'default' in type) && type.name != 'ANY' && type.name != tn);
- if (type?.type == 'defined')
- stats.defs[type.id] = subval.content().split(/\n/);
- else if (type?.definedBy && stats.defs?.[type.definedBy]?.[1]) // hope current OIDs contain the type name (will need to parse from RFC itself)
+ if (type?.type == 'builtin' || type?.type == 'defined') {
+ let v = subval.content();
+ if (typeof v == 'string')
+ v = v.split(/\n/);
+ stats.defs[type.id] = v;
+ } else if (type?.definedBy && stats.defs?.[type.definedBy]?.[1]) // hope current OIDs contain the type name (will need to parse from RFC itself)
type = Defs.searchType(firstUpper(stats.defs[type.definedBy][1]));
}
}
@@ -112,6 +119,7 @@ Defs.commonTypes = [
[ 'X.509 certificate', '1.3.6.1.5.5.7.0.18', 'Certificate' ],
[ 'CMS / PKCS#7 envelope', '1.2.840.113549.1.9.16.0.14', 'ContentInfo' ],
[ 'PKCS#8 encrypted private key', '1.2.840.113549.1.8.1.1', 'EncryptedPrivateKeyInfo' ],
+ [ 'PKCS#8 private key', '1.2.840.113549.1.8.1.1', 'PrivateKeyInfo' ],
].map(arr => ({ description: arr[0], ...Defs.moduleAndType(rfc[arr[1]], arr[2]) }));
return Defs;
diff --git a/dom.js b/dom.js
index aef4c74..f065571 100644
--- a/dom.js
+++ b/dom.js
@@ -74,6 +74,10 @@ class ASN1DOM extends ASN1 {
head.appendChild(DOM.tag('span', 'name type', this.def.name));
head.appendChild(DOM.space());
}
+ if (this.def.mismatch) {
+ head.appendChild(DOM.tag('span', 'name type', '[?]'));
+ head.appendChild(DOM.space());
+ }
}
head.appendChild(DOM.text(typeName));
let content = this.content(contentLength);
diff --git a/dumpASN1.js b/dumpASN1.js
index c67531d..d43872f 100755
--- a/dumpASN1.js
+++ b/dumpASN1.js
@@ -17,6 +17,7 @@ function print(value, indent) {
if (def?.type) {
if (def.id) name += colBlue + def.id + colReset;
if (typeof def.type == 'object' && def.name) name = (name ? name + ' ' : '') + def.name;
+ if (def.mismatch) name = (name ? name + ' ' : '') + '[?]';
if (name) name += ' ';
}
let s = indent + name + colYellow + value.typeName() + colReset + ' @' + value.stream.pos;
@@ -57,7 +58,8 @@ const t1 = performance.now();
console.log('Parsed in ' + (t1 - t0).toFixed(2) + ' ms; possible types:');
for (const t of types)
console.log((t.match * 100).toFixed(2).padStart(6) + '% ' + t.type.description);
-Defs.match(result, types[0].match >= 0.1 ? types[0].type : null);
+const stats = Defs.match(result, types[0].match >= 0.1 ? types[0].type : null);
+// console.log('Stats:', stats);
console.log('Parsed as:', result.def);
// const type = searchType(process.argv[2]);
// const stats = applyDef(result, type);
--
2.51.2
From 06d8aeb468c405d5d7a1bee99c75815effdfac84 Mon Sep 17 00:00:00 2001
From: Lapo Luchini
Date: Fri, 24 Mar 2023 18:15:14 +0000
Subject: [PATCH 07/14] Recover lost matching in some cases..
---
defs.js | 12 ++++++++----
dumpASN1.js | 2 +-
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/defs.js b/defs.js
index 2a85ef1..2e485c3 100644
--- a/defs.js
+++ b/defs.js
@@ -21,10 +21,15 @@
const rfc = require('./rfcdef');
-function translate(def, tn) {
+function translate(def, tn, stats) {
if (def?.type == 'tag' && !def.explicit)
// def.type = def.content[0].type;
def = def.content[0].type;
+ if (def?.definedBy)
+ try {
+ // hope current OIDs contain the type name (will need to parse from RFC itself)
+ def = Defs.searchType(firstUpper(stats.defs[def.definedBy][1]));
+ } catch (e) {}
while (def?.type == 'defined' || def?.type?.type == 'defined') {
const name = def?.type?.type ? def.type.name : def.name;
def = Object.assign({}, def);
@@ -69,7 +74,7 @@ class Defs {
static match(value, def, stats = { total: 0, recognized: 0, defs: {} }) {
value.def = {};
let tn = value.typeName().replaceAll('_', ' ');
- def = translate(def, tn);
+ def = translate(def, tn, stats);
++stats.total;
if (def?.type) {
// if (def.id || def.name) ++stats.recognized;
@@ -101,8 +106,7 @@ class Defs {
if (typeof v == 'string')
v = v.split(/\n/);
stats.defs[type.id] = v;
- } else if (type?.definedBy && stats.defs?.[type.definedBy]?.[1]) // hope current OIDs contain the type name (will need to parse from RFC itself)
- type = Defs.searchType(firstUpper(stats.defs[type.definedBy][1]));
+ }
}
}
Defs.match(subval, type, stats);
diff --git a/dumpASN1.js b/dumpASN1.js
index d43872f..c937469 100755
--- a/dumpASN1.js
+++ b/dumpASN1.js
@@ -58,7 +58,7 @@ const t1 = performance.now();
console.log('Parsed in ' + (t1 - t0).toFixed(2) + ' ms; possible types:');
for (const t of types)
console.log((t.match * 100).toFixed(2).padStart(6) + '% ' + t.type.description);
-const stats = Defs.match(result, types[0].match >= 0.1 ? types[0].type : null);
+const stats = Defs.match(result, types[0].type);
// console.log('Stats:', stats);
console.log('Parsed as:', result.def);
// const type = searchType(process.argv[2]);
--
2.51.2
From ce3dc3d2bde4145203489cb67d1bebc6ca334999 Mon Sep 17 00:00:00 2001
From: Lapo Luchini
Date: Fri, 4 Aug 2023 06:49:14 +0000
Subject: [PATCH 08/14] Check that `type` is an object before accessing to
members. Closes #69 on GitHub.
---
defs.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/defs.js b/defs.js
index 3c3b1cf..96bda2c 100644
--- a/defs.js
+++ b/defs.js
@@ -91,7 +91,7 @@ class Defs {
// type = translate(type, tn);
if (type?.type?.type)
type = type.type;
- } while (type && ('optional' in type || 'default' in type) && type.name != 'ANY' && type.name != tn);
+ } while (type && typeof type == 'object' && ('optional' in type || 'default' in type) && type.name != 'ANY' && type.name != tn);
if (type?.type == 'defined')
stats.defs[type.id] = subval.content().split(/\n/);
else if (type?.definedBy && stats.defs?.[type.definedBy]?.[1]) // hope current OIDs contain the type name (will need to parse from RFC itself)
--
2.51.2
From 9948fb74b9c5baafd379e367ac9bbb3b3276f4d3 Mon Sep 17 00:00:00 2001
From: Lapo Luchini
Date: Fri, 4 Aug 2023 07:02:56 +0000
Subject: [PATCH 09/14] Fix GitHub action.
---
.github/workflows/node.js.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml
index dc44dfe..0bb55ff 100644
--- a/.github/workflows/node.js.yml
+++ b/.github/workflows/node.js.yml
@@ -22,4 +22,4 @@ jobs:
node-version: ${{ matrix.node-version }}
- run: npm test all
- run: npm run lint
- if: ${{ matrix.node-version == latest }}
+ if: matrix.node-version == 'latest'
--
2.51.2
From 53a2cf9c7d0532a692e507e8d8903bcbe65d788e Mon Sep 17 00:00:00 2001
From: Lapo Luchini
Date: Tue, 29 Aug 2023 20:49:37 +0000
Subject: [PATCH 10/14] Sync with latest changes from Peter Gutmann's website.
Last-Modified: Thu, 29 Jun 2023 10:25:59 GMT
---
oids.js | 52 ++++++++++++++++++++++++++++------------------------
1 file changed, 28 insertions(+), 24 deletions(-)
diff --git a/oids.js b/oids.js
index 2310c5c..06fb616 100644
--- a/oids.js
+++ b/oids.js
@@ -1352,10 +1352,10 @@ return {
"1.3.6.1.4.1.8301.3.5.1": { "d": "validityModelChain", "c": "TU Darmstadt ValidityModel" },
"1.3.6.1.4.1.8301.3.5.2": { "d": "validityModelShell", "c": "ValidityModel" },
"1.3.6.1.4.1.8231.1": { "d": "rolUnicoNacional", "c": "Chilean Government national unique roll number" },
-"1.3.6.1.4.1.11129.2.4.2": { "d": "googleEV", "c": "Google" },
-"1.3.6.1.4.1.11129.2.4.3": { "d": "googleCertificateTransparency", "c": "Google" },
-"1.3.6.1.4.1.11129.2.4.4": { "d": "googlePrecertificate", "c": "Google" },
-"1.3.6.1.4.1.11129.2.4.5": { "d": "googleOcspStapling", "c": "Google" },
+"1.3.6.1.4.1.11129.2.4.2": { "d": "googleSignedCertificateTimestamp", "c": "Google Certificate Transparency" },
+"1.3.6.1.4.1.11129.2.4.3": { "d": "googlePrecertificatePoison", "c": "Google Certificate Transparency" },
+"1.3.6.1.4.1.11129.2.4.4": { "d": "googlePrecertificateCA", "c": "Google Certificate Transparency" },
+"1.3.6.1.4.1.11129.2.4.5": { "d": "googleOcspSignedCertificateTimestamp", "c": "Google Certificate Transparency" },
"1.3.6.1.4.1.11591": { "d": "gnu", "c": "GNU Project (see https://www.gnupg.org/oids.html)" },
"1.3.6.1.4.1.11591.1": { "d": "gnuRadius", "c": "GNU Radius" },
"1.3.6.1.4.1.11591.2.2.1": { "d": "gpgX509StandaloneCert", "c": "Cert is intentionally self-signed." },
@@ -2657,26 +2657,30 @@ return {
"2.23.43.1.4.6": { "d": "wTLS-ECC-curve6", "c": "WAP WTLS" },
"2.23.43.1.4.8": { "d": "wTLS-ECC-curve8", "c": "WAP WTLS" },
"2.23.43.1.4.9": { "d": "wTLS-ECC-curve9", "c": "WAP WTLS" },
-"2.23.133": { "d": "tCPA", "c": "TCPA" },
-"2.23.133.1": { "d": "tcpaSpecVersion", "c": "TCPA" },
-"2.23.133.2": { "d": "tcpaAttribute", "c": "TCPA" },
-"2.23.133.2.1": { "d": "tcpaTpmManufacturer", "c": "TCPA Attribute" },
-"2.23.133.2.2": { "d": "tcpaTpmModel", "c": "TCPA Attribute" },
-"2.23.133.2.3": { "d": "tcpaTpmVersion", "c": "TCPA Attribute" },
-"2.23.133.2.4": { "d": "tcpaPlatformManufacturer", "c": "TCPA Attribute" },
-"2.23.133.2.5": { "d": "tcpaPlatformModel", "c": "TCPA Attribute" },
-"2.23.133.2.6": { "d": "tcpaPlatformVersion", "c": "TCPA Attribute" },
-"2.23.133.2.7": { "d": "tcpaComponentManufacturer", "c": "TCPA Attribute" },
-"2.23.133.2.8": { "d": "tcpaComponentModel", "c": "TCPA Attribute" },
-"2.23.133.2.9": { "d": "tcpaComponentVersion", "c": "TCPA Attribute" },
-"2.23.133.2.10": { "d": "tcpaSecurityQualities", "c": "TCPA Attribute" },
-"2.23.133.2.11": { "d": "tcpaTpmProtectionProfile", "c": "TCPA Attribute" },
-"2.23.133.2.12": { "d": "tcpaTpmSecurityTarget", "c": "TCPA Attribute" },
-"2.23.133.2.13": { "d": "tcpaFoundationProtectionProfile", "c": "TCPA Attribute" },
-"2.23.133.2.14": { "d": "tcpaFoundationSecurityTarget", "c": "TCPA Attribute" },
-"2.23.133.2.15": { "d": "tcpaTpmIdLabel", "c": "TCPA Attribute" },
-"2.23.133.3": { "d": "tcpaProtocol", "c": "TCPA" },
-"2.23.133.3.1": { "d": "tcpaPrttTpmIdProtocol", "c": "TCPA Protocol" },
+"2.23.133": { "d": "tCPA", "c": "TCPA/TCG" },
+"2.23.133.1": { "d": "tcgSpecVersion", "c": "TCPA/TCG" },
+"2.23.133.2": { "d": "tcgAttribute", "c": "TCPA/TCG" },
+"2.23.133.2.1": { "d": "tcgTpmManufacturer", "c": "TCPA/TCG Attribute" },
+"2.23.133.2.2": { "d": "tcgTpmModel", "c": "TCPA/TCG Attribute" },
+"2.23.133.2.3": { "d": "tcgTpmVersion", "c": "TCPA/TCG Attribute" },
+"2.23.133.2.4": { "d": "tcgPlatformManufacturer", "c": "TCPA/TCG Attribute" },
+"2.23.133.2.5": { "d": "tcgPlatformModel", "c": "TCPA/TCG Attribute" },
+"2.23.133.2.6": { "d": "tcgPlatformVersion", "c": "TCPA/TCG Attribute" },
+"2.23.133.2.7": { "d": "tcgComponentManufacturer", "c": "TCPA/TCG Attribute" },
+"2.23.133.2.8": { "d": "tcgComponentModel", "c": "TCPA/TCG Attribute" },
+"2.23.133.2.9": { "d": "tcgComponentVersion", "c": "TCPA/TCG Attribute" },
+"2.23.133.2.10": { "d": "tcgSecurityQualities", "c": "TCPA/TCG Attribute" },
+"2.23.133.2.11": { "d": "tcgTpmProtectionProfile", "c": "TCPA/TCG Attribute" },
+"2.23.133.2.12": { "d": "tcgTpmSecurityTarget", "c": "TCPA/TCG Attribute" },
+"2.23.133.2.13": { "d": "tcgFoundationProtectionProfile", "c": "TCPA/TCG Attribute" },
+"2.23.133.2.14": { "d": "tcgFoundationSecurityTarget", "c": "TCPA/TCG Attribute" },
+"2.23.133.2.15": { "d": "tcgTpmIdLabel", "c": "TCPA/TCG Attribute" },
+"2.23.133.2.16": { "d": "tcgTpmSpecification", "c": "TCPA/TCG Attribute" },
+"2.23.133.2.18": { "d": "tcgTpmSecurityAssertions", "c": "TCPA/TCG Attribute" },
+"2.23.133.3": { "d": "tcgProtocol", "c": "TCPA/TCG" },
+"2.23.133.3.1": { "d": "tcgPrttTpmIdProtocol", "c": "TCPA/TCG Protocol" },
+"2.23.133.8.1": { "d": "tcgEKCertificate", "c": "TCPA/TCG Key Usage" },
+"2.23.133.10.1.1.1": { "d": "tcgObject", "c": "TCPA/TCG Object" },
"2.23.134.1.4.2.1": { "d": "postSignumRootQCA", "c": "PostSignum CA" },
"2.23.134.1.2.2.3": { "d": "postSignumPublicCA", "c": "PostSignum CA" },
"2.23.134.1.2.1.8.210": { "d": "postSignumCommercialServerPolicy", "c": "PostSignum CA" },
--
2.51.2
From 3a2ce63610cf4cf40ce93c55db6fc105af64502b Mon Sep 17 00:00:00 2001
From: Lapo Luchini
Date: Tue, 29 Aug 2023 20:55:10 +0000
Subject: [PATCH 11/14] Do not fail on missing types. (un-break example
`letsencrypt-x3.cer`)
---
defs.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/defs.js b/defs.js
index 96bda2c..07f8500 100644
--- a/defs.js
+++ b/defs.js
@@ -95,7 +95,9 @@ class Defs {
if (type?.type == 'defined')
stats.defs[type.id] = subval.content().split(/\n/);
else if (type?.definedBy && stats.defs?.[type.definedBy]?.[1]) // hope current OIDs contain the type name (will need to parse from RFC itself)
- type = Defs.searchType(firstUpper(stats.defs[type.definedBy][1]));
+ try {
+ type = Defs.searchType(firstUpper(stats.defs[type.definedBy][1]));
+ } catch (e) {}
}
}
Defs.match(subval, type, stats);
--
2.51.2
From c9324b2c17c29d7018e77bb867f02bcddb28becf Mon Sep 17 00:00:00 2001
From: Lapo Luchini
Date: Tue, 29 Aug 2023 20:55:57 +0000
Subject: [PATCH 12/14] Add type support for PKCS#10 certification requests.
---
README.md | 2 +-
defs.js | 1 +
package.json | 2 +-
parseRFC.js | 13 +++++++++++++
updateRFC.sh | 2 +-
5 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 34a6c44..de091f8 100644
--- a/README.md
+++ b/README.md
@@ -52,7 +52,7 @@ require([
ISC license
-----------
-ASN.1 JavaScript decoder Copyright (c) 2008-2022 Lapo Luchini
+ASN.1 JavaScript decoder Copyright (c) 2008-2023 Lapo Luchini
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
diff --git a/defs.js b/defs.js
index 07f8500..bc5d5d6 100644
--- a/defs.js
+++ b/defs.js
@@ -114,6 +114,7 @@ Defs.commonTypes = [
[ 'X.509 certificate', '1.3.6.1.5.5.7.0.18', 'Certificate' ],
[ 'CMS / PKCS#7 envelope', '1.2.840.113549.1.9.16.0.14', 'ContentInfo' ],
[ 'PKCS#8 encrypted private key', '1.2.840.113549.1.8.1.1', 'EncryptedPrivateKeyInfo' ],
+ [ 'PKCS#10 certification request', '1.2.840.113549.1.10.1.1', 'CertificationRequest' ],
].map(arr => ({ description: arr[0], ...Defs.moduleAndType(rfc[arr[1]], arr[2]) }));
return Defs;
diff --git a/package.json b/package.json
index 1e8db15..4a0bb0a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@lapo/asn1js",
- "version": "1.2.4",
+ "version": "1.3.0",
"description": "Generic ASN.1 parser/decoder that can decode any valid ASN.1 DER or BER structures.",
"main": "asn1.js",
"repository": {
diff --git a/parseRFC.js b/parseRFC.js
index 686458c..e39e392 100755
--- a/parseRFC.js
+++ b/parseRFC.js
@@ -13,6 +13,19 @@ const
[ /SIGNED \{ (SEQUENCE \{[^}]+\})\s*\}/g, 'SEQUENCE { toBeSigned $1, algorithm AlgorithmIdentifier, signature BIT STRING }' ],
[ /EXTENSION\.&[^,]+/g, 'OBJECT IDENTIFIER'],
],
+ 2986: [ // currently unsupported
+ [ /FROM (InformationFramework|AuthenticationFramework) [a-zA-Z]+/g, 'FROM $1 {joint-iso-itu-t(2) ds(5) module(1) usefulDefinitions(0) 3}' ],
+ [ /[(]v1,[^)]+[)]/g, '' ],
+ [ /[{][{][^}]+[}][}]/g, '' ],
+ [ 'SubjectPublicKeyInfo {ALGORITHM: IOSet}', 'SubjectPublicKeyInfo' ],
+ [ /PKInfoAlgorithms ALGORITHM ::=[^}]+[}]/g, '' ],
+ [ /(Attributes?) [{] ATTRIBUTE:IOSet [}]/g, '$1' ],
+ [ /CRIAttributes +ATTRIBUTE +::=[^}]+[}]/g, '' ],
+ [ /[A-Z]+[.]&id[(][{]IOSet[}][)]/g, 'OBJECT IDENTIFIER' ],
+ [ /[A-Z]+[.]&Type[(][{]IOSet[}][{]@[a-z]+[}][)]/g, 'ANY' ],
+ [ /(AlgorithmIdentifier) [{]ALGORITHM:IOSet [}]/g, '$1' ],
+ [ /SignatureAlgorithms ALGORITHM ::=[^}]+[}]/g, '' ],
+ ],
3161: [ // actual syntax errors
[ /--.*}/g, '}' ],
[ /^( +)--.*\n(?:\1 .*\n)+/mg, '' ],
diff --git a/updateRFC.sh b/updateRFC.sh
index cb9f812..bf3f8f7 100755
--- a/updateRFC.sh
+++ b/updateRFC.sh
@@ -1,5 +1,5 @@
#/bin/sh
-RFCs="5280 5208 3369 3161"
+RFCs="5280 5208 3369 3161 2986"
downloadRFC() {
URL="https://www.ietf.org/rfc/rfc$1.txt"
if [ -x /usr/bin/fetch ]; then
--
2.51.2
From b7d379bb54800bfa42cc121b8cdff2ab94acacda Mon Sep 17 00:00:00 2001
From: Lapo Luchini
Date: Tue, 29 Aug 2023 21:34:46 +0000
Subject: [PATCH 13/14] Add PKCS#10 example.
---
examples/pkcs10.pem | 9 +++++++++
index.html | 1 +
2 files changed, 10 insertions(+)
create mode 100644 examples/pkcs10.pem
diff --git a/examples/pkcs10.pem b/examples/pkcs10.pem
new file mode 100644
index 0000000..c51b61b
--- /dev/null
+++ b/examples/pkcs10.pem
@@ -0,0 +1,9 @@
+PKCS#10 certification request based on Daniel J. Bernstein’s Curve25519.
+$ openssl req -nodes -newkey ed25519 -keyout /dev/null -out pkcs10.pem -days 36500 -subj '/CN=test' -addext 'subjectAltName=otherName:msUPN;UTF8:address@domain.test'
+-----BEGIN CERTIFICATE REQUEST-----
+MIHQMIGDAgEAMA8xDTALBgNVBAMMBHRlc3QwKjAFBgMrZXADIQD7Fua9ZF+wPXVd
+DCBwQr+Aqny6OFvs25wZ/P4LyVsYmKBBMD8GCSqGSIb3DQEJDjEyMDAwLgYDVR0R
+BCcwJaAjBgorBgEEAYI3FAIDoBUME2FkZHJlc3NAZG9tYWluLnRlc3QwBQYDK2Vw
+A0EAUp5FenHF1rZzRGU+7wiF+/D1bfyDRF0dzWz2sl44nltu8iLjHO3aIfOTYWpq
+ZlaDg1Bq3L7Fcb7If4yZAsE5Cw==
+-----END CERTIFICATE REQUEST-----
diff --git a/index.html b/index.html
index 524f19a..588a911 100644
--- a/index.html
+++ b/index.html
@@ -44,6 +44,7 @@
+
--
2.51.2
From e66a38d2f9d06179c9e1eeaeb52fc0606d44bff5 Mon Sep 17 00:00:00 2001
From: Lapo Luchini
Date: Wed, 30 Aug 2023 12:16:46 +0000
Subject: [PATCH 14/14] Fix lint error.
---
dumpASN1.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/dumpASN1.js b/dumpASN1.js
index c937469..dd02546 100755
--- a/dumpASN1.js
+++ b/dumpASN1.js
@@ -58,7 +58,8 @@ const t1 = performance.now();
console.log('Parsed in ' + (t1 - t0).toFixed(2) + ' ms; possible types:');
for (const t of types)
console.log((t.match * 100).toFixed(2).padStart(6) + '% ' + t.type.description);
-const stats = Defs.match(result, types[0].type);
+Defs.match(result, types[0].type);
+// const stats = Defs.match(result, types[0].type);
// console.log('Stats:', stats);
console.log('Parsed as:', result.def);
// const type = searchType(process.argv[2]);