Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUILDSYS-389 Add Extension Element to the schema to announce extendable structures like with ... in asn1 #12

Merged
merged 6 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@
},
"plugins": [
"@typescript-eslint",
"deprecation"
"deprecation",
"import",
"unused-imports"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
"plugin:@typescript-eslint/recommended",
"plugin:import/typescript"
],
"ignorePatterns": [
"build/**/*.ts",
Expand Down Expand Up @@ -49,6 +52,18 @@
"@typescript-eslint/semi": 2,
"@typescript-eslint/quotes": 2,
"@typescript-eslint/explicit-module-boundary-types": 0,
"@typescript-eslint/triple-slash-reference": 0
"@typescript-eslint/triple-slash-reference": 0,
"import/no-restricted-paths": [
"error",
{
"zones": [
{
"target": "**/*",
"from": "build"
}
]
}
],
"unused-imports/no-unused-imports": "error"
}
}
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,4 @@ jobs:
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel: true
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"
}
}
}
11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@estos/asn1ts",
"version": "3.1.12",
"version": "3.2.0",
"license": "BSD-3-Clause",
"author": {
"name": "estos GmbH",
Expand All @@ -22,21 +22,24 @@
}
],
"engines": {
"node": ">=18.0.0"
"node": ">=18"
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^15.2.3",
"@types/mocha": "^10.0.1",
"@types/node": "^20.8.6",
"@typescript-eslint/eslint-plugin": "^6.7.5",
"@typescript-eslint/parser": "^6.7.5",
"asn1-test-suite": "^1.0.2",
"eslint": "^8.31.0",
"eslint-plugin-deprecation": "^2.0.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-unused-imports": "^3.0.0",
"mocha": "^10.2.0",
"npm-run-all": "^4.1.5",
"nyc": "^15.1.0",
"rollup": "^4.1.4",
"rollup-plugin-dts": "^6.1.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-typescript2": "^0.36.0",
"ts-node": "^10.9.1",
"typescript": "^5.2.2"
Expand Down Expand Up @@ -85,6 +88,8 @@
"prepublishOnly": "npm run build",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint --fix . --ext .ts",
"relint": "run-s lintclear lint",
"lintclear": "del /s .eslintcache",
"coverage": "nyc npm test"
}
}
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
6 changes: 0 additions & 6 deletions test/asn1repeated.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import * as assert from "assert";
import * as asn1ts from "../src";
import * as pvtsutils from "pvtsutils";
import { SchemaContext } from "../src";
import { ILocalIdentificationBlockParams } from "../src/internals/LocalIdentificationBlock";
import { localFromBER } from "../src/parser";
import { ETagClass } from "../src/TypeStore";


/**
* Gets a repeated schema
Expand Down
20 changes: 6 additions & 14 deletions test/simple_examples.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import * as assert from "assert";
import * as asn1ts from "../src";
import * as pvtsutils from "pvtsutils";

context("Simple examples from the readme.md", () => {
it ("How to create a simple ASN structures", () => {
Expand All @@ -18,7 +17,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 +46,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 +80,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