Skip to content

Commit

Permalink
Merge 9d5fe7d into a9cd745
Browse files Browse the repository at this point in the history
  • Loading branch information
microshine committed Apr 6, 2020
2 parents a9cd745 + 9d5fe7d commit 482884c
Show file tree
Hide file tree
Showing 22 changed files with 1,121 additions and 599 deletions.
1,069 changes: 481 additions & 588 deletions package-lock.json

Large diffs are not rendered by default.

21 changes: 12 additions & 9 deletions package.json
@@ -1,6 +1,6 @@
{
"name": "webcrypto-core",
"version": "1.0.18",
"version": "1.0.19-next.0",
"description": "Common layer to be used by crypto libraries based on WebCrypto API for input validation.",
"main": "build/webcrypto-core.js",
"module": "build/webcrypto-core.es.js",
Expand Down Expand Up @@ -41,21 +41,24 @@
"ec"
],
"dependencies": {
"@peculiar/asn1-schema": "^1.0.5",
"@peculiar/json-schema": "^1.1.10",
"asn1js": "^2.0.26",
"pvtsutils": "^1.0.10",
"tslib": "^1.11.1"
},
"devDependencies": {
"@types/mocha": "^7.0.2",
"@types/node": "^10.17.17",
"coveralls": "^3.0.9",
"mocha": "^7.1.0",
"nyc": "^15.0.0",
"@types/node": "^10.17.18",
"coveralls": "^3.0.11",
"mocha": "^7.1.1",
"nyc": "^15.0.1",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rollup": "^2.0.6",
"rollup": "^2.3.3",
"rollup-plugin-typescript2": "^0.26.0",
"ts-node": "^8.6.2",
"tslint": "^6.1.0",
"ts-node": "^8.8.2",
"tslint": "^6.1.1",
"typescript": "^3.8.3"
},
"author": "PeculiarVentures",
Expand Down Expand Up @@ -85,7 +88,7 @@
"extension": [
"ts"
],
"watch-files": [
"spec": [
"test/**/*.ts"
]
}
Expand Down
31 changes: 31 additions & 0 deletions src/asn1/algorithm_identifier.ts
@@ -0,0 +1,31 @@
import { AsnProp, AsnPropTypes } from "@peculiar/asn1-schema";

// RFC 5280
// https://tools.ietf.org/html/rfc5280#section-4.1.1.2
//
// AlgorithmIdentifier ::= SEQUENCE {
// algorithm OBJECT IDENTIFIER,
// parameters ANY DEFINED BY algorithm OPTIONAL }
// -- contains a value of the type
// -- registered for use with the
// -- algorithm object identifier value

export type ParametersType = ArrayBuffer | null;

export class AlgorithmIdentifier {

@AsnProp({
type: AsnPropTypes.ObjectIdentifier,
})
public algorithm!: string;

@AsnProp({
type: AsnPropTypes.Any,
optional: true,
})
public parameters?: ParametersType;

constructor(params?: Partial<AlgorithmIdentifier>) {
Object.assign(this, params);
}
}
1 change: 1 addition & 0 deletions src/asn1/converters/index.ts
@@ -0,0 +1 @@
export * from "./integer_without_paddings";
21 changes: 21 additions & 0 deletions src/asn1/converters/integer_without_paddings.ts
@@ -0,0 +1,21 @@
import { IAsnConverter } from "@peculiar/asn1-schema";
// @ts-ignore
import * as asn1 from "asn1js";

export const AsnIntegerWithoutPaddingConverter: IAsnConverter<ArrayBuffer> = {
fromASN: (value: any) => {
const bytes = new Uint8Array(value.valueBlock.valueHex);
return (bytes[0] === 0)
? bytes.buffer.slice(1)
: bytes.buffer;
},
toASN: (value: ArrayBuffer): any => {
const bytes = new Uint8Array(value);
if (bytes[0] > 127) {
const newValue = new Uint8Array(bytes.length + 1);
newValue.set(bytes, 1);
return new asn1.Integer({ valueHex: newValue });
}
return new asn1.Integer({ valueHex: value });
},
};
54 changes: 54 additions & 0 deletions src/asn1/ec_private_key.ts
@@ -0,0 +1,54 @@
import { AsnIntegerConverter, AsnProp, AsnPropTypes, AsnSerializer } from "@peculiar/asn1-schema";
import { IJsonConvertible } from "@peculiar/json-schema";
import { Convert } from "pvtsutils";
import { EcPublicKey } from "./ec_public_key";

// RFC 5915
// https://tools.ietf.org/html/rfc5915#section-3
//
// ECPrivateKey ::= SEQUENCE {
// version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
// privateKey OCTET STRING,
// parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
// publicKey [1] BIT STRING OPTIONAL
// }

export class EcPrivateKey implements IJsonConvertible {

@AsnProp({ type: AsnPropTypes.Integer, converter: AsnIntegerConverter })
public version = 1;

@AsnProp({ type: AsnPropTypes.OctetString })
public privateKey = new ArrayBuffer(0);

@AsnProp({ context: 0, type: AsnPropTypes.Any, optional: true })
public parameters?: ArrayBuffer;

@AsnProp({ context: 1, type: AsnPropTypes.BitString, optional: true })
public publicKey?: ArrayBuffer;

public fromJSON(json: any): this {
if (!("d" in json)) {
throw new Error("d: Missing required property");
}
this.privateKey = Convert.FromBase64Url(json.d);

if ("x" in json) {
const publicKey = new EcPublicKey();
publicKey.fromJSON(json);

this.publicKey = AsnSerializer.toASN(publicKey).valueBlock.valueHex;
}

return this;
}
public toJSON() {
const jwk: JsonWebKey = {};
jwk.d = Convert.ToBase64Url(this.privateKey);
if (this.publicKey) {
Object.assign(jwk, new EcPublicKey(this.publicKey).toJSON());
}
return jwk;
}

}
64 changes: 64 additions & 0 deletions src/asn1/ec_public_key.ts
@@ -0,0 +1,64 @@
import { AsnProp, AsnPropTypes, AsnType, AsnTypeTypes } from "@peculiar/asn1-schema";
import { IJsonConvertible } from "@peculiar/json-schema";
import { Convert } from "pvtsutils";
import { CryptoError } from "../errors";

// RFC 5480
// https://tools.ietf.org/html/rfc5480#section-2.2
//
// ECPoint ::= OCTET STRING

@AsnType({ type: AsnTypeTypes.Choice })
export class EcPublicKey implements IJsonConvertible {

@AsnProp({ type: AsnPropTypes.OctetString })
public value = new ArrayBuffer(0);

constructor(value?: ArrayBuffer) {
if (value) {
this.value = value;
}
}

public toJSON() {
let bytes = new Uint8Array(this.value);

if (bytes[0] !== 0x04) {
throw new CryptoError("Wrong ECPoint. Current version supports only Uncompressed (0x04) point");
}

bytes = new Uint8Array(this.value.slice(1));
const size = bytes.length / 2;

const offset = 0;
const json = {
x: Convert.ToBase64Url(bytes.buffer.slice(offset, offset + size)),
y: Convert.ToBase64Url(bytes.buffer.slice(offset + size, offset + size + size)),
};

return json;
}

public fromJSON(json: any): this {
if (!("x" in json)) {
throw new Error("x: Missing required property");
}
if (!("y" in json)) {
throw new Error("y: Missing required property");
}

const x = Convert.FromBase64Url(json.x);
const y = Convert.FromBase64Url(json.y);

const value = Buffer.concat([
new Uint8Array([0x04]), // uncompressed bit
new Uint8Array(x),
new Uint8Array(y),
]);

this.value = new Uint8Array(value).buffer;

return this;
}

}
78 changes: 78 additions & 0 deletions src/asn1/ec_signature.ts
@@ -0,0 +1,78 @@
import { AsnProp, AsnPropTypes } from "@peculiar/asn1-schema";
import { BufferSourceConverter } from "pvtsutils";
import { AsnIntegerWithoutPaddingConverter } from "./converters";

// RFC 3279
// https://tools.ietf.org/html/rfc3279#section-2.2.3
//
// ECDSA-Sig-Value ::= SEQUENCE {
// r INTEGER,
// s INTEGER
// }



export class EcDsaSignature {

public static fromWebCryptoSignature(value: BufferSource): EcDsaSignature {
const wcSignature = BufferSourceConverter.toUint8Array(value);
const pointSize = wcSignature.byteLength / 2;
const ecSignature = new this();
ecSignature.r = ecSignature.removePadding(wcSignature.slice(0, pointSize));
ecSignature.s = ecSignature.removePadding(wcSignature.slice(pointSize, pointSize * 2));
return ecSignature;
}

@AsnProp({ type: AsnPropTypes.Integer, converter: AsnIntegerWithoutPaddingConverter })
public r = new ArrayBuffer(0);

@AsnProp({ type: AsnPropTypes.Integer, converter: AsnIntegerWithoutPaddingConverter })
public s = new ArrayBuffer(0);

public toWebCryptoSignature(pointSize?: number) {
pointSize = this.getPointSize();
const r = this.addPadding(pointSize, BufferSourceConverter.toUint8Array(this.r));
const s = this.addPadding(pointSize, BufferSourceConverter.toUint8Array(this.s));

const wcSignature = new Uint8Array(r.byteLength + s.byteLength);
wcSignature.set(r, 0);
wcSignature.set(s, r.length);
return wcSignature.buffer;
}

private getPointSize(): number {
// tslint:disable-next-line: no-bitwise
const size = Math.max(this.r.byteLength, this.s.byteLength);
switch (size) {
case 31:
case 32:
return 32;
case 47:
case 48:
return 48;
case 65:
case 66:
return 66;
}
throw new Error("Unsupported EC point size");
}

private addPadding(pointSize: number, bytes: BufferSource) {
const res = new Uint8Array(pointSize);
const uint8Array = BufferSourceConverter.toUint8Array(bytes);
res.set(uint8Array, pointSize - uint8Array.length);
return res;
}

private removePadding(bytes: BufferSource) {
const uint8Array = BufferSourceConverter.toUint8Array(bytes);
for (let i = 0; i < uint8Array.length; i++) {
if (!uint8Array[i]) {
continue;
}
return uint8Array.slice(i);
}
return new Uint8Array(0);
}

}
10 changes: 10 additions & 0 deletions src/asn1/index.ts
@@ -0,0 +1,10 @@
export * from "./object_identifier";
export * from "./algorithm_identifier";
export * from "./private_key_info";
export * from "./public_key_info";
export * from "./rsa_private_key";
export * from "./rsa_public_key";
export * from "./ec_private_key";
export * from "./ec_public_key";
export * from "./ec_signature";
export * as converters from "./converters";
14 changes: 14 additions & 0 deletions src/asn1/object_identifier.ts
@@ -0,0 +1,14 @@
import { AsnProp, AsnPropTypes, AsnType, AsnTypeTypes } from "@peculiar/asn1-schema";

@AsnType({ type: AsnTypeTypes.Choice })
export class ObjectIdentifier {

@AsnProp({type: AsnPropTypes.ObjectIdentifier})
public value!: string;

constructor(value?: string) {
if (value) {
this.value = value;
}
}
}
35 changes: 35 additions & 0 deletions src/asn1/private_key_info.ts
@@ -0,0 +1,35 @@
import { AsnProp, AsnPropTypes } from "@peculiar/asn1-schema";
import { AlgorithmIdentifier } from "./algorithm_identifier";

// RFC 5208
// https://tools.ietf.org/html/rfc5208#section-5
//
// PrivateKeyInfo ::= SEQUENCE {
// version Version,
// privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
// privateKey PrivateKey,
// attributes [0] IMPLICIT Attributes OPTIONAL }
//
// Version ::= INTEGER
//
// PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
//
// PrivateKey ::= OCTET STRING
//
// Attributes ::= SET OF Attribute

export class PrivateKeyInfo {

@AsnProp({ type: AsnPropTypes.Integer })
public version = 0;

@AsnProp({ type: AlgorithmIdentifier })
public privateKeyAlgorithm = new AlgorithmIdentifier();

@AsnProp({ type: AsnPropTypes.OctetString })
public privateKey = new ArrayBuffer(0);

@AsnProp({ type: AsnPropTypes.Any, optional: true })
public attributes?: ArrayBuffer;

}
19 changes: 19 additions & 0 deletions src/asn1/public_key_info.ts
@@ -0,0 +1,19 @@
import { AsnProp, AsnPropTypes } from "@peculiar/asn1-schema";
import { AlgorithmIdentifier } from "./algorithm_identifier";

// RFC 5280
// https://tools.ietf.org/html/rfc5280#section-4.1
//
// SubjectPublicKeyInfo ::= SEQUENCE {
// algorithm AlgorithmIdentifier,
// subjectPublicKey BIT STRING

export class PublicKeyInfo {

@AsnProp({ type: AlgorithmIdentifier })
public publicKeyAlgorithm = new AlgorithmIdentifier();

@AsnProp({ type: AsnPropTypes.BitString })
public publicKey = new ArrayBuffer(0);

}

0 comments on commit 482884c

Please sign in to comment.