Merge pull request #36 from PeculiarVentures:ec-brainpool
EC Brainpool curves
This commit is contained in:
commit
ed2a2bf448
|
@ -8,7 +8,7 @@ jobs:
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [10.x, 12.x, 14.x]
|
node-version: [12.x, 14.x, 16.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
import * as core from "webcrypto-core";
|
export declare class Crypto implements globalThis.Crypto {
|
||||||
|
|
||||||
export declare class Crypto implements core.NativeCrypto {
|
|
||||||
public subtle: SubtleCrypto;
|
public subtle: SubtleCrypto;
|
||||||
public getRandomValues<T extends Int8Array | Int16Array | Int32Array | Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray | Float32Array | Float64Array | DataView | null>(array: T): T;
|
public getRandomValues<T extends ArrayBufferView | null>(array: T): T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare class CryptoKey implements core.NativeCryptoKey {
|
export declare class CryptoKey implements globalThis.CryptoKey {
|
||||||
public algorithm: KeyAlgorithm;
|
public algorithm: KeyAlgorithm;
|
||||||
public extractable: boolean;
|
public extractable: boolean;
|
||||||
public type: KeyType;
|
public type: KeyType;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
30
package.json
30
package.json
|
@ -61,27 +61,27 @@
|
||||||
"banner": "// Copyright (c) 2021, Peculiar Ventures, All rights reserved.",
|
"banner": "// Copyright (c) 2021, Peculiar Ventures, All rights reserved.",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@peculiar/webcrypto-test": "^1.0.7",
|
"@peculiar/webcrypto-test": "^1.0.7",
|
||||||
"@types/mocha": "^8.2.2",
|
"@types/mocha": "^9.0.0",
|
||||||
"@types/node": "^14.14.44",
|
"@types/node": "^16.11.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.23.0",
|
"@typescript-eslint/eslint-plugin": "^5.2.0",
|
||||||
"@typescript-eslint/parser": "^4.23.0",
|
"@typescript-eslint/parser": "^5.2.0",
|
||||||
"coveralls": "^3.1.0",
|
"coveralls": "^3.1.1",
|
||||||
"eslint": "^7.26.0",
|
"eslint": "^8.1.0",
|
||||||
"eslint-plugin-import": "^2.22.1",
|
"eslint-plugin-import": "^2.25.2",
|
||||||
"mocha": "^8.4.0",
|
"mocha": "^9.1.3",
|
||||||
"nyc": "^15.1.0",
|
"nyc": "^15.1.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rollup": "^2.47.0",
|
"rollup": "^2.58.1",
|
||||||
"rollup-plugin-typescript2": "^0.30.0",
|
"rollup-plugin-typescript2": "^0.30.0",
|
||||||
"ts-node": "^9.1.1",
|
"ts-node": "^10.4.0",
|
||||||
"typescript": "^4.2.4"
|
"typescript": "^4.4.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@peculiar/asn1-schema": "^2.0.32",
|
"@peculiar/asn1-schema": "^2.0.38",
|
||||||
"@peculiar/json-schema": "^1.1.12",
|
"@peculiar/json-schema": "^1.1.12",
|
||||||
"pvtsutils": "^1.1.6",
|
"pvtsutils": "^1.2.1",
|
||||||
"tslib": "^2.2.0",
|
"tslib": "^2.3.1",
|
||||||
"webcrypto-core": "^1.2.0"
|
"webcrypto-core": "^1.3.0"
|
||||||
},
|
},
|
||||||
"nyc": {
|
"nyc": {
|
||||||
"extension": [
|
"extension": [
|
||||||
|
|
|
@ -6,7 +6,10 @@ export class Crypto extends core.Crypto {
|
||||||
|
|
||||||
public subtle = new SubtleCrypto();
|
public subtle = new SubtleCrypto();
|
||||||
|
|
||||||
public getRandomValues<T extends ArrayBufferView>(array: T): T {
|
public getRandomValues<T extends ArrayBufferView | null>(array: T): T {
|
||||||
|
if (!ArrayBuffer.isView(array)) {
|
||||||
|
throw new TypeError("Failed to execute 'getRandomValues' on 'Crypto': parameter 1 is not of type 'ArrayBufferView'");
|
||||||
|
}
|
||||||
const buffer = Buffer.from(array.buffer);
|
const buffer = Buffer.from(array.buffer);
|
||||||
crypto.randomFillSync(buffer);
|
crypto.randomFillSync(buffer);
|
||||||
return array;
|
return array;
|
||||||
|
|
|
@ -125,12 +125,12 @@ export class AesCmacProvider extends core.AesCmacProvider {
|
||||||
return setCryptoKey(key);
|
return setCryptoKey(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async onSign(algorithm: AesCmacParams, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
|
public async onSign(algorithm: core.AesCmacParams, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
|
||||||
const result = aesCmac(getCryptoKey(key).data, Buffer.from(data));
|
const result = aesCmac(getCryptoKey(key).data, Buffer.from(data));
|
||||||
return new Uint8Array(result).buffer;
|
return new Uint8Array(result).buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async onVerify(algorithm: AesCmacParams, key: AesCryptoKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean> {
|
public async onVerify(algorithm: core.AesCmacParams, key: AesCryptoKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean> {
|
||||||
const signature2 = await this.sign(algorithm, key, data);
|
const signature2 = await this.sign(algorithm, key, data);
|
||||||
return Buffer.from(signature).compare(Buffer.from(signature2)) === 0;
|
return Buffer.from(signature).compare(Buffer.from(signature2)) === 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
import { AsnParser, AsnSerializer } from "@peculiar/asn1-schema";
|
import { AsnParser, AsnSerializer } from "@peculiar/asn1-schema";
|
||||||
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
|
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
|
||||||
|
import {BufferSourceConverter} from "pvtsutils";
|
||||||
import * as core from "webcrypto-core";
|
import * as core from "webcrypto-core";
|
||||||
import { CryptoKey } from "../../keys";
|
import { CryptoKey } from "../../keys";
|
||||||
import { getOidByNamedCurve } from "./helper";
|
import { getOidByNamedCurve } from "./helper";
|
||||||
|
@ -12,7 +13,7 @@ export class EcCrypto {
|
||||||
public static publicKeyUsages = ["verify"];
|
public static publicKeyUsages = ["verify"];
|
||||||
public static privateKeyUsages = ["sign", "deriveKey", "deriveBits"];
|
public static privateKeyUsages = ["sign", "deriveKey", "deriveBits"];
|
||||||
|
|
||||||
public static async generateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair> {
|
public static async generateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKeyPair> {
|
||||||
const privateKey = new EcPrivateKey();
|
const privateKey = new EcPrivateKey();
|
||||||
privateKey.algorithm = algorithm;
|
privateKey.algorithm = algorithm;
|
||||||
privateKey.extractable = extractable;
|
privateKey.extractable = extractable;
|
||||||
|
@ -38,7 +39,7 @@ export class EcCrypto {
|
||||||
privateKey.data = keys.privateKey;
|
privateKey.data = keys.privateKey;
|
||||||
publicKey.data = keys.publicKey;
|
publicKey.data = keys.publicKey;
|
||||||
|
|
||||||
const res: CryptoKeyPair = {
|
const res = {
|
||||||
privateKey,
|
privateKey,
|
||||||
publicKey,
|
publicKey,
|
||||||
};
|
};
|
||||||
|
@ -61,12 +62,9 @@ export class EcCrypto {
|
||||||
const signature = signer.sign(options);
|
const signature = signer.sign(options);
|
||||||
const ecSignature = AsnParser.parse(signature, core.asn1.EcDsaSignature);
|
const ecSignature = AsnParser.parse(signature, core.asn1.EcDsaSignature);
|
||||||
|
|
||||||
const pointSize = this.getPointSize(key.algorithm.namedCurve);
|
const signatureRaw = core.EcUtils.encodeSignature(ecSignature, core.EcCurves.get(key.algorithm.namedCurve).size);
|
||||||
const r = this.addPadding(pointSize, Buffer.from(ecSignature.r));
|
|
||||||
const s = this.addPadding(pointSize, Buffer.from(ecSignature.s));
|
return signatureRaw.buffer;
|
||||||
|
|
||||||
const signatureRaw = new Uint8Array(Buffer.concat([r, s])).buffer;
|
|
||||||
return signatureRaw;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async verify(algorithm: EcdsaParams, key: EcPublicKey, signature: Uint8Array, data: Uint8Array): Promise<boolean> {
|
public static async verify(algorithm: EcdsaParams, key: EcPublicKey, signature: Uint8Array, data: Uint8Array): Promise<boolean> {
|
||||||
|
@ -82,9 +80,10 @@ export class EcCrypto {
|
||||||
};
|
};
|
||||||
|
|
||||||
const ecSignature = new core.asn1.EcDsaSignature();
|
const ecSignature = new core.asn1.EcDsaSignature();
|
||||||
const pointSize = this.getPointSize(key.algorithm.namedCurve);
|
const namedCurve = core.EcCurves.get(key.algorithm.namedCurve);
|
||||||
ecSignature.r = this.removePadding(signature.slice(0, pointSize));
|
const signaturePoint = core.EcUtils.decodeSignature(signature, namedCurve.size);
|
||||||
ecSignature.s = this.removePadding(signature.slice(pointSize, pointSize + pointSize));
|
ecSignature.r = BufferSourceConverter.toArrayBuffer(signaturePoint.r);
|
||||||
|
ecSignature.s = BufferSourceConverter.toArrayBuffer(signaturePoint.s);
|
||||||
|
|
||||||
const ecSignatureRaw = Buffer.from(AsnSerializer.serialize(ecSignature));
|
const ecSignatureRaw = Buffer.from(AsnSerializer.serialize(ecSignature));
|
||||||
const ok = signer.verify(options, ecSignatureRaw);
|
const ok = signer.verify(options, ecSignatureRaw);
|
||||||
|
@ -215,38 +214,8 @@ export class EcCrypto {
|
||||||
case "P-521":
|
case "P-521":
|
||||||
return "secp521r1";
|
return "secp521r1";
|
||||||
default:
|
default:
|
||||||
throw new core.OperationError(`Cannot convert WebCrypto named curve to NodeJs. Unknown name '${curve}'`);
|
return curve;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static getPointSize(namedCurve: string) {
|
|
||||||
switch (namedCurve) {
|
|
||||||
case "P-256":
|
|
||||||
case "K-256":
|
|
||||||
return 32;
|
|
||||||
case "P-384":
|
|
||||||
return 48;
|
|
||||||
case "P-521":
|
|
||||||
return 66;
|
|
||||||
default:
|
|
||||||
throw new Error(`Cannot get size for the named curve '${namedCurve}'`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static addPadding(pointSize: number, bytes: Buffer) {
|
|
||||||
const res = Buffer.alloc(pointSize);
|
|
||||||
res.set(Buffer.from(bytes), pointSize - bytes.length);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static removePadding(bytes: Uint8Array) {
|
|
||||||
for (let i = 0; i < bytes.length; i++) {
|
|
||||||
if (!bytes[i]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return bytes.slice(i).buffer;
|
|
||||||
}
|
|
||||||
return new ArrayBuffer(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,9 @@ import { EcPublicKey } from "./public_key";
|
||||||
|
|
||||||
export class EcdhProvider extends core.EcdhProvider {
|
export class EcdhProvider extends core.EcdhProvider {
|
||||||
|
|
||||||
public async onGenerateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair> {
|
public namedCurves = core.EcCurves.names;
|
||||||
|
|
||||||
|
public async onGenerateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKeyPair> {
|
||||||
const keys = await EcCrypto.generateKey(
|
const keys = await EcCrypto.generateKey(
|
||||||
{
|
{
|
||||||
...algorithm,
|
...algorithm,
|
||||||
|
|
|
@ -6,9 +6,9 @@ import { EcPublicKey } from "./public_key";
|
||||||
|
|
||||||
export class EcdsaProvider extends core.EcdsaProvider {
|
export class EcdsaProvider extends core.EcdsaProvider {
|
||||||
|
|
||||||
public namedCurves = ["P-256", "P-384", "P-521", "K-256"];
|
public namedCurves = core.EcCurves.names;
|
||||||
|
|
||||||
public async onGenerateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair> {
|
public async onGenerateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKeyPair> {
|
||||||
const keys = await EcCrypto.generateKey(
|
const keys = await EcCrypto.generateKey(
|
||||||
{
|
{
|
||||||
...algorithm,
|
...algorithm,
|
||||||
|
|
|
@ -13,6 +13,36 @@ const namedOIDs: { [key: string]: string } = {
|
||||||
// K-256
|
// K-256
|
||||||
"1.3.132.0.10": "K-256",
|
"1.3.132.0.10": "K-256",
|
||||||
"K-256": "1.3.132.0.10",
|
"K-256": "1.3.132.0.10",
|
||||||
|
|
||||||
|
// brainpool
|
||||||
|
"brainpoolP160r1": "1.3.36.3.3.2.8.1.1.1",
|
||||||
|
"1.3.36.3.3.2.8.1.1.1": "brainpoolP160r1",
|
||||||
|
"brainpoolP160t1": "1.3.36.3.3.2.8.1.1.2",
|
||||||
|
"1.3.36.3.3.2.8.1.1.2": "brainpoolP160t1",
|
||||||
|
"brainpoolP192r1": "1.3.36.3.3.2.8.1.1.3",
|
||||||
|
"1.3.36.3.3.2.8.1.1.3": "brainpoolP192r1",
|
||||||
|
"brainpoolP192t1": "1.3.36.3.3.2.8.1.1.4",
|
||||||
|
"1.3.36.3.3.2.8.1.1.4": "brainpoolP192t1",
|
||||||
|
"brainpoolP224r1": "1.3.36.3.3.2.8.1.1.5",
|
||||||
|
"1.3.36.3.3.2.8.1.1.5": "brainpoolP224r1",
|
||||||
|
"brainpoolP224t1": "1.3.36.3.3.2.8.1.1.6",
|
||||||
|
"1.3.36.3.3.2.8.1.1.6": "brainpoolP224t1",
|
||||||
|
"brainpoolP256r1": "1.3.36.3.3.2.8.1.1.7",
|
||||||
|
"1.3.36.3.3.2.8.1.1.7": "brainpoolP256r1",
|
||||||
|
"brainpoolP256t1": "1.3.36.3.3.2.8.1.1.8",
|
||||||
|
"1.3.36.3.3.2.8.1.1.8": "brainpoolP256t1",
|
||||||
|
"brainpoolP320r1": "1.3.36.3.3.2.8.1.1.9",
|
||||||
|
"1.3.36.3.3.2.8.1.1.9": "brainpoolP320r1",
|
||||||
|
"brainpoolP320t1": "1.3.36.3.3.2.8.1.1.10",
|
||||||
|
"1.3.36.3.3.2.8.1.1.10": "brainpoolP320t1",
|
||||||
|
"brainpoolP384r1": "1.3.36.3.3.2.8.1.1.11",
|
||||||
|
"1.3.36.3.3.2.8.1.1.11": "brainpoolP384r1",
|
||||||
|
"brainpoolP384t1": "1.3.36.3.3.2.8.1.1.12",
|
||||||
|
"1.3.36.3.3.2.8.1.1.12": "brainpoolP384t1",
|
||||||
|
"brainpoolP512r1": "1.3.36.3.3.2.8.1.1.13",
|
||||||
|
"1.3.36.3.3.2.8.1.1.13": "brainpoolP512r1",
|
||||||
|
"brainpoolP512t1": "1.3.36.3.3.2.8.1.1.14",
|
||||||
|
"1.3.36.3.3.2.8.1.1.14": "brainpoolP512t1",
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getNamedCurveByOid(oid: string) {
|
export function getNamedCurveByOid(oid: string) {
|
||||||
|
|
|
@ -12,7 +12,7 @@ export class EdCrypto {
|
||||||
public static publicKeyUsages = ["verify"];
|
public static publicKeyUsages = ["verify"];
|
||||||
public static privateKeyUsages = ["sign", "deriveKey", "deriveBits"];
|
public static privateKeyUsages = ["sign", "deriveKey", "deriveBits"];
|
||||||
|
|
||||||
public static async generateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair> {
|
public static async generateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKeyPair> {
|
||||||
const privateKey = new EdPrivateKey();
|
const privateKey = new EdPrivateKey();
|
||||||
privateKey.algorithm = algorithm;
|
privateKey.algorithm = algorithm;
|
||||||
privateKey.extractable = extractable;
|
privateKey.extractable = extractable;
|
||||||
|
@ -38,7 +38,7 @@ export class EdCrypto {
|
||||||
privateKey.data = keys.privateKey;
|
privateKey.data = keys.privateKey;
|
||||||
publicKey.data = keys.publicKey;
|
publicKey.data = keys.publicKey;
|
||||||
|
|
||||||
const res: CryptoKeyPair = {
|
const res = {
|
||||||
privateKey,
|
privateKey,
|
||||||
publicKey,
|
publicKey,
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { EdCrypto } from "./crypto";
|
||||||
|
|
||||||
export class EcdhEsProvider extends core.EcdhEsProvider {
|
export class EcdhEsProvider extends core.EcdhEsProvider {
|
||||||
|
|
||||||
public async onGenerateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair> {
|
public async onGenerateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKeyPair> {
|
||||||
const keys = await EdCrypto.generateKey(
|
const keys = await EdCrypto.generateKey(
|
||||||
{
|
{
|
||||||
name: this.name,
|
name: this.name,
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { EdPublicKey } from "./public_key";
|
||||||
|
|
||||||
export class EdDsaProvider extends core.EdDsaProvider {
|
export class EdDsaProvider extends core.EdDsaProvider {
|
||||||
|
|
||||||
public async onGenerateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair> {
|
public async onGenerateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKeyPair> {
|
||||||
const keys = await EdCrypto.generateKey(
|
const keys = await EdCrypto.generateKey(
|
||||||
{
|
{
|
||||||
name: this.name,
|
name: this.name,
|
||||||
|
@ -21,19 +21,19 @@ export class EdDsaProvider extends core.EdDsaProvider {
|
||||||
publicKey: setCryptoKey(keys.publicKey as CryptoKey),
|
publicKey: setCryptoKey(keys.publicKey as CryptoKey),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async onSign(algorithm: EcdsaParams, key: CryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
|
public async onSign(algorithm: EcdsaParams, key: CryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
|
||||||
return EdCrypto.sign(algorithm, getCryptoKey(key) as EdPrivateKey, new Uint8Array(data));
|
return EdCrypto.sign(algorithm, getCryptoKey(key) as EdPrivateKey, new Uint8Array(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async onVerify(algorithm: EcdsaParams, key: CryptoKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean> {
|
public async onVerify(algorithm: EcdsaParams, key: CryptoKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean> {
|
||||||
return EdCrypto.verify(algorithm, getCryptoKey(key) as EdPublicKey, new Uint8Array(signature), new Uint8Array(data));
|
return EdCrypto.verify(algorithm, getCryptoKey(key) as EdPublicKey, new Uint8Array(signature), new Uint8Array(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async onExportKey(format: KeyFormat, key: CryptoKey): Promise<ArrayBuffer | JsonWebKey> {
|
public async onExportKey(format: KeyFormat, key: CryptoKey): Promise<ArrayBuffer | JsonWebKey> {
|
||||||
return EdCrypto.exportKey(format, getCryptoKey(key));
|
return EdCrypto.exportKey(format, getCryptoKey(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async onImportKey(format: KeyFormat, keyData: ArrayBuffer | JsonWebKey, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKey> {
|
public async onImportKey(format: KeyFormat, keyData: ArrayBuffer | JsonWebKey, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKey> {
|
||||||
const key = await EdCrypto.importKey(format, keyData, { ...algorithm, name: this.name }, extractable, keyUsages);
|
const key = await EdCrypto.importKey(format, keyData, { ...algorithm, name: this.name }, extractable, keyUsages);
|
||||||
return setCryptoKey(key);
|
return setCryptoKey(key);
|
||||||
|
|
|
@ -18,7 +18,7 @@ export class RsaCrypto {
|
||||||
public static publicKeyUsages = ["verify", "encrypt", "wrapKey"];
|
public static publicKeyUsages = ["verify", "encrypt", "wrapKey"];
|
||||||
public static privateKeyUsages = ["sign", "decrypt", "unwrapKey"];
|
public static privateKeyUsages = ["sign", "decrypt", "unwrapKey"];
|
||||||
|
|
||||||
public static async generateKey(algorithm: RsaHashedKeyGenParams | RsaKeyGenParams, extractable: boolean, keyUsages: string[]): Promise<CryptoKeyPair> {
|
public static async generateKey(algorithm: RsaHashedKeyGenParams | RsaKeyGenParams, extractable: boolean, keyUsages: string[]): Promise<core.CryptoKeyPair> {
|
||||||
const privateKey = new RsaPrivateKey();
|
const privateKey = new RsaPrivateKey();
|
||||||
privateKey.algorithm = algorithm as RsaHashedKeyAlgorithm;
|
privateKey.algorithm = algorithm as RsaHashedKeyAlgorithm;
|
||||||
privateKey.extractable = extractable;
|
privateKey.extractable = extractable;
|
||||||
|
@ -50,7 +50,7 @@ export class RsaCrypto {
|
||||||
privateKey.data = keys.privateKey;
|
privateKey.data = keys.privateKey;
|
||||||
publicKey.data = keys.publicKey;
|
publicKey.data = keys.publicKey;
|
||||||
|
|
||||||
const res: CryptoKeyPair = {
|
const res = {
|
||||||
privateKey,
|
privateKey,
|
||||||
publicKey,
|
publicKey,
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,7 +14,7 @@ export class RsaEsProvider extends core.ProviderCrypto {
|
||||||
privateKey: ["decrypt", "unwrapKey"] as core.KeyUsages,
|
privateKey: ["decrypt", "unwrapKey"] as core.KeyUsages,
|
||||||
};
|
};
|
||||||
|
|
||||||
public async onGenerateKey(algorithm: RsaKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair> {
|
public async onGenerateKey(algorithm: RsaKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKeyPair> {
|
||||||
const keys = await RsaCrypto.generateKey(
|
const keys = await RsaCrypto.generateKey(
|
||||||
{
|
{
|
||||||
...algorithm,
|
...algorithm,
|
||||||
|
|
|
@ -15,7 +15,7 @@ import { RsaPublicKey } from "./public_key";
|
||||||
|
|
||||||
export class RsaOaepProvider extends core.RsaOaepProvider {
|
export class RsaOaepProvider extends core.RsaOaepProvider {
|
||||||
|
|
||||||
public async onGenerateKey(algorithm: RsaHashedKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair> {
|
public async onGenerateKey(algorithm: RsaHashedKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKeyPair> {
|
||||||
const keys = await RsaCrypto.generateKey(
|
const keys = await RsaCrypto.generateKey(
|
||||||
{
|
{
|
||||||
...algorithm,
|
...algorithm,
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { RsaPublicKey } from "./public_key";
|
||||||
|
|
||||||
export class RsaPssProvider extends core.RsaPssProvider {
|
export class RsaPssProvider extends core.RsaPssProvider {
|
||||||
|
|
||||||
public async onGenerateKey(algorithm: RsaHashedKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair> {
|
public async onGenerateKey(algorithm: RsaHashedKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKeyPair> {
|
||||||
const keys = await RsaCrypto.generateKey(
|
const keys = await RsaCrypto.generateKey(
|
||||||
{
|
{
|
||||||
...algorithm,
|
...algorithm,
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { RsaPublicKey } from "./public_key";
|
||||||
|
|
||||||
export class RsaSsaProvider extends core.RsaSsaProvider {
|
export class RsaSsaProvider extends core.RsaSsaProvider {
|
||||||
|
|
||||||
public async onGenerateKey(algorithm: RsaHashedKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair> {
|
public async onGenerateKey(algorithm: RsaHashedKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<core.CryptoKeyPair> {
|
||||||
const keys = await RsaCrypto.generateKey(
|
const keys = await RsaCrypto.generateKey(
|
||||||
{
|
{
|
||||||
...algorithm,
|
...algorithm,
|
||||||
|
|
125
test/crypto.ts
125
test/crypto.ts
|
@ -1,6 +1,7 @@
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
import process from "process";
|
import process from "process";
|
||||||
import { WebcryptoTest } from "@peculiar/webcrypto-test";
|
import { WebcryptoTest } from "@peculiar/webcrypto-test";
|
||||||
|
import { Convert } from "pvtsutils";
|
||||||
import * as core from "webcrypto-core";
|
import * as core from "webcrypto-core";
|
||||||
import { Crypto } from "../src";
|
import { Crypto } from "../src";
|
||||||
|
|
||||||
|
@ -12,7 +13,6 @@ const crypto = new Crypto();
|
||||||
WebcryptoTest.check(crypto as any, {});
|
WebcryptoTest.check(crypto as any, {});
|
||||||
context("Crypto", () => {
|
context("Crypto", () => {
|
||||||
|
|
||||||
|
|
||||||
context("getRandomValues", () => {
|
context("getRandomValues", () => {
|
||||||
|
|
||||||
it("Uint8Array", () => {
|
it("Uint8Array", () => {
|
||||||
|
@ -63,47 +63,48 @@ context("Crypto", () => {
|
||||||
|
|
||||||
(nodeMajorVersion < 14 ? context.skip : context)("EdDSA", () => {
|
(nodeMajorVersion < 14 ? context.skip : context)("EdDSA", () => {
|
||||||
|
|
||||||
context("generateKey", () => {
|
context("generateKey", () => {
|
||||||
|
|
||||||
it("Ed25519", async () => {
|
it("Ed25519", async () => {
|
||||||
const keys = await crypto.subtle.generateKey({ name: "eddsa", namedCurve: "ed25519" } as globalThis.EcKeyGenParams, false, ["sign", "verify"]) as CryptoKeyPair;
|
const keys = await crypto.subtle.generateKey({ name: "eddsa", namedCurve: "ed25519" } as globalThis.EcKeyGenParams, false, ["sign", "verify"]);
|
||||||
assert.strictEqual(keys.privateKey.algorithm.name, "EdDSA");
|
|
||||||
assert.strictEqual((keys.privateKey.algorithm as EcKeyAlgorithm).namedCurve, "Ed25519");
|
assert.strictEqual(keys.privateKey.algorithm.name, "EdDSA");
|
||||||
});
|
assert.strictEqual((keys.privateKey.algorithm as EcKeyAlgorithm).namedCurve, "Ed25519");
|
||||||
|
});
|
||||||
|
|
||||||
it("Ed448", async () => {
|
it("Ed448", async () => {
|
||||||
const keys = await crypto.subtle.generateKey({ name: "eddsa", namedCurve: "ed448" } as globalThis.EcKeyGenParams, true, ["sign", "verify"]) as CryptoKeyPair;
|
const keys = await crypto.subtle.generateKey({ name: "eddsa", namedCurve: "ed448" } as globalThis.EcKeyGenParams, true, ["sign", "verify"]);
|
||||||
assert.strictEqual(keys.privateKey.algorithm.name, "EdDSA");
|
assert.strictEqual(keys.privateKey.algorithm.name, "EdDSA");
|
||||||
assert.strictEqual((keys.privateKey.algorithm as EcKeyAlgorithm).namedCurve, "Ed448");
|
assert.strictEqual((keys.privateKey.algorithm as EcKeyAlgorithm).namedCurve, "Ed448");
|
||||||
|
|
||||||
const data = await crypto.subtle.exportKey("jwk", keys.privateKey);
|
const data = await crypto.subtle.exportKey("jwk", keys.privateKey);
|
||||||
assert.strictEqual(data.kty, "OKP");
|
assert.strictEqual(data.kty, "OKP");
|
||||||
assert.strictEqual(data.crv, "Ed448");
|
assert.strictEqual(data.crv, "Ed448");
|
||||||
assert.strictEqual(!!data.d, true);
|
assert.strictEqual(!!data.d, true);
|
||||||
const privateKey = await crypto.subtle.importKey("jwk", data, { name: "eddsa", namedCurve: "ed448" } as EcKeyImportParams, false, ["sign"]);
|
const privateKey = await crypto.subtle.importKey("jwk", data, { name: "eddsa", namedCurve: "ed448" } as EcKeyImportParams, false, ["sign"]);
|
||||||
|
|
||||||
const message = Buffer.from("message");
|
|
||||||
const signature = await crypto.subtle.sign({ name: "EdDSA" }, privateKey, message);
|
|
||||||
const ok = await crypto.subtle.verify({ name: "EdDSA" }, keys.publicKey, signature, message);
|
|
||||||
assert.strictEqual(ok, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
const message = Buffer.from("message");
|
||||||
|
const signature = await crypto.subtle.sign({ name: "EdDSA" }, privateKey, message);
|
||||||
|
const ok = await crypto.subtle.verify({ name: "EdDSA" }, keys.publicKey, signature, message);
|
||||||
|
assert.strictEqual(ok, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
(nodeMajorVersion < 14 ? context.skip : context)("ECDH-ES", () => {
|
});
|
||||||
|
|
||||||
|
(nodeMajorVersion < 14 ? context.skip : context)("ECDH-ES", () => {
|
||||||
|
|
||||||
context("generateKey", () => {
|
context("generateKey", () => {
|
||||||
|
|
||||||
it("X25519", async () => {
|
it("X25519", async () => {
|
||||||
const keys = await crypto.subtle.generateKey({ name: "ecdh-es", namedCurve: "x25519" } as globalThis.EcKeyGenParams, false, ["deriveBits", "deriveKey"]) as CryptoKeyPair;
|
const keys = await crypto.subtle.generateKey({ name: "ecdh-es", namedCurve: "x25519" } as globalThis.EcKeyGenParams, false, ["deriveBits", "deriveKey"]);
|
||||||
assert.strictEqual(keys.privateKey.algorithm.name, "ECDH-ES");
|
assert.strictEqual(keys.privateKey.algorithm.name, "ECDH-ES");
|
||||||
assert.strictEqual((keys.privateKey.algorithm as EcKeyAlgorithm).namedCurve, "X25519");
|
assert.strictEqual((keys.privateKey.algorithm as EcKeyAlgorithm).namedCurve, "X25519");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("X448", async () => {
|
it("X448", async () => {
|
||||||
const keys = await crypto.subtle.generateKey({ name: "ecdh-es", namedCurve: "x448" } as globalThis.EcKeyGenParams, true, ["deriveBits", "deriveKey"]) as CryptoKeyPair;
|
const keys = await crypto.subtle.generateKey({ name: "ecdh-es", namedCurve: "x448" } as globalThis.EcKeyGenParams, true, ["deriveBits", "deriveKey"]);
|
||||||
assert.strictEqual(keys.privateKey.algorithm.name, "ECDH-ES");
|
assert.strictEqual(keys.privateKey.algorithm.name, "ECDH-ES");
|
||||||
assert.strictEqual((keys.privateKey.algorithm as EcKeyAlgorithm).namedCurve, "X448");
|
assert.strictEqual((keys.privateKey.algorithm as EcKeyAlgorithm).namedCurve, "X448");
|
||||||
|
|
||||||
|
@ -120,4 +121,78 @@ context("Crypto", () => {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
context("Extra ECC named curves", () => {
|
||||||
|
const namedCurves = [
|
||||||
|
"brainpoolP160r1",
|
||||||
|
"brainpoolP160t1",
|
||||||
|
"brainpoolP192r1",
|
||||||
|
"brainpoolP192t1",
|
||||||
|
"brainpoolP224r1",
|
||||||
|
"brainpoolP224t1",
|
||||||
|
"brainpoolP256r1",
|
||||||
|
"brainpoolP256t1",
|
||||||
|
"brainpoolP320r1",
|
||||||
|
"brainpoolP320t1",
|
||||||
|
"brainpoolP384r1",
|
||||||
|
"brainpoolP384t1",
|
||||||
|
"brainpoolP512r1",
|
||||||
|
"brainpoolP512t1",
|
||||||
|
];
|
||||||
|
|
||||||
|
context("sign/verify + pkcs8/spki", () => {
|
||||||
|
const data = new Uint8Array(10);
|
||||||
|
|
||||||
|
namedCurves.forEach((namedCurve) => {
|
||||||
|
it(namedCurve, async () => {
|
||||||
|
const alg: EcKeyGenParams = { name: "ECDSA", namedCurve };
|
||||||
|
const signAlg = { ...alg, hash: "SHA-256" } as EcdsaParams;
|
||||||
|
|
||||||
|
const keys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
|
||||||
|
|
||||||
|
const signature = await crypto.subtle.sign(signAlg, keys.privateKey, data);
|
||||||
|
|
||||||
|
const ok = await crypto.subtle.verify(signAlg, keys.publicKey, signature, data);
|
||||||
|
assert.ok(ok);
|
||||||
|
|
||||||
|
const pkcs8 = await crypto.subtle.exportKey("pkcs8", keys.privateKey);
|
||||||
|
const spki = await crypto.subtle.exportKey("spki", keys.publicKey);
|
||||||
|
|
||||||
|
const privateKey = await crypto.subtle.importKey("pkcs8", pkcs8, alg, true, ["sign"]);
|
||||||
|
const publicKey = await crypto.subtle.importKey("spki", spki, alg, true, ["verify"]);
|
||||||
|
|
||||||
|
const signature2 = await crypto.subtle.sign(signAlg, privateKey, data);
|
||||||
|
const ok2 = await crypto.subtle.verify(signAlg, keys.publicKey, signature2, data);
|
||||||
|
assert.ok(ok2);
|
||||||
|
|
||||||
|
const ok3 = await crypto.subtle.verify(signAlg, publicKey, signature, data);
|
||||||
|
assert.ok(ok3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context("deriveBits + jwk", () => {
|
||||||
|
namedCurves.forEach((namedCurve) => {
|
||||||
|
it(namedCurve, async () => {
|
||||||
|
const alg: EcKeyGenParams = { name: "ECDH", namedCurve };
|
||||||
|
|
||||||
|
const keys = await crypto.subtle.generateKey(alg, true, ["deriveBits", "deriveKey"]);
|
||||||
|
|
||||||
|
const deriveAlg: EcdhKeyDeriveParams = { name: "ECDH", public: keys.publicKey };
|
||||||
|
const derivedBits = await crypto.subtle.deriveBits(deriveAlg, keys.privateKey, 128);
|
||||||
|
|
||||||
|
const privateJwk = await crypto.subtle.exportKey("jwk", keys.privateKey);
|
||||||
|
const publicJwk = await crypto.subtle.exportKey("jwk", keys.publicKey);
|
||||||
|
const privateKey = await crypto.subtle.importKey("jwk", privateJwk, alg, true, ["deriveBits"]);
|
||||||
|
const publicKey = await crypto.subtle.importKey("jwk", publicJwk, alg, true, []);
|
||||||
|
|
||||||
|
const derivedBits2 = await crypto.subtle.deriveBits({ name: "ECDH", public: keys.publicKey } as EcdhKeyDeriveParams, privateKey, 128);
|
||||||
|
const derivedBits3 = await crypto.subtle.deriveBits({ name: "ECDH", public: publicKey } as EcdhKeyDeriveParams, keys.privateKey, 128);
|
||||||
|
|
||||||
|
assert.strictEqual(Convert.ToHex(derivedBits2), Convert.ToHex(derivedBits));
|
||||||
|
assert.strictEqual(Convert.ToHex(derivedBits3), Convert.ToHex(derivedBits));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
Reference in New Issue