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

Feature: dynamic asn parser #289

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions src/main/javascript/crypto/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"pack": "webpack",
"clean": "rm -R dist",
"watch": "webpack --watch --progress",
"serve": "webpack serve",
"testjest": "jest",
"prepublishOnly": "npm run clean && npm run build"
},
Expand Down
99 changes: 99 additions & 0 deletions src/main/javascript/crypto/parser-test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<html>
<head>
<script>
process = {env: {NODE_DEBUG: "false"}};
</script>
<script type="text/javascript" src="dist/authenticator.bundle.js"></script>
</head>
<body>
<script>
function test(){
let generatedSchema = new SchemaGenerator({
ticket: {
name: "Ticket",
items: {
devconId: {
type: "Utf8String",
optional: false
},
ticketIdNumber: {
type: "Integer",
optional: true
},
ticketIdString: {
type: "Utf8String",
optional: true
},
ticketClass: {
type: "Integer",
optional: false
},
linkedTicket: {
name: "Linked Ticket",
items: {
devconId: {
type: "Utf8String",
optional: false
},
ticketIdNumber: {
type: "Integer",
optional: true
},
ticketIdString: {
type: "Utf8String",
optional: true
},
ticketClass: {
type: "Integer",
optional: false
}
}
}
}
},
commitment: {
type: "OctetString",
optional: true
},
signatureValue: {
type: "BitString",
optional: false
}
});

let asnObject = generatedSchema.getSchemaObject();

asnObject.ticket.devconId = "6";
asnObject.ticket.ticketIdNumber = 10;
asnObject.ticket.ticketClass = 1;

asnObject.ticket.linkedTicket.devconId = "6";
asnObject.ticket.linkedTicket.ticketIdNumber = 10;
asnObject.ticket.linkedTicket.ticketClass = 1;

asnObject.signatureValue = new Uint8Array([
177, 53, 222, 215, 60, 2, 17, 132, 21, 143, 166,
234, 145, 239, 240, 169, 119, 83, 242, 113, 99, 243,
179, 90, 87, 211, 250, 197, 113, 70, 191, 10, 69,
121, 82, 36, 254, 251, 149, 237, 222, 125, 213, 90,
21, 84, 130, 155, 91, 226, 15, 62, 57, 177, 251,
39, 165, 43, 214, 57, 114, 209, 232, 156, 28
]);

console.log("Populated schema object: ");
console.log(asnObject);

let encoded = generatedSchema.serializeAndFormat(asnObject);

console.log("Encoded: ");
console.log(encoded);

let decoded = generatedSchema.parse(encoded);

console.log("Decoded: ");
console.log(decoded);
}
</script>
<button onclick="test();">Run Test</button>
</body>
</html>
4 changes: 3 additions & 1 deletion src/main/javascript/crypto/src/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import {Authenticator} from "./Authenticator";
import {Eip712AttestationRequest} from "./libs/Eip712AttestationRequest";
import {AttestationCrypto} from "./libs/AttestationCrypto";
import {IntegrationExample} from "./IntegrationExample";
import {SchemaGenerator} from "./util/SchemaGenerator";

(window as any).Authenticator = Authenticator;
(window as any).Attest = Eip712AttestationRequest;
(window as any).AttestationCrypto = AttestationCrypto;
(window as any).IntegrationExample = IntegrationExample;
(window as any).IntegrationExample = IntegrationExample;
(window as any).SchemaGenerator = SchemaGenerator;
97 changes: 97 additions & 0 deletions src/main/javascript/crypto/src/main.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import {Issuer} from "./libs/Issuer";
import { AttestedObject } from './libs/AttestedObject';
import { AttestableObject } from './libs/AttestableObject';
import { UseToken } from './asn1/shemas/UseToken';
import {SchemaGenerator} from "./util/SchemaGenerator";
import {DevconTicket, SignedDevconTicket} from "./asn1/shemas/SignedDevconTicket";
const url = require('url');

let EC = require("elliptic");
Expand Down Expand Up @@ -676,4 +678,99 @@ describe("read attested object", () => {
})
})

describe("Schema Generator", function(){

test("Serialize & parse ASN based on a dynamic schema", async function(){

let generatedSchema = new SchemaGenerator({
ticket: {
name: "Ticket",
items: {
devconId: {
type: "Utf8String",
optional: false
},
ticketIdNumber: {
type: "Integer",
optional: true
},
ticketIdString: {
type: "Utf8String",
optional: true
},
ticketClass: {
type: "Integer",
optional: false
},
linkedTicket: {
name: "Linked Ticket",
items: {
devconId: {
type: "Utf8String",
optional: false
},
ticketIdNumber: {
type: "Integer",
optional: true
},
ticketIdString: {
type: "Utf8String",
optional: true
},
ticketClass: {
type: "Integer",
optional: false
}
}
}
}
},
commitment: {
type: "OctetString",
optional: true
},
signatureValue: {
type: "BitString",
optional: false
}
});

let asnObject = generatedSchema.getSchemaObject();

asnObject.ticket.devconId = "6";
asnObject.ticket.ticketIdNumber = 5;
asnObject.ticket.ticketClass = 1;

asnObject.ticket.linkedTicket.devconId = "6";
asnObject.ticket.linkedTicket.ticketIdNumber = 10;
asnObject.ticket.linkedTicket.ticketClass = 2;

asnObject.signatureValue = new Uint8Array(hexStringToUint8("0xb135ded73c021184158fa6ea91eff0a97753f27163f3b35a57d3fac57146bf0a45795224fefb95edde7dd55a1554829b5be20f3e39b1fb27a52bd63972d1e89c1c"));

console.log(asnObject);

let encoded = generatedSchema.serializeAndFormat(asnObject);

console.log("Encoded: ");
console.log(encoded);

let decoded = generatedSchema.parse(encoded);

console.log("Decoded: ");
console.log(decoded);

// Can't do a single match here because nested properties are set in the prototype
expect(asnObject.ticket.devconId).toBe("6");
expect(asnObject.ticket.ticketIdNumber).toBe(5);
expect(asnObject.ticket.ticketClass).toBe(1);

expect(asnObject.ticket.linkedTicket.devconId).toBe("6");
expect(asnObject.ticket.linkedTicket.ticketIdNumber).toBe(10);
expect(asnObject.ticket.linkedTicket.ticketClass).toBe(2);

decoded.signatureValue = new Uint8Array(decoded.signatureValue);
expect(asnObject.signatureValue).toEqual(decoded.signatureValue);
});
});


133 changes: 133 additions & 0 deletions src/main/javascript/crypto/src/util/SchemaGenerator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import * as asn1_schema_1 from "@peculiar/asn1-schema";
import {AsnParser, AsnPropTypes, AsnSerializer} from "@peculiar/asn1-schema";
import {base64ToUint8array, hexStringToUint8, uint8arrayToBase64, uint8tohex} from "../libs/utils";
import {AsnItemType, AsnRepeatType, IAsn1PropOptions} from "@peculiar/asn1-schema/build/types/decorators";
import {IAsnConverter} from "@peculiar/asn1-schema/build/types/types";

interface SchemaDefinitionInterface {
[key: string]: SchemaItemInterface
}

interface SchemaItemInterface {
name?: string;
items?: SchemaDefinitionInterface,
type?: AsnItemType | string;
optional?: boolean;
defaultValue?: any;
context?: number;
implicit?: boolean;
converter?: IAsnConverter;
repeated?: AsnRepeatType;
}

export declare type EncodingType = "hex" | "base64";

export class SchemaGenerator {

jsonSchema: any;

generatedSchema: any;

private static __decorate = function (decorators: any, target: any, key: any, desc: any) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
// @ts-ignore
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};

constructor(jsonSchema: SchemaDefinitionInterface) {
this.jsonSchema = jsonSchema;

this.generatedSchema = this.generateSchema();
}

private generateSchema(): any {

let Schema: any = class {};

for (let i in this.jsonSchema){

if (this.jsonSchema[i].items){

let childSchemaGenerator: any = new SchemaGenerator(this.jsonSchema[i].items);
let childSchema = childSchemaGenerator.getSchemaType();
Schema.prototype[i] = new childSchema();

SchemaGenerator.__decorate([
(asn1_schema_1.AsnProp)(this.getDecoratorOptions({ type: childSchema }))
], Schema.prototype, i, void 0);

} else {
SchemaGenerator.__decorate([
(asn1_schema_1.AsnProp)(this.getDecoratorOptions(this.jsonSchema[i]))
], Schema.prototype, i, void 0);
}

}

return Schema;
}

private getDecoratorOptions(item: SchemaItemInterface){

let type: AsnPropTypes;

if (typeof item.type === "string"){
if (!(item.type in AsnPropTypes))
throw new Error("Non-existent AsnPropType " + item.type);

type = AsnPropTypes[item.type as any] as unknown as AsnPropTypes;
} else {
type = item.type as AsnPropTypes;
}

let decoratorOptions: IAsn1PropOptions = {
type: type,
optional: item.optional,
defaultValue: item.defaultValue,
context: item.context,
implicit: item.implicit,
converter: item.converter,
repeated: item.repeated
}

return decoratorOptions;
}

getSchemaType(){
return this.generatedSchema;
}

getSchemaObject(){
return new this.generatedSchema();
}

serialize(object: Object): Uint8Array {
return new Uint8Array(AsnSerializer.serialize(object));
}

serializeAndFormat(object: Object, encoding: EncodingType = "hex"){

let uint = this.serialize(object);

if (encoding === "hex"){
return uint8tohex(uint);
} else {
return uint8arrayToBase64(uint);
}
}

parse(data: Uint8Array|string, encoding: EncodingType = "hex"): any {

if (!(data instanceof Uint8Array)){
if (encoding === "hex"){
data = hexStringToUint8(data);
} else {
data = base64ToUint8array(data);
}
}

return AsnParser.parse(data, this.generatedSchema);
}
}
8 changes: 8 additions & 0 deletions src/main/javascript/crypto/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const path = require('path');

module.exports = {
entry: './src/bundle.ts',
mode: "development",
module: {
rules: [
{
Expand All @@ -13,6 +14,13 @@ module.exports = {
}
]
},
devServer: {
static: {
directory: path.join(__dirname, '/'),
},
compress: true,
port: 3015,
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ],
fallback:
Expand Down