diff options
Diffstat (limited to 'third_party/js/PKI.js/src/ECPublicKey.ts')
-rw-r--r-- | third_party/js/PKI.js/src/ECPublicKey.ts | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/third_party/js/PKI.js/src/ECPublicKey.ts b/third_party/js/PKI.js/src/ECPublicKey.ts new file mode 100644 index 0000000000..4e50e92f47 --- /dev/null +++ b/third_party/js/PKI.js/src/ECPublicKey.ts @@ -0,0 +1,192 @@ +import * as asn1js from "asn1js"; +import { BufferSourceConverter } from "pvtsutils"; +import * as pvutils from "pvutils"; +import { EMPTY_BUFFER, EMPTY_STRING } from "./constants"; +import { ECNamedCurves } from "./ECNamedCurves"; +import { ParameterError } from "./errors"; +import { PkiObject, PkiObjectParameters } from "./PkiObject"; +import * as Schema from "./Schema"; + +const X = "x"; +const Y = "y"; +const NAMED_CURVE = "namedCurve"; + +export interface IECPublicKey { + namedCurve: string; + x: ArrayBuffer; + y: ArrayBuffer; +} + +export interface ECPublicKeyJson { + crv: string; + x: string; + y: string; +} + +export type ECPublicKeyParameters = PkiObjectParameters & Partial<IECPublicKey> & { json?: ECPublicKeyJson; }; + +/** + * Represents the PrivateKeyInfo structure described in [RFC5480](https://datatracker.ietf.org/doc/html/rfc5480) + */ +export class ECPublicKey extends PkiObject implements IECPublicKey { + + public static override CLASS_NAME = "ECPublicKey"; + + public namedCurve!: string; + public x!: ArrayBuffer; + public y!: ArrayBuffer; + + /** + * Initializes a new instance of the {@link ECPublicKey} class + * @param parameters Initialization parameters + */ + constructor(parameters: ECPublicKeyParameters = {}) { + super(); + + this.x = pvutils.getParametersValue(parameters, X, ECPublicKey.defaultValues(X)); + this.y = pvutils.getParametersValue(parameters, Y, ECPublicKey.defaultValues(Y)); + this.namedCurve = pvutils.getParametersValue(parameters, NAMED_CURVE, ECPublicKey.defaultValues(NAMED_CURVE)); + + if (parameters.json) { + this.fromJSON(parameters.json); + } + + if (parameters.schema) { + this.fromSchema(parameters.schema); + } + } + + /** + * Returns default values for all class members + * @param memberName String name for a class member + * @returns Default value + */ + public static override defaultValues(memberName: typeof NAMED_CURVE): string; + public static override defaultValues(memberName: typeof X | typeof Y): ArrayBuffer; + public static override defaultValues(memberName: string): any { + switch (memberName) { + case X: + case Y: + return EMPTY_BUFFER; + case NAMED_CURVE: + return EMPTY_STRING; + default: + return super.defaultValues(memberName); + } + } + + + /** + * Compare values with default values for all class members + * @param memberName String name for a class member + * @param memberValue Value to compare with default value + */ + static compareWithDefault<T>(memberName: string, memberValue: T): memberValue is T { + switch (memberName) { + case X: + case Y: + return memberValue instanceof ArrayBuffer && + (pvutils.isEqualBuffer(memberValue, ECPublicKey.defaultValues(memberName))); + case NAMED_CURVE: + return typeof memberValue === "string" && + memberValue === ECPublicKey.defaultValues(memberName); + default: + return super.defaultValues(memberName); + } + } + + /** + * Returns value of pre-defined ASN.1 schema for current class + * @param parameters Input parameters for the schema + * @returns ASN.1 schema object + */ + public static override schema(): Schema.SchemaType { + return new asn1js.RawData(); + } + + public fromSchema(schema1: BufferSource): any { + //#region Check the schema is valid + + const view = BufferSourceConverter.toUint8Array(schema1); + if (view[0] !== 0x04) { + throw new Error("Object's schema was not verified against input data for ECPublicKey"); + } + //#endregion + + //#region Get internal properties from parsed schema + const namedCurve = ECNamedCurves.find(this.namedCurve); + if (!namedCurve) { + throw new Error(`Incorrect curve OID: ${this.namedCurve}`); + } + const coordinateLength = namedCurve.size; + + if (view.byteLength !== (coordinateLength * 2 + 1)) { + throw new Error("Object's schema was not verified against input data for ECPublicKey"); + } + + this.namedCurve = namedCurve.name; + this.x = view.slice(1, coordinateLength + 1).buffer; + this.y = view.slice(1 + coordinateLength, coordinateLength * 2 + 1).buffer; + //#endregion + } + + public toSchema(): asn1js.RawData { + return new asn1js.RawData({ + data: pvutils.utilConcatBuf( + (new Uint8Array([0x04])).buffer, + this.x, + this.y + ) + }); + } + + public toJSON(): ECPublicKeyJson { + const namedCurve = ECNamedCurves.find(this.namedCurve); + + return { + crv: namedCurve ? namedCurve.name : this.namedCurve, + x: pvutils.toBase64(pvutils.arrayBufferToString(this.x), true, true, false), + y: pvutils.toBase64(pvutils.arrayBufferToString(this.y), true, true, false) + }; + } + + /** + * Converts JSON value into current object + * @param json JSON object + */ + public fromJSON(json: any): void { + ParameterError.assert("json", json, "crv", "x", "y"); + + let coordinateLength = 0; + const namedCurve = ECNamedCurves.find(json.crv); + if (namedCurve) { + this.namedCurve = namedCurve.id; + coordinateLength = namedCurve.size; + } + + // TODO Simplify Base64url encoding + const xConvertBuffer = pvutils.stringToArrayBuffer(pvutils.fromBase64(json.x, true)); + + if (xConvertBuffer.byteLength < coordinateLength) { + this.x = new ArrayBuffer(coordinateLength); + const view = new Uint8Array(this.x); + const convertBufferView = new Uint8Array(xConvertBuffer); + view.set(convertBufferView, 1); + } else { + this.x = xConvertBuffer.slice(0, coordinateLength); + } + + // TODO Simplify Base64url encoding + const yConvertBuffer = pvutils.stringToArrayBuffer(pvutils.fromBase64(json.y, true)); + + if (yConvertBuffer.byteLength < coordinateLength) { + this.y = new ArrayBuffer(coordinateLength); + const view = new Uint8Array(this.y); + const convertBufferView = new Uint8Array(yConvertBuffer); + view.set(convertBufferView, 1); + } else { + this.y = yConvertBuffer.slice(0, coordinateLength); + } + } + +} |