Skip to content

Commit

Permalink
x509: finish JSON interface with extensions
Browse files Browse the repository at this point in the history
Complete the certificate fromJSON interface and demonstrate
with a test how to create a new key and self-signed cert. Part
of this commit includes refactoring classes like RDNSequence from
x509 up to ASN1, so the classes can be imported by the extensions
as well. There is an openssl test which can be made optional.
  • Loading branch information
pinheadmz committed Jun 9, 2020
1 parent 82d018f commit adfce1a
Show file tree
Hide file tree
Showing 4 changed files with 554 additions and 234 deletions.
231 changes: 228 additions & 3 deletions lib/encoding/asn1.js
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,7 @@ class Choice extends Node {
assert(node instanceof Node);
this.node = node;
this.from(...options);
this.types = types;
}

get type() {
Expand All @@ -607,6 +608,10 @@ class Choice extends Node {
throw new Error('Unimplemented.');
}

typeToClass(type) {
return typeToClass(type);
}

getSize(extra) {
return this.node.getSize(extra);
}
Expand Down Expand Up @@ -634,7 +639,7 @@ class Choice extends Node {
if (choices.indexOf(hdr.type) === -1)
throw new Error(`Could not satisfy choice for: ${hdr.type}.`);

const Node = typeToClass(hdr.type);
const Node = this.typeToClass(hdr.type);
const el = new Node();
el.flags = this.flags;

Expand Down Expand Up @@ -680,8 +685,8 @@ class Choice extends Node {
}

fromJSON(json) {
const type = types[json.type.toUpperCase()];
const Node = typeToClass(type);
const type = this.types[json.type.toUpperCase()];
const Node = this.typeToClass(type);
this.node = new Node();
this.node.fromJSON(json.node);
this.node.flags = this.flags;
Expand Down Expand Up @@ -2012,6 +2017,223 @@ class GenString extends Str {
}
}

/**
* RDNSequence
*/

// Name ::= CHOICE { -- only one possibility for now --
// rdnSequence RDNSequence }
//
// RDNSequence ::= SEQUENCE OF RelativeDistinguishedName

class RDNSequence extends Sequence {
constructor() {
super();
this.names = [];
}

getBodySize() {
let size = 0;

for (const rdn of this.names)
size += rdn.getSize();

return size;
}

writeBody(bw) {
for (const rdn of this.names)
rdn.write(bw);
return bw;
}

readBody(br) {
while (br.left()) {
const rdn = RDN.read(br);
this.names.push(rdn);
}

return this;
}

clean() {
return this.names.length === 0;
}

format() {
return {
type: this.constructor.name,
names: this.names
};
}

getJSON() {
const names = [];
for (const name of this.names)
names.push(name.getJSON());

return names;
}

fromJSON(json) {
assert(Array.isArray(json));
for (const name of json)
this.names.push(RDN.fromJSON(name));

return this;
}
}

/**
* RDN
*/

// RelativeDistinguishedName ::=
// SET SIZE (1..MAX) OF AttributeTypeAndValue
//

class RDN extends Set {
constructor(id, value) {
super();
this.attributes = [new Attribute(id, value)];
}

getBodySize() {
let size = 0;

assert(this.attributes.length >= 1);

for (const attr of this.attributes)
size += attr.getSize();

return size;
}

writeBody(bw) {
assert(this.attributes.length >= 1);

for (const attr of this.attributes)
attr.write(bw);

return bw;
}

readBody(br) {
this.attributes[0].read(br);

while (br.left()) {
const attr = Attribute.read(br);
this.attributes.push(attr);
}

return this;
}

clean() {
return this.attributes.length === 1 && this.attributes[0].clean();
}

format() {
return {
type: this.constructor.name,
attributes: this.attributes
};
}

getJSON() {
const attributes = [];
for (const attr of this.attributes)
attributes.push(attr.getJSON());

return attributes;
}

fromJSON(json) {
assert(Array.isArray(json));
this.attributes = [];

for (const attr of json)
this.attributes.push(Attribute.fromJSON(attr));

return this;
}

static fromJSON(json) {
return new this().fromJSON(json);
}
}

/**
* Attribute
*/

// AttributeTypeAndValue ::= SEQUENCE {
// type AttributeType,
// value AttributeValue }
//
// AttributeType ::= OBJECT IDENTIFIER
//
// AttributeValue ::= ANY -- DEFINED BY AttributeType

class Attribute extends Sequence {
constructor(id, value) {
super();

this.id = new OID(id);
this.value = new Any(value);
}

getBodySize() {
let size = 0;
size += this.id.getSize();
size += this.value.getSize();
return size;
}

writeBody(bw) {
this.id.write(bw);
this.value.write(bw);
return bw;
}

readBody(br) {
this.id.read(br);
this.value.read(br);
return this;
}

clean() {
return this.id.clean()
&& this.value.clean();
}

format() {
return {
type: this.constructor.name,
id: this.id,
value: this.value
};
}

getJSON() {
return {
id: this.id.getJSON(),
value: this.value.getJSON()
};
}

fromJSON(json) {
this.id.fromJSON(json.id);
this.value.fromJSON(json.value);

return this;
}

static fromJSON(json) {
return new this().fromJSON(json);
}
}

/**
* API
*/
Expand Down Expand Up @@ -2615,5 +2837,8 @@ exports.Time = Time;
exports.UTCTime = UTCTime;
exports.GenTime = GenTime;
exports.GenString = GenString;
exports.RDNSequence = RDNSequence;
exports.RDN = RDN;
exports.Attribute = Attribute;

exports.typeToClass = typeToClass;
Loading

0 comments on commit adfce1a

Please sign in to comment.