Skip to content

Commit

Permalink
BUILDSYS-389 Added an Extension element which may be added to a schem…
Browse files Browse the repository at this point in the history
…a to tell that this strucutre may be larger in the payload than in the schema.

This corresponds to adding ... in the asn1 schema definition which announces a structure to be extendable
  • Loading branch information
JanFellner committed Jan 5, 2024
1 parent a913715 commit af33b16
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 76 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
},
"eslint.alwaysShowStatus": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit"
}
}
18 changes: 18 additions & 0 deletions asn1ts.code-workspace
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"folders": [
{
"path": "."
}
],
"settings": {
"tslint.enable": false,
"eslint.enable": true,
"javascript.preferences.quoteStyle": "double",
"typescript.preferences.quoteStyle": "double",
"editor.tabCompletion": "on",
"eslint.alwaysShowStatus": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}
}
54 changes: 54 additions & 0 deletions src/Extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { ViewWriter } from "./ViewWriter";
import { ValueBlock, ValueBlockJson } from "./ValueBlock";
import { BaseBlock } from "./BaseBlock";
import { ETagClass, EUniversalTagNumber, typeStore } from "./TypeStore";

/**
* It is possible to add a ... to the asn1 Schema for objects that are extendable (The other side may add attributes)
* In order to handle that properly we define a dummy type inside of a schema and this dummy type tells us whether a structure may be larger than expecteed (contains a ... in the ans1 Definitions)
*
* This will value will never get encoded or decoded as there is no such type in any encoding
*/
export class Extension extends BaseBlock<ValueBlock, ValueBlockJson> {
static {
typeStore.Extension = this;
}

public static override NAME = "NULL";
public static override defaultIDs = {tagClass: ETagClass.UNIVERSAL, tagNumber: EUniversalTagNumber.Extension};

constructor() {
super({idBlock: Extension.defaultIDs}, ValueBlock);

}

public getValue(): null {
return null;
}

public setValue(value: number): void {
}

public override fromBER(inputBuffer: ArrayBuffer | Uint8Array, inputOffset: number, inputLength: number): number {
throw new Error("An extension attribute should never get decoded... It´s just for schema validation");
}

public override toBER(sizeOnly?: boolean, writer?: ViewWriter): ArrayBuffer {
throw new Error("An extension attribute should never get encoded... It´s just for schema validation");
}

protected override onAsciiEncoding(): string {
return `${(this.constructor as typeof Extension).NAME}`;
}

/**
* A typeguard that allows to validate if a certain asn1.js object is of our type
*
* @param obj The object we want to match against the type of this class
* @returns true if obj is of the same type as our class
*/
public static typeGuard(obj: unknown | undefined): obj is Extension {
return this.matches(obj);
}

}
8 changes: 7 additions & 1 deletion src/TypeStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import type { UTCTime } from "./UTCTime";
import type { Utf8String } from "./Utf8String";
import type { VideotexString } from "./VideotexString";
import type { VisibleString } from "./VisibleString";
import { Extension } from "./Extension";

export type AsnType = BaseBlock | EndOfContent | AsnBoolean | Integer | Real | BitString | OctetString | Null | ObjectIdentifier | Enumerated | Utf8String | RelativeObjectIdentifier | TIME | Sequence | Set | NumericString | PrintableString | TeletexString | VideotexString | IA5String | UTCTime | GeneralizedTime | GraphicString | VisibleString | GeneralString | UniversalString | CharacterString | BmpString | DATE | TimeOfDay | DateTime | Duration | Constructed | Primitive;

Expand Down Expand Up @@ -69,6 +70,7 @@ export interface TypeStore {
Utf8String: typeof Utf8String;
VideotexString: typeof VideotexString;
VisibleString: typeof VisibleString;
Extension: typeof Extension;
}

export enum ETagClass {
Expand Down Expand Up @@ -115,7 +117,9 @@ export enum EUniversalTagNumber {
DATE = 31,
TimeOfDay = 32,
DateTime = 33,
Duration = 34
Duration = 34,
/** The extension element marks that a schema may contain more attributes than specifieid, thus it defines whether parsing complains about further elements in the schema or not */
Extension = 35
}

/* istanbul ignore next */
Expand Down Expand Up @@ -199,6 +203,8 @@ export function getTagNumberAsText(tagNumber: EUniversalTagNumber): string {
return "DateTime";
case EUniversalTagNumber.Duration:
return "Duration";
case EUniversalTagNumber.Extension:
return "Extension";
default:
return `TAGNUMBER(${tagNumber})`;
}
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export * from "./EndOfContent";

/** common */
export * from "./Null";
export * from "./Extension";
export * from "./Boolean";
export * from "./OctetString";
export * from "./BitString";
Expand Down
24 changes: 13 additions & 11 deletions src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { LocalLengthBlock } from "./internals/LocalLengthBlock";
import { BaseBlock } from "./BaseBlock";
import { SequenceOf } from "./SequenceOf";
import { SetOf } from "./SetOf";
import { Extension } from "./Extension";

export type AsnSchemaType = AsnType | Any | Choice | SequenceOf | SetOf;

Expand All @@ -30,14 +31,11 @@ export interface CompareSchemaFail {
* Allows to configure laxer or stricter parsing
*/
export class VerifyOptions {
constructor(continueOnError = true, allowLargerThanSchema = false) {
constructor(continueOnError = true) {
this.continueOnError = continueOnError;
this.allowLargerThanSchema = allowLargerThanSchema;
}
/** Parsing continues on an error, the errorlist contains all errors */
public continueOnError: boolean;
/** If the asn1 object is larger than the schema, this is not an error */
public allowLargerThanSchema: boolean;
}

/**
Expand Down Expand Up @@ -529,14 +527,18 @@ function compareSchemaInternal(root: AsnType, inputSchema: AsnSchemaType, option
let inputObject = inputValue[i - admission];
let schema = inputSchema.valueBlock.value[i];

if (Extension.typeGuard(schema)) {
// As soon as we face the extension attribute we can no further decode the incoming object
// The schema ends here and the incoming playload may be larger but not relevant to the one providing the schema
break;
}

const newContext = context.recurse(schema);

if (!schema) {
/** The input object exists but is not reference in the schema. */
if (!options.allowLargerThanSchema) {
/** This is not allowed, let´s throw an error */
newContext.path += inputObject.idBlock.getDebug("-");
errors.push(new SchemaError(ESchemaError.ASN1_IS_LARGER_THAN_SCHEMA, newContext));
}
/** This is not allowed, let´s throw an error */
newContext.path += inputObject.idBlock.getDebug("-");
errors.push(new SchemaError(ESchemaError.ASN1_IS_LARGER_THAN_SCHEMA, newContext));
return errors;
}

Expand Down Expand Up @@ -580,7 +582,7 @@ function compareSchemaInternal(root: AsnType, inputSchema: AsnSchemaType, option
/** Now we need to calculate the offset of the payload inside the source elements, It´s the source idblock length + the source len block length */
const offset = inputObject.lenBlock.blockLength + inputObject.idBlock.blockLength;
const decoded = contextualElement.fromBER(contextualElement.valueBeforeDecodeView, offset, contextualElement.valueBeforeDecodeView.length);
if (decoded) {
if (decoded >= 0) {
inputObject = contextualElement;
inputValue[i - admission] = contextualElement;
}
Expand Down
19 changes: 6 additions & 13 deletions test/simple_examples.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ context("Simple examples from the readme.md", () => {
const encoded = seq.toBER();

// 300e0c06737472696e670201010101ff
console.log(Buffer.from(new Uint8Array(encoded)).toString("hex"));
const HexEncoded = Buffer.from(new Uint8Array(encoded)).toString("hex");
assert.equal(HexEncoded, "300e0c06737472696e670201010101ff");
});

it ("How to validate data against a scheme", () => {
Expand Down Expand Up @@ -46,15 +47,11 @@ context("Simple examples from the readme.md", () => {

// Verify the data against the schema
const result = asn1ts.verifySchema(encoded, scheme);
assert.equal(result.verified, true);
if (result.verified) {
// Schema has been verified, let´s get the property "integer_value"
const asn1tsInteger = result.result.getTypedValueByName(asn1ts.Integer, "integer_value");
if (asn1tsInteger) {
// 1
console.log(asn1tsInteger.getValue());
}
} else {
console.log(result.errors);
assert.equal(asn1tsInteger.getValue(), 1);
}
});

Expand Down Expand Up @@ -84,15 +81,11 @@ context("Simple examples from the readme.md", () => {

// Verify the data against the schema
const result = asn1ts.verifySchema(encoded, scheme);
assert.equal(result.verified, true);
if (result.verified) {
// Schema has been verified, let´s get the property "integer_value"
const asn1tsInteger = result.result.getTypedValueByName(asn1ts.Integer, "integer_value");
if (asn1tsInteger) {
// 2
console.log(asn1tsInteger.getValue());
}
} else {
console.log(result.errors);
assert.equal(asn1tsInteger.getValue(), 2);
}
});

Expand Down
Loading

0 comments on commit af33b16

Please sign in to comment.