Skip to content

Commit

Permalink
feat: lazy serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
JonathanWilbur committed Aug 29, 2023
1 parent e5b5853 commit e39e47b
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 180 deletions.
166 changes: 10 additions & 156 deletions source/asn1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,22 @@ abstract class ASN1Element implements Byteable, Elementable, Named, Long {
}
this._tagNumber = value;
}
public value: Uint8Array = new Uint8Array(0);

abstract get value (): Uint8Array;
abstract set value (v: Uint8Array);
abstract construct (els: ASN1Element[]): void;
public abstract tagAndLengthBytes (): Uint8Array;
public abstract toBuffers (): Uint8Array[];

public toBytes (): Buffer {
return Buffer.concat(this.toBuffers());
}

get length (): number {
return this.value.length;
}

abstract fromBytes (bytes: Uint8Array): number;
abstract toBytes (): Uint8Array;

abstract set boolean (value: BOOLEAN);
abstract get boolean (): BOOLEAN;
Expand Down Expand Up @@ -298,160 +306,6 @@ abstract class ASN1Element implements Byteable, Elementable, Named, Long {
return 0;
}

// Shorter aliases to make for smaller libraries

set bool (value: BOOLEAN) {
this.boolean = value;
}

get bool (): BOOLEAN {
return this.boolean;
}

set int (value: INTEGER) {
this.integer = value;
}

get int (): INTEGER {
return this.integer;
}

set bits (value: BIT_STRING) {
this.bitString = value;
}

get bits (): BIT_STRING {
return this.bitString;
}

set octs (value: OCTET_STRING) {
this.octetString = value;
}

get octs (): OCTET_STRING {
return this.octetString;
}

set oid (value: OBJECT_IDENTIFIER) {
this.objectIdentifier = value;
}

get oid (): OBJECT_IDENTIFIER {
return this.objectIdentifier;
}

set odesc (value: ObjectDescriptor) {
this.objectDescriptor = value;
}

get odesc (): ObjectDescriptor {
return this.objectDescriptor;
}

set enum (value: ENUMERATED) {
this.enumerated = value;
}

get enum (): ENUMERATED {
return this.enumerated;
}

set utf8 (value: UTF8String) {
this.utf8String = value;
}

get utf8 (): UTF8String {
return this.utf8String;
}

set roid (value: RELATIVE_OID) {
this.relativeObjectIdentifier = value;
}

get roid (): RELATIVE_OID {
return this.relativeObjectIdentifier;
}

set seq (value: SEQUENCE<ASN1Element>) {
this.sequence = value;
}

get seq (): SEQUENCE<ASN1Element> {
return this.sequence;
}

set nums (value: NumericString) {
this.numericString = value;
}

get nums (): NumericString {
return this.numericString;
}

set prints (value: PrintableString) {
this.printableString = value;
}

get prints (): PrintableString {
return this.printableString;
}

set ttex (value: TeletexString) {
this.teletexString = value;
}

get ttex (): TeletexString {
return this.teletexString;
}

set vtex (value: VideotexString) {
this.videotexString = value;
}

get vtex (): VideotexString {
return this.videotexString;
}

set ia5 (value: IA5String) {
this.ia5String = value;
}

get ia5 (): IA5String {
return this.ia5String;
}

set utc (value: UTCTime) {
this.utcTime = value;
}

get utc (): UTCTime {
return this.utcTime;
}

set gtime (value: GeneralizedTime) {
this.generalizedTime = value;
}

get gtime (): GeneralizedTime {
return this.generalizedTime;
}

set ustr (value: UniversalString) {
this.universalString = value;
}

get ustr (): UniversalString {
return this.universalString;
}

set bmp (value: BMPString) {
this.bmpString = value;
}

get bmp (): BMPString {
return this.bmpString;
}

abstract get inner (): ASN1Element;
abstract get components (): ASN1Element[];

Expand Down
44 changes: 35 additions & 9 deletions source/codecs/ber.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,21 @@ export default
class BERElement extends X690Element {
public static lengthEncodingPreference: LengthEncodingPreference = LengthEncodingPreference.definite;

private _value: Uint8Array | ASN1Element[] = new Uint8Array(0);
get value (): Uint8Array {
if (this._value instanceof Uint8Array) {
return this._value;
}
return encodeSequence(this._value);
}
set value (v: Uint8Array) {
this._value = v;
}

public construct (els: ASN1Element[]): void {
this._value = els;
}

set boolean (value: BOOLEAN) {
this.value = encodeBoolean(value);
}
Expand Down Expand Up @@ -688,10 +703,15 @@ class BERElement extends X690Element {
}
}

public toBytes (): Uint8Array {
public tagAndLengthBytes (): Uint8Array {
const tagBytes: number[] = [ 0x00 ];
tagBytes[0] |= (this.tagClass << 6);
tagBytes[0] |= (this.construction << 5);
tagBytes[0] |= (
(BERElement.lengthEncodingPreference === LengthEncodingPreference.indefinite)
|| this.construction === ASN1Construction.constructed
)
? (1 << 5)
: 0;
if (this.tagNumber < 31) {
tagBytes[0] |= this.tagNumber;
} else {
Expand Down Expand Up @@ -744,18 +764,24 @@ class BERElement extends X690Element {
throw new errors.ASN1UndefinedError("Invalid LengthEncodingPreference encountered!", this);
}

const ret: Uint8Array = new Uint8Array(
tagBytes.length
+ lengthOctets.length
+ this.value.length
+ (BERElement.lengthEncodingPreference === LengthEncodingPreference.indefinite ? 2 : 0),
);
const ret: Uint8Array = new Uint8Array(tagBytes.length + lengthOctets.length);
ret.set(tagBytes, 0);
ret.set(lengthOctets, tagBytes.length);
ret.set(this.value, (tagBytes.length + lengthOctets.length));
return ret;
}

public toBuffers (): Uint8Array[] {
return [
this.tagAndLengthBytes(),
...(Array.isArray(this._value)
? this._value.flatMap((el) => el.toBuffers())
: [ this._value ]),
...(BERElement.lengthEncodingPreference === LengthEncodingPreference.indefinite
? [ new Uint8Array(2) ]
: []),
];
}

public deconstruct (dataType: string): Uint8Array {
if (this.construction === ASN1Construction.primitive) {
return new Uint8Array(this.value); // Clones it.
Expand Down
37 changes: 29 additions & 8 deletions source/codecs/cer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,21 @@ import { isUniquelyTagged } from "../utils";

export default
class CERElement extends X690Element {
private _value: Uint8Array | ASN1Element[] = new Uint8Array(0);
get value (): Uint8Array {
if (this._value instanceof Uint8Array) {
return this._value;
}
return encodeSequence(this._value);
}
set value (v: Uint8Array) {
this._value = v;
}

public construct (els: ASN1Element[]): void {
this._value = els;
}

get unfragmentedValue (): Uint8Array {
return this.deconstruct("");
}
Expand Down Expand Up @@ -708,7 +723,7 @@ class CERElement extends X690Element {
}
}

public toBytes (): Uint8Array {
public tagAndLengthBytes (): Uint8Array {
const tagBytes: number[] = [ 0x00 ];
tagBytes[0] |= (this.tagClass << 6);
tagBytes[0] |= (this.construction << 5);
Expand Down Expand Up @@ -764,18 +779,24 @@ class CERElement extends X690Element {
throw new errors.ASN1UndefinedError("Invalid LengthEncodingPreference encountered!");
}

const ret: Uint8Array = new Uint8Array(
tagBytes.length
+ lengthOctets.length
+ this.value.length
+ (this.construction === ASN1Construction.constructed ? 2 : 0),
);
const ret: Uint8Array = new Uint8Array(tagBytes.length + lengthOctets.length);
ret.set(tagBytes, 0);
ret.set(lengthOctets, tagBytes.length);
ret.set(this.value, (tagBytes.length + lengthOctets.length));
return ret;
}

public toBuffers (): Uint8Array[] {
return [
this.tagAndLengthBytes(),
...(Array.isArray(this._value)
? this._value.flatMap((el) => el.toBuffers())
: [ this._value ]),
...(this.construction === ASN1Construction.constructed
? [ new Uint8Array(2) ]
: []),
];
}

public deconstruct (dataType: string): Uint8Array {
if (this.construction === ASN1Construction.primitive) {
return new Uint8Array(this.value); // Clones it.
Expand Down
33 changes: 26 additions & 7 deletions source/codecs/der.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@ import { isUniquelyTagged } from "../utils";

export default
class DERElement extends X690Element {
private _value: Uint8Array | ASN1Element[] = new Uint8Array(0);
get value (): Uint8Array {
if (this._value instanceof Uint8Array) {
return this._value;
}
return encodeSequence(this._value);
}
set value (v: Uint8Array) {
this._value = v;
}

public construct (els: ASN1Element[]): void {
this._value = els;
}

set boolean (value: BOOLEAN) {
this.value = encodeBoolean(value);
}
Expand Down Expand Up @@ -674,7 +689,7 @@ class DERElement extends X690Element {
}
}

public toBytes (): Uint8Array {
public tagAndLengthBytes (): Uint8Array {
const tagBytes: number[] = [ 0x00 ];
tagBytes[0] |= (this.tagClass << 6);
tagBytes[0] |= (this.construction << 5);
Expand Down Expand Up @@ -719,17 +734,21 @@ class DERElement extends X690Element {
lengthOctets.unshift(0b10000000 | lengthOctets.length);
}

const ret: Uint8Array = new Uint8Array(
tagBytes.length
+ lengthOctets.length
+ this.value.length,
);
const ret: Uint8Array = new Uint8Array(tagBytes.length + lengthOctets.length);
ret.set(tagBytes, 0);
ret.set(lengthOctets, tagBytes.length);
ret.set(this.value, (tagBytes.length + lengthOctets.length));
return ret;
}

public toBuffers (): Uint8Array[] {
return [
this.tagAndLengthBytes(),
...(Array.isArray(this._value)
? this._value.flatMap((el) => el.toBuffers())
: [ this._value ]),
];
}

public deconstruct (): Uint8Array {
return new Uint8Array(this.value);
}
Expand Down
1 change: 1 addition & 0 deletions test/x690/constructor.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ const asn1 = require("../../dist/node/index.js");
],
);
const containedElements = el.sequence;
console.log(containedElements);
expect(containedElements.length).toEqual(4);
expect(containedElements[0].boolean).toBe(false);
});
Expand Down

0 comments on commit e39e47b

Please sign in to comment.