add web package

This commit is contained in:
microshine 2022-05-24 15:06:18 +03:00
parent 5a5412ecd3
commit 0bd5151604
82 changed files with 6111 additions and 4 deletions

View File

@ -4,5 +4,5 @@ require:
extension:
- ts
spec:
- "packages/**/*.ts"
- "packages/**/*.spec.ts"
exit: true

11
packages/web/README.md Normal file
View File

@ -0,0 +1,11 @@
# `@peculiar/webcrypto-web`
> TODO: description
## Usage
```
const webcryptoWeb = require('@peculiar/webcrypto-web');
// TODO: DEMONSTRATE API
```

44
packages/web/package.json Normal file
View File

@ -0,0 +1,44 @@
{
"name": "@peculiar/webcrypto-web",
"version": "3.0.0",
"description": "> TODO: description",
"author": "microshine <microshine@mail.ru>",
"homepage": "https://github.com/PeculiarVentures/webcrypto/tree/master/packages/webcrypto-types#readme",
"license": "MIT",
"types": "src/index.d.ts",
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"build",
"README.md",
"LICENSE"
],
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/PeculiarVentures/webcrypto.git"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1"
},
"bugs": {
"url": "https://github.com/PeculiarVentures/webcrypto/issues"
},
"dependencies": {
"@peculiar/asn1-schema": "^2.1.6",
"@peculiar/json-schema": "^1.1.12",
"@peculiar/webcrypto-core": "^3.0.0",
"@peculiar/webcrypto-types": "^3.0.0",
"@stablelib/sha3": "^1.0.1",
"asmcrypto.js": "^2.3.2",
"asn1js": "^3.0.5",
"des.js": "^1.0.1",
"elliptic": "https://github.com/mahrud/elliptic",
"pvtsutils": "^1.3.2",
"tslib": "^2.4.0"
}
}

View File

@ -0,0 +1,17 @@
import * as core from "@peculiar/webcrypto-core";
import { nativeCrypto } from "./native";
import { SubtleCrypto } from "./subtle";
export class Crypto extends core.Crypto {
public get nativeCrypto() {
return nativeCrypto;
}
public subtle = new SubtleCrypto();
getRandomValues<T extends ArrayBufferView | null>(array: T): T {
return nativeCrypto.getRandomValues(array as any);
}
}

44
packages/web/src/debug.ts Normal file
View File

@ -0,0 +1,44 @@
declare const self: any;
export class Debug {
public static get enabled() {
return typeof self !== "undefined" && (self as any).PV_WEBCRYPTO_LINER_LOG;
}
public static log(message?: any, ...optionalParams: any[]): void;
public static log(...args: any[]) {
if (this.enabled) {
console.log.apply(console, args);
}
}
public static error(message?: any, ...optionalParams: any[]): void;
public static error(...args: any[]) {
if (this.enabled) {
console.error.apply(console, args);
}
}
public static info(message?: any, ...optionalParams: any[]): void;
public static info(...args: any[]) {
if (this.enabled) {
console.info.apply(console, args);
}
}
public static warn(message?: any, ...optionalParams: any[]): void;
public static warn(...args: any[]) {
if (this.enabled) {
console.warn.apply(console, args);
}
}
public static trace(message?: any, ...optionalParams: any[]): void;
public static trace(...args: any[]) {
if (this.enabled) {
console.trace.apply(console, args);
}
}
}

View File

@ -0,0 +1,8 @@
import { CryptoError } from "@peculiar/webcrypto-core";
export class LinerError extends CryptoError {
public static MODULE_NOT_FOUND = "Module '%1' is not found. Download it from %2";
public static UNSUPPORTED_ALGORITHM = "Unsupported algorithm '%1'";
public code = 10;
}

View File

@ -0,0 +1,95 @@
declare const self: any;
export enum Browser {
Unknown = "Unknown",
IE = "Internet Explorer",
Safari = "Safari",
Edge = "Edge",
Chrome = "Chrome",
Firefox = "Firefox Mozilla",
Mobile = "Mobile",
}
export interface IBrowserInfo {
name: Browser;
version: string;
}
/**
* Returns info about browser
*/
export function BrowserInfo() {
const res: IBrowserInfo = {
name: Browser.Unknown,
version: "0",
};
if (typeof self === "undefined") {
return res;
}
const userAgent = self.navigator.userAgent;
let reg: string[] | null;
if (reg = /edge\/([\d\.]+)/i.exec(userAgent)) {
res.name = Browser.Edge;
res.version = reg[1];
} else if (/msie/i.test(userAgent)) {
res.name = Browser.IE;
res.version = /msie ([\d\.]+)/i.exec(userAgent)![1];
} else if (/Trident/i.test(userAgent)) {
res.name = Browser.IE;
res.version = /rv:([\d\.]+)/i.exec(userAgent)![1];
} else if (/chrome/i.test(userAgent)) {
res.name = Browser.Chrome;
res.version = /chrome\/([\d\.]+)/i.exec(userAgent)![1];
} else if (/firefox/i.test(userAgent)) {
res.name = Browser.Firefox;
res.version = /firefox\/([\d\.]+)/i.exec(userAgent)![1];
} else if (/mobile/i.test(userAgent)) {
res.name = Browser.Mobile;
res.version = /mobile\/([\w]+)/i.exec(userAgent)![1];
} else if (/safari/i.test(userAgent)) {
res.name = Browser.Safari;
res.version = /version\/([\d\.]+)/i.exec(userAgent)![1];
}
return res;
}
export function string2buffer(binaryString: string) {
const res = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
res[i] = binaryString.charCodeAt(i);
}
return res;
}
export function buffer2string(buffer: Uint8Array) {
let res = "";
for (let i = 0; i < buffer.length; i++) {
res += String.fromCharCode(buffer[i]);
}
return res;
}
export function concat(...buf: Uint8Array[]) {
const res = new Uint8Array(buf.map((item) => item.length).reduce((prev, cur) => prev + cur));
let offset = 0;
buf.forEach((item, index) => {
for (let i = 0; i < item.length; i++) {
res[offset + i] = item[i];
}
offset += item.length;
});
return res;
}
export function assign(target: any, ...sources: any[]): any;
export function assign(...args: any[]) {
const res = args[0];
for (let i = 1; i < args.length; i++) {
const obj = args[i];
for (const prop in obj) {
res[prop] = obj[prop];
}
}
return res;
}

View File

@ -0,0 +1,3 @@
export * from "./native";
export * from "./crypto";
export * from "./key";

45
packages/web/src/init.ts Normal file
View File

@ -0,0 +1,45 @@
import { nativeSubtle } from "./native";
declare const self: any;
function WrapFunction(subtle: any, name: string) {
const fn = subtle[name];
subtle[name] = function () {
const args = arguments;
return new Promise((resolve, reject) => {
const op: any = fn.apply(subtle, args);
op.oncomplete = (e: any) => {
resolve(e.target.result);
};
op.onerror = (e: any) => {
reject(`Error on running '${name}' function`);
};
});
};
}
if (typeof self !== "undefined" && self["msCrypto"]) {
WrapFunction(nativeSubtle, "generateKey");
WrapFunction(nativeSubtle, "digest");
WrapFunction(nativeSubtle, "sign");
WrapFunction(nativeSubtle, "verify");
WrapFunction(nativeSubtle, "encrypt");
WrapFunction(nativeSubtle, "decrypt");
WrapFunction(nativeSubtle, "importKey");
WrapFunction(nativeSubtle, "exportKey");
WrapFunction(nativeSubtle, "wrapKey");
WrapFunction(nativeSubtle, "unwrapKey");
WrapFunction(nativeSubtle, "deriveKey");
WrapFunction(nativeSubtle, "deriveBits");
}
// fix: Math.imul for IE
if (!(Math as any).imul) {
(Math as any).imul = function imul(a: number, b: number) {
const ah = (a >>> 16) & 0xffff;
const al = a & 0xffff;
const bh = (b >>> 16) & 0xffff;
const bl = b & 0xffff;
return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0) | 0);
};
}

15
packages/web/src/key.ts Normal file
View File

@ -0,0 +1,15 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
export class CryptoKey extends core.CryptoKey {
public override algorithm: types.KeyAlgorithm;
constructor(
algorithm: types.KeyAlgorithm,
public override extractable: boolean,
public override type: types.KeyType,
public override usages: types.KeyUsage[],
) {
super();
this.algorithm = { ...algorithm };
}
}

9
packages/web/src/lib.ts Normal file
View File

@ -0,0 +1,9 @@
import { Crypto, nativeCrypto } from ".";
import "./init";
if (nativeCrypto) {
Object.freeze(nativeCrypto.getRandomValues);
}
export const crypto = new Crypto();
export * from ".";

View File

@ -0,0 +1,33 @@
import * as core from "@peculiar/webcrypto-core";
import { AesKeyGenParams, KeyUsage, CryptoKey, AesCbcParams, KeyFormat, JsonWebKey, Algorithm } from "@peculiar/webcrypto-types";
import { AesCrypto } from "./crypto";
import { AesCryptoKey } from "./key";
export class AesCbcProvider extends core.AesCbcProvider {
public async onGenerateKey(algorithm: AesKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<AesCryptoKey> {
return AesCrypto.generateKey(algorithm, extractable, keyUsages);
}
public async onEncrypt(algorithm: AesCbcParams, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return AesCrypto.encrypt(algorithm, key, data);
}
public async onDecrypt(algorithm: AesCbcParams, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return AesCrypto.decrypt(algorithm, key, data);
}
public async onExportKey(format: KeyFormat, key: AesCryptoKey): Promise<JsonWebKey | ArrayBuffer> {
return AesCrypto.exportKey(format, key);
}
public onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: Algorithm, extractable: boolean, keyUsages: KeyUsage[], ...args: any[]): Promise<AesCryptoKey> {
return AesCrypto.importKey(format, keyData, algorithm, extractable, keyUsages);
}
public override checkCryptoKey(key: CryptoKey, keyUsage: KeyUsage): asserts key is AesCryptoKey {
super.checkCryptoKey(key, keyUsage);
AesCrypto.checkCryptoKey(key);
}
}

View File

@ -0,0 +1,38 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import * as pvtsutils from "pvtsutils";
import * as asmCrypto from "asmcrypto.js";
import { AesCrypto } from "./crypto";
import { AesCryptoKey } from "./key";
export class AesCtrProvider extends core.AesCtrProvider {
public async onEncrypt(algorithm: types.AesCtrParams, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
const result = new asmCrypto.AES_CTR(key.raw, pvtsutils.BufferSourceConverter.toUint8Array(algorithm.counter))
.encrypt(pvtsutils.BufferSourceConverter.toUint8Array(data));
return pvtsutils.BufferSourceConverter.toArrayBuffer(result);
}
public async onDecrypt(algorithm: types.AesCtrParams, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
const result = new asmCrypto.AES_CTR(key.raw, pvtsutils.BufferSourceConverter.toUint8Array(algorithm.counter))
.decrypt(pvtsutils.BufferSourceConverter.toUint8Array(data));
return pvtsutils.BufferSourceConverter.toArrayBuffer(result);
}
public async onGenerateKey(algorithm: types.AesKeyGenParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<AesCryptoKey> {
return AesCrypto.generateKey(algorithm, extractable, keyUsages);
}
public async onExportKey(format: types.KeyFormat, key: AesCryptoKey): Promise<ArrayBuffer | types.JsonWebKey> {
return AesCrypto.exportKey(format, key);
}
public async onImportKey(format: types.KeyFormat, keyData: ArrayBuffer | types.JsonWebKey, algorithm: types.Algorithm, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<AesCryptoKey> {
return AesCrypto.importKey(format, keyData, algorithm, extractable, keyUsages);
}
public override checkCryptoKey(key: types.CryptoKey, keyUsage: types.KeyUsage): asserts key is AesCryptoKey {
super.checkCryptoKey(key, keyUsage);
AesCrypto.checkCryptoKey(key);
}
}

View File

@ -0,0 +1,33 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import { AesCrypto } from "./crypto";
import { AesCryptoKey } from "./key";
export class AesEcbProvider extends core.AesEcbProvider {
public async onGenerateKey(algorithm: types.AesKeyGenParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<AesCryptoKey> {
return AesCrypto.generateKey(algorithm, extractable, keyUsages);
}
public async onEncrypt(algorithm: types.Algorithm, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return AesCrypto.encrypt(algorithm, key, data);
}
public async onDecrypt(algorithm: types.Algorithm, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return AesCrypto.decrypt(algorithm, key, data);
}
public async onExportKey(format: types.KeyFormat, key: AesCryptoKey): Promise<types.JsonWebKey | ArrayBuffer> {
return AesCrypto.exportKey(format, key);
}
public async onImportKey(format: types.KeyFormat, keyData: types.JsonWebKey | ArrayBuffer, algorithm: types.Algorithm, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<AesCryptoKey> {
return AesCrypto.importKey(format, keyData, algorithm, extractable, keyUsages);
}
public override checkCryptoKey(key: types.CryptoKey, keyUsage: types.KeyUsage) {
super.checkCryptoKey(key, keyUsage);
AesCrypto.checkCryptoKey(key);
}
}

View File

@ -0,0 +1,33 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import { AesCrypto } from "./crypto";
import { AesCryptoKey } from "./key";
export class AesGcmProvider extends core.AesGcmProvider {
public async onGenerateKey(algorithm: types.AesKeyGenParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<AesCryptoKey> {
return AesCrypto.generateKey(algorithm, extractable, keyUsages);
}
public async onEncrypt(algorithm: types.AesGcmParams, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return AesCrypto.encrypt(algorithm, key, data);
}
public async onDecrypt(algorithm: types.AesGcmParams, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return AesCrypto.decrypt(algorithm, key, data);
}
public async onExportKey(format: types.KeyFormat, key: AesCryptoKey): Promise<types.JsonWebKey | ArrayBuffer> {
return AesCrypto.exportKey(format, key);
}
public async onImportKey(format: types.KeyFormat, keyData: types.JsonWebKey | ArrayBuffer, algorithm: types.Algorithm, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<AesCryptoKey> {
return AesCrypto.importKey(format, keyData, algorithm, extractable, keyUsages);
}
public override checkCryptoKey(key: types.CryptoKey, keyUsage: types.KeyUsage): asserts key is AesCryptoKey {
super.checkCryptoKey(key, keyUsage);
AesCrypto.checkCryptoKey(key);
}
}

View File

@ -0,0 +1,27 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import { AesCrypto } from "./crypto";
import { AesCryptoKey } from "./key";
export class AesKwProvider extends core.AesKwProvider {
public override async onEncrypt(algorithm: types.Algorithm, key: types.CryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
throw new Error("Method not implemented.");
}
public override async onDecrypt(algorithm: types.Algorithm, key: types.CryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
throw new Error("Method not implemented.");
}
public async onGenerateKey(algorithm: types.AesKeyGenParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<AesCryptoKey> {
throw new Error("Method not implemented.");
}
public async onExportKey(format: types.KeyFormat, key: types.CryptoKey): Promise<ArrayBuffer | types.JsonWebKey> {
throw new Error("Method not implemented.");
}
public async onImportKey(format: types.KeyFormat, keyData: ArrayBuffer | types.JsonWebKey, algorithm: types.Algorithm, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<AesCryptoKey> {
throw new Error("Method not implemented.");
}
public override checkCryptoKey(key: types.CryptoKey, keyUsage: types.KeyUsage): asserts key is AesCryptoKey {
super.checkCryptoKey(key, keyUsage);
AesCrypto.checkCryptoKey(key);
}
}

View File

@ -0,0 +1,99 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import * as asmCrypto from "asmcrypto.js";
import * as pvtsutils from "pvtsutils";
import { nativeCrypto } from "../../native";
import { isAlgorithm } from "../../utils";
import { AesCryptoKey } from "./key";
export class AesCrypto {
public static AesCBC = "AES-CBC";
public static AesECB = "AES-ECB";
public static AesGCM = "AES-GCM";
public static checkCryptoKey(key: any) {
if (!(key instanceof AesCryptoKey)) {
throw new TypeError("key: Is not AesCryptoKey");
}
}
public static async generateKey(algorithm: types.AesKeyGenParams, extractable: boolean, usages: types.KeyUsage[]) {
// gat random bytes for key
const raw = nativeCrypto.getRandomValues(new Uint8Array(algorithm.length / 8));
return new AesCryptoKey(algorithm, extractable, usages, raw);
}
public static async encrypt(algorithm: types.Algorithm, key: AesCryptoKey, data: ArrayBuffer) {
return this.cipher(algorithm, key, pvtsutils.BufferSourceConverter.toUint8Array(data), true);
}
public static async decrypt(algorithm: types.Algorithm, key: AesCryptoKey, data: ArrayBuffer) {
return this.cipher(algorithm, key, pvtsutils.BufferSourceConverter.toUint8Array(data), false);
}
public static async exportKey(format: string, key: AesCryptoKey): Promise<types.JsonWebKey | ArrayBuffer> {
switch (format) {
case "jwk":
return key.toJSON();
case "raw":
return key.raw.buffer;
default:
throw new core.OperationError("format: Must be 'jwk' or 'raw'");
}
}
public static async importKey(format: string, keyData: types.JsonWebKey | ArrayBuffer, algorithm: types.Algorithm, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<AesCryptoKey> {
let raw: ArrayBuffer;
if (core.isJWK(keyData)) {
if (!keyData.k) {
throw new core.RequiredPropertyError("k", "JWK");
}
raw = pvtsutils.Convert.FromBase64Url(keyData.k);
} else {
raw = pvtsutils.BufferSourceConverter.toArrayBuffer(keyData);
}
// check key length
switch (raw.byteLength << 3) {
case 128:
case 192:
case 256:
break;
default:
throw new core.OperationError("keyData: Is wrong key length");
}
const key = new AesCryptoKey({ name: algorithm.name, length: raw.byteLength << 3 }, extractable, keyUsages, new Uint8Array(raw));
return key;
}
private static async cipher(algorithm: types.Algorithm, key: AesCryptoKey, data: Uint8Array, encrypt: boolean) {
const action = encrypt ? "encrypt" : "decrypt";
let result: Uint8Array;
if (isAlgorithm<types.AesCbcParams>(algorithm, AesCrypto.AesCBC)) {
// AES-CBC
const iv = pvtsutils.BufferSourceConverter.toUint8Array(algorithm.iv);
result = asmCrypto.AES_CBC[action](data, key.raw, undefined, iv);
} else if (isAlgorithm<types.AesGcmParams>(algorithm, AesCrypto.AesGCM)) {
// AES-GCM
const iv = pvtsutils.BufferSourceConverter.toUint8Array(algorithm.iv);
let additionalData;
if (algorithm.additionalData) {
additionalData = pvtsutils.BufferSourceConverter.toUint8Array(algorithm.additionalData);
}
const tagLength = (algorithm.tagLength || 128) / 8;
result = asmCrypto.AES_GCM[action](data, key.raw, iv, additionalData, tagLength);
} else if (isAlgorithm<types.Algorithm>(algorithm, AesCrypto.AesECB)) {
// // AES-ECB
result = asmCrypto.AES_ECB[action](data, key.raw, true);
} else {
throw new core.OperationError(`algorithm: Is not recognized`);
}
return pvtsutils.BufferSourceConverter.toArrayBuffer(result);
}
}

View File

@ -0,0 +1,6 @@
export * from "./crypto";
export * from "./aes_cbc";
export * from "./aes_ecb";
export * from "./aes_gcm";
export * from "./aes_ctr";
export * from "./aes_kw";

View File

@ -0,0 +1,38 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import * as pvtsutils from "pvtsutils";
import { CryptoKey } from "../../key";
export class AesCryptoKey extends CryptoKey {
declare public algorithm: types.AesKeyAlgorithm;
constructor(algorithm: types.AesKeyAlgorithm, extractable: boolean, usages: types.KeyUsage[], public raw: Uint8Array) {
super(algorithm, extractable, "secret", usages);
}
public toJSON() {
const jwk: types.JsonWebKey = {
kty: "oct",
alg: this.getJwkAlgorithm(),
k: pvtsutils.Convert.ToBase64Url(this.raw),
ext: this.extractable,
key_ops: this.usages,
};
return jwk;
}
private getJwkAlgorithm() {
switch (this.algorithm.name.toUpperCase()) {
case "AES-CBC":
return `A${this.algorithm.length}CBC`;
case "AES-CTR":
return `A${this.algorithm.length}CTR`;
case "AES-GCM":
return `A${this.algorithm.length}GCM`;
case "AES-ECB":
return `A${this.algorithm.length}ECB`;
default:
throw new core.AlgorithmError("Unsupported algorithm name");
}
}
}

View File

@ -0,0 +1,107 @@
/// <reference path="../../typings/des.d.ts" />
import * as core from "@peculiar/webcrypto-core";
import { RequiredPropertyError } from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import * as des from "des.js";
import * as pvtsutils from "pvtsutils";
import { nativeCrypto } from "../../native";
import { DesCryptoKey } from "./key";
export class DesCrypto {
public static checkLib() {
if (typeof (des) === "undefined") {
throw new core.OperationError("Cannot implement DES mechanism. Add 'https://peculiarventures.github.io/pv-webcrypto-tests/src/des.js' script to your project");
}
}
public static checkCryptoKey(key: any) {
if (!(key instanceof DesCryptoKey)) {
throw new TypeError("key: Is not DesCryptoKey");
}
}
public static async generateKey(algorithm: types.DesKeyGenParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<DesCryptoKey> {
this.checkLib();
// gat random bytes for key
const raw = nativeCrypto.getRandomValues(new Uint8Array(algorithm.length / 8));
return new DesCryptoKey(algorithm, extractable, keyUsages, raw);
}
public static async exportKey(format: types.KeyFormat, key: DesCryptoKey): Promise<types.JsonWebKey | ArrayBuffer> {
this.checkLib();
switch (format) {
case "jwk":
return key.toJSON();
case "raw":
return key.raw.buffer;
default:
throw new core.OperationError("format: Must be 'jwk' or 'raw'");
}
}
public static async importKey(format: string, keyData: types.JsonWebKey | ArrayBuffer, algorithm: types.DesImportParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<DesCryptoKey> {
this.checkLib();
let raw: ArrayBuffer;
if (core.isJWK(keyData)) {
if (!keyData.k) {
throw new RequiredPropertyError("k", "JWK");
}
raw = pvtsutils.Convert.FromBase64Url(keyData.k);
} else {
raw = pvtsutils.BufferSourceConverter.toArrayBuffer(keyData);
}
// check key length
if ((algorithm.name === "DES-CBC" && raw.byteLength !== 8)
|| (algorithm.name === "DES-EDE3-CBC" && raw.byteLength !== 24)) {
throw new core.OperationError("keyData: Is wrong key length");
}
const key = new DesCryptoKey({ name: algorithm.name, length: raw.byteLength << 3 }, extractable, keyUsages, new Uint8Array(raw));
return key;
}
public static async encrypt(algorithm: types.DesParams, key: DesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return this.cipher(algorithm, key, data, true);
}
public static async decrypt(algorithm: types.DesParams, key: DesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return this.cipher(algorithm, key, data, false);
}
private static async cipher(algorithm: types.DesParams, key: DesCryptoKey, data: ArrayBuffer, encrypt: boolean): Promise<ArrayBuffer> {
this.checkLib();
const type = encrypt ? "encrypt" : "decrypt";
let DesCipher: des.Cipher;
const iv = pvtsutils.BufferSourceConverter.toUint8Array(algorithm.iv);
switch (algorithm.name.toUpperCase()) {
case "DES-CBC":
DesCipher = des.CBC.instantiate(des.DES).create({
key: key.raw,
type,
iv,
});
break;
case "DES-EDE3-CBC":
DesCipher = des.CBC.instantiate(des.EDE).create({
key: key.raw,
type,
iv,
});
break;
default:
throw new core.OperationError("algorithm: Is not recognized");
}
const enc = DesCipher.update(new Uint8Array(data)).concat(DesCipher.final());
return new Uint8Array(enc).buffer;
}
}

View File

@ -0,0 +1,37 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import { DesCrypto } from "./crypto";
import { DesCryptoKey } from "./key";
export class DesCbcProvider extends core.DesProvider {
public keySizeBits = 64;
public ivSize = 8;
public name = "DES-CBC";
public async onGenerateKey(algorithm: types.DesKeyGenParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<DesCryptoKey> {
return DesCrypto.generateKey(algorithm, extractable, keyUsages);
}
public async onExportKey(format: types.KeyFormat, key: DesCryptoKey): Promise<types.JsonWebKey | ArrayBuffer> {
return DesCrypto.exportKey(format, key);
}
public async onImportKey(format: types.KeyFormat, keyData: types.JsonWebKey | ArrayBuffer, algorithm: types.DesImportParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<DesCryptoKey> {
return DesCrypto.importKey(format, keyData, algorithm, extractable, keyUsages);
}
public async onEncrypt(algorithm: types.DesParams, key: DesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return DesCrypto.encrypt(algorithm, key, data);
}
public async onDecrypt(algorithm: types.DesParams, key: DesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return DesCrypto.decrypt(algorithm, key, data);
}
public override checkCryptoKey(key: types.CryptoKey, keyUsage: types.KeyUsage): asserts key is DesCryptoKey {
super.checkCryptoKey(key, keyUsage);
DesCrypto.checkCryptoKey(key);
}
}

View File

@ -0,0 +1,39 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import { DesCrypto } from "./crypto";
import { DesCryptoKey } from "./key";
export type DesEde3CbcParams = types.DesParams;
export class DesEde3CbcProvider extends core.DesProvider {
public keySizeBits = 192;
public ivSize = 8;
public name = "DES-EDE3-CBC";
public async onGenerateKey(algorithm: types.DesKeyGenParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<DesCryptoKey> {
return DesCrypto.generateKey(algorithm, extractable, keyUsages);
}
public async onExportKey(format: types.KeyFormat, key: DesCryptoKey): Promise<types.JsonWebKey | ArrayBuffer> {
return DesCrypto.exportKey(format, key);
}
public async onImportKey(format: types.KeyFormat, keyData: types.JsonWebKey | ArrayBuffer, algorithm: types.DesImportParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<DesCryptoKey> {
return DesCrypto.importKey(format, keyData, algorithm, extractable, keyUsages);
}
public async onEncrypt(algorithm: types.DesParams, key: DesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return DesCrypto.encrypt(algorithm, key, data);
}
public async onDecrypt(algorithm: types.DesParams, key: DesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return DesCrypto.decrypt(algorithm, key, data);
}
public override checkCryptoKey(key: DesCryptoKey, keyUsage: types.KeyUsage): asserts key is DesCryptoKey {
super.checkCryptoKey(key, keyUsage);
DesCrypto.checkCryptoKey(key);
}
}

View File

@ -0,0 +1,3 @@
export * from "./crypto";
export * from "./des_cbc";
export * from "./des_ede3_cbc";

View File

@ -0,0 +1,35 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import * as pvtsutils from "pvtsutils";
import { CryptoKey } from "../../key";
export class DesCryptoKey extends CryptoKey {
declare public algorithm: types.DesKeyAlgorithm;
constructor(algorithm: types.DesKeyAlgorithm, extractable: boolean, usages: types.KeyUsage[], public raw: Uint8Array) {
super(algorithm, extractable, "secret", usages);
}
public toJSON() {
const jwk: types.JsonWebKey = {
kty: "oct",
alg: this.getJwkAlgorithm(),
k: pvtsutils.Convert.ToBase64Url(this.raw),
ext: this.extractable,
key_ops: this.usages,
};
return jwk;
}
private getJwkAlgorithm() {
switch (this.algorithm.name.toUpperCase()) {
case "DES-CBC":
return `DES-CBC`;
case "DES-EDE3-CBC":
return `3DES-CBC`;
default:
throw new core.AlgorithmError("Unsupported algorithm name");
}
}
}

View File

@ -0,0 +1,230 @@
import { AsnConvert } from "@peculiar/asn1-schema";
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import * as elliptic from "elliptic";
import { concat } from "../../helper";
import { getOidByNamedCurve } from "./helper";
import { EcCryptoKey } from "./key";
export class EcCrypto {
public static privateUsages: types.KeyUsage[] = ["sign", "deriveKey", "deriveBits"];
public static publicUsages: types.KeyUsage[] = ["verify"];
public static readonly ASN_ALGORITHM = "1.2.840.10045.2.1";
public static checkLib() {
if (typeof (elliptic) === "undefined") {
throw new core.OperationError("Cannot implement EC mechanism. Add 'https://peculiarventures.github.io/pv-webcrypto-tests/src/elliptic.js' script to your project");
}
}
public static async generateKey(algorithm: types.EcKeyGenParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<types.CryptoKeyPair> {
this.checkLib();
const key = this.initEcKey(algorithm.namedCurve);
const ecKey = key.genKeyPair();
ecKey.getPublic(); // Fills internal `pub` field
// set key params
const prvKey = new EcCryptoKey(
{ ...algorithm },
extractable,
"private",
keyUsages.filter((usage) => ~this.privateUsages.indexOf(usage)),
ecKey,
);
const pubKey = new EcCryptoKey(
{ ...algorithm },
true,
"public",
keyUsages.filter((usage) => ~this.publicUsages.indexOf(usage)),
ecKey,
);
return {
privateKey: prvKey,
publicKey: pubKey,
};
}
public static checkCryptoKey(key: unknown) {
if (!(key instanceof EcCryptoKey)) {
throw new TypeError("key: Is not EcCryptoKey");
}
}
public static concat(...buf: Uint8Array[]) {
const res = new Uint8Array(buf.map((item) => item.length).reduce((prev, cur) => prev + cur));
let offset = 0;
buf.forEach((item, index) => {
for (let i = 0; i < item.length; i++) {
res[offset + i] = item[i];
}
offset += item.length;
});
return res;
}
public static async exportKey(format: types.KeyFormat, key: EcCryptoKey): Promise<types.JsonWebKey | ArrayBuffer> {
this.checkLib();
switch (format) {
case "pkcs8":
return this.exportPkcs8Key(key);
case "spki":
return this.exportSpkiKey(key);
case "jwk":
return this.exportJwkKey(key);
case "raw":
return new Uint8Array(key.data.getPublic("der")).buffer;
default:
throw new core.OperationError("format: Must be 'jwk', 'raw, 'pkcs8' or 'spki'");
}
}
public static async importKey(format: types.KeyFormat, keyData: types.JsonWebKey | ArrayBuffer, algorithm: types.EcKeyImportParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<EcCryptoKey> {
this.checkLib();
let ecKey: EllipticJS.EllipticKeyPair;
switch (format) {
case "pkcs8":
ecKey = this.importPkcs8Key(keyData as ArrayBuffer, algorithm.namedCurve);
break;
case "spki":
ecKey = this.importSpkiKey(keyData as ArrayBuffer, algorithm.namedCurve);
break;
case "raw":
ecKey = this.importEcKey(new core.asn1.EcPublicKey(keyData as ArrayBuffer), algorithm.namedCurve);
break;
case "jwk":
ecKey = this.importJwkKey(keyData as types.JsonWebKey);
break;
default:
throw new core.OperationError("format: Must be 'jwk', 'raw', 'pkcs8' or 'spki'");
}
const key = new EcCryptoKey(
{
...algorithm,
} as types.EcKeyAlgorithm,
extractable,
ecKey.priv ? "private" : "public",
keyUsages,
ecKey,
);
return key;
}
protected static getNamedCurve(wcNamedCurve: string) {
const crv = wcNamedCurve.toUpperCase();
let res = "";
if (["P-256", "P-384", "P-521"].indexOf(crv) > -1) {
res = crv.replace("-", "").toLowerCase();
} else if (crv === "K-256") {
res = "secp256k1";
} else if (["brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1"].includes(wcNamedCurve)) {
res = wcNamedCurve;
} else {
throw new core.OperationError(`Unsupported named curve '${wcNamedCurve}'`);
}
return res;
}
private static initEcKey(namedCurve: string) {
return elliptic.ec(this.getNamedCurve(namedCurve));
}
private static exportPkcs8Key(key: EcCryptoKey) {
const keyInfo = new core.asn1.PrivateKeyInfo();
keyInfo.privateKeyAlgorithm.algorithm = this.ASN_ALGORITHM;
keyInfo.privateKeyAlgorithm.parameters = AsnConvert.serialize(
new core.asn1.ObjectIdentifier(getOidByNamedCurve(key.algorithm.namedCurve)),
);
keyInfo.privateKey = AsnConvert.serialize(this.exportEcKey(key));
return AsnConvert.serialize(keyInfo);
}
private static importPkcs8Key(data: ArrayBuffer, namedCurve: string) {
const keyInfo = AsnConvert.parse(data, core.asn1.PrivateKeyInfo);
const privateKey = AsnConvert.parse(keyInfo.privateKey, core.asn1.EcPrivateKey);
return this.importEcKey(privateKey, namedCurve);
}
private static importSpkiKey(data: ArrayBuffer, namedCurve: string) {
const keyInfo = AsnConvert.parse(data, core.asn1.PublicKeyInfo);
const publicKey = new core.asn1.EcPublicKey(keyInfo.publicKey);
return this.importEcKey(publicKey, namedCurve);
}
private static exportSpkiKey(key: EcCryptoKey) {
const publicKey = new core.asn1.EcPublicKey(new Uint8Array(key.data.getPublic("der")).buffer);
const keyInfo = new core.asn1.PublicKeyInfo();
keyInfo.publicKeyAlgorithm.algorithm = this.ASN_ALGORITHM;
keyInfo.publicKeyAlgorithm.parameters = AsnConvert.serialize(
new core.asn1.ObjectIdentifier(getOidByNamedCurve(key.algorithm.namedCurve)),
);
keyInfo.publicKey = publicKey.value;
return AsnConvert.serialize(keyInfo);
}
private static importJwkKey(data: types.JsonWebKey) {
let key: core.asn1.EcPrivateKey | core.asn1.EcPublicKey;
if (data.d) {
// private
key = JsonParser.fromJSON(data, { targetSchema: core.asn1.EcPrivateKey });
} else {
// public
key = JsonParser.fromJSON(data, { targetSchema: core.asn1.EcPublicKey });
}
if (!data.crv) {
throw new Error();
}
return this.importEcKey(key, data.crv);
}
private static exportJwkKey(key: EcCryptoKey) {
const asnKey = this.exportEcKey(key);
const jwk = JsonSerializer.toJSON(asnKey) as types.JsonWebKey;
jwk.ext = true;
jwk.key_ops = key.usages;
jwk.crv = key.algorithm.namedCurve;
jwk.kty = "EC";
return jwk;
}
private static exportEcKey(ecKey: EcCryptoKey): core.asn1.EcPrivateKey | core.asn1.EcPublicKey {
if (ecKey.type === "private") {
// private
const privateKey = new core.asn1.EcPrivateKey();
const point = new Uint8Array(ecKey.data.getPrivate("der").toArray());
const pointPad = new Uint8Array(this.getPointSize(ecKey.algorithm.namedCurve) - point.length);
privateKey.privateKey = concat(pointPad, point);
privateKey.publicKey = new Uint8Array(ecKey.data.getPublic("der"));
return privateKey;
} else if (ecKey.data.pub) {
// public
return new core.asn1.EcPublicKey(new Uint8Array(ecKey.data.getPublic("der")).buffer);
} else {
throw new Error("Cannot get private or public key");
}
}
private static importEcKey(key: core.asn1.EcPrivateKey | core.asn1.EcPublicKey, namedCurve: string) {
const ecKey = this.initEcKey(namedCurve);
if (key instanceof core.asn1.EcPublicKey) {
return ecKey.keyFromPublic(new Uint8Array(key.value));
}
return ecKey.keyFromPrivate(new Uint8Array(key.privateKey));
}
private static getPointSize(namedCurve: string) {
return core.EcCurves.get(namedCurve).size + 7 >> 3;
}
}

View File

@ -0,0 +1,43 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import { EcCrypto } from "./crypto";
import { EcCryptoKey } from "./key";
export class EcdhProvider extends core.EcdhProvider {
public override namedCurves = ["P-256", "P-384", "P-521", "K-256", "brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1"];
public async onGenerateKey(algorithm: types.EcKeyGenParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<types.CryptoKeyPair> {
return EcCrypto.generateKey(algorithm, extractable, keyUsages);
}
public async onExportKey(format: types.KeyFormat, key: EcCryptoKey): Promise<ArrayBuffer | types.JsonWebKey> {
return EcCrypto.exportKey(format, key);
}
public async onImportKey(format: types.KeyFormat, keyData: ArrayBuffer | types.JsonWebKey, algorithm: types.EcKeyImportParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<EcCryptoKey> {
return EcCrypto.importKey(format, keyData, algorithm, extractable, keyUsages);
}
public async onDeriveBits(algorithm: types.EcdhKeyDeriveParams, baseKey: EcCryptoKey, length: number): Promise<ArrayBuffer> {
EcCrypto.checkLib();
const shared = baseKey.data.derive((algorithm.public as EcCryptoKey).data.getPublic());
let array = new Uint8Array(shared.toArray());
// Padding
let len = array.length;
len = (len > 32 ? (len > 48 ? 66 : 48) : 32);
if (array.length < len) {
array = EcCrypto.concat(new Uint8Array(len - array.length), array);
}
const buf = array.slice(0, length / 8).buffer;
return buf;
}
public override checkCryptoKey(key: EcCryptoKey, keyUsage: types.KeyUsage): asserts key is EcCryptoKey {
super.checkCryptoKey(key, keyUsage);
EcCrypto.checkCryptoKey(key);
}
}

View File

@ -0,0 +1,76 @@
import * as core from "@peculiar/webcrypto-core";
import { EcKeyGenParams, KeyUsage, CryptoKeyPair, KeyFormat, JsonWebKey, EcKeyImportParams, CryptoKey, EcdsaParams } from "@peculiar/webcrypto-types";
import { Crypto } from "../../crypto";
import { EcCrypto } from "./crypto";
import { EcCryptoKey } from "./key";
/**
* Converts buffer to number array
* @param buffer ArrayBuffer or ArrayBufferView
*/
export function b2a(buffer: ArrayBuffer | ArrayBufferView) {
const buf = new Uint8Array(buffer as ArrayBuffer);
const res: number[] = [];
for (let i = 0; i < buf.length; i++) {
res.push(buf[i]);
}
return res;
}
export class EcdsaProvider extends core.EcdsaProvider {
public override hashAlgorithms = ["SHA-1", "SHA-256", "SHA-384", "SHA-512", "SHA3-256", "SHA3-384", "SHA3-512"];
public override namedCurves = ["P-256", "P-384", "P-521", "K-256", "brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1"];
public async onGenerateKey(algorithm: EcKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair> {
return EcCrypto.generateKey(algorithm, extractable, keyUsages);
}
public async onExportKey(format: KeyFormat, key: EcCryptoKey): Promise<ArrayBuffer | JsonWebKey> {
return EcCrypto.exportKey(format, key);
}
public async onImportKey(format: KeyFormat, keyData: ArrayBuffer | JsonWebKey, algorithm: EcKeyImportParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<EcCryptoKey> {
return EcCrypto.importKey(format, keyData, algorithm, extractable, keyUsages);
}
public async onSign(algorithm: EcdsaParams, key: EcCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
EcCrypto.checkLib();
// get digests
const crypto = new Crypto();
let array;
const hash = await crypto.subtle.digest(algorithm.hash, data);
array = b2a(hash);
const signature = await key.data.sign(array);
const asnSignature = new core.asn1.EcDsaSignature();
asnSignature.r = new Uint8Array(signature.r.toArray()).buffer;
asnSignature.s = new Uint8Array(signature.s.toArray()).buffer;
return asnSignature.toWebCryptoSignature();
}
public async onVerify(algorithm: EcdsaParams, key: EcCryptoKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean> {
EcCrypto.checkLib();
const crypto = new Crypto();
const sig = {
r: new Uint8Array(signature.slice(0, signature.byteLength / 2)),
s: new Uint8Array(signature.slice(signature.byteLength / 2)),
};
// get digest
const hashedData = await crypto.subtle.digest(algorithm.hash, data);
const array = b2a(hashedData);
return key.data.verify(array, sig);
}
public override checkCryptoKey(key: CryptoKey, keyUsage: KeyUsage): asserts key is EcCryptoKey {
super.checkCryptoKey(key, keyUsage);
EcCrypto.checkCryptoKey(key);
}
}

View File

@ -0,0 +1,42 @@
import * as core from "@peculiar/webcrypto-core";
// TODO Use EcCurve from core
const namedOIDs: { [key: string]: string; } = {
// P-256
"1.2.840.10045.3.1.7": "P-256",
"P-256": "1.2.840.10045.3.1.7",
// P-384
"1.3.132.0.34": "P-384",
"P-384": "1.3.132.0.34",
// P-521
"1.3.132.0.35": "P-521",
"P-521": "1.3.132.0.35",
// K-256
"1.3.132.0.10": "K-256",
"K-256": "1.3.132.0.10",
// brainpoolP256r1
"1.3.36.3.3.2.8.1.1.7": "brainpoolP256r1",
"brainpoolP256r1": "1.3.36.3.3.2.8.1.1.7",
// brainpoolP384r1
"1.3.36.3.3.2.8.1.1.11": "brainpoolP384r1",
"brainpoolP384r1": "1.3.36.3.3.2.8.1.1.11",
// brainpoolP512r1
"1.3.36.3.3.2.8.1.1.13": "brainpoolP512r1",
"brainpoolP512r1": "1.3.36.3.3.2.8.1.1.13",
};
export function getNamedCurveByOid(oid: string) {
const namedCurve = namedOIDs[oid];
if (!namedCurve) {
throw new core.OperationError(`Cannot convert OID(${oid}) to WebCrypto named curve`);
}
return namedCurve;
}
export function getOidByNamedCurve(namedCurve: string) {
const oid = namedOIDs[namedCurve];
if (!oid) {
throw new core.OperationError(`Cannot convert WebCrypto named curve '${namedCurve}' to OID`);
}
return oid;
}

View File

@ -0,0 +1,3 @@
export * from "./ec_dh";
export * from "./ec_dsa";
export * from "./crypto";

View File

@ -0,0 +1,13 @@
/// <reference path="../../typings/elliptic.d.ts" />
import { EcKeyAlgorithm, KeyType, KeyUsage } from "@peculiar/webcrypto-types";
import { CryptoKey } from "../../key";
export class EcCryptoKey extends CryptoKey {
declare public algorithm: EcKeyAlgorithm;
constructor(algorithm: EcKeyAlgorithm, extractable: boolean, type: KeyType, usages: KeyUsage[], public data: EllipticJS.EllipticKeyPair) {
super(algorithm, extractable, type, usages);
}
}

View File

@ -0,0 +1,214 @@
import { AsnConvert, OctetString } from "@peculiar/asn1-schema";
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import * as elliptic from "elliptic";
import { Convert } from "pvtsutils";
import { CryptoKey } from "../../key";
import { nativeCrypto } from "../../native";
import { b2a } from "../ec";
import { getOidByNamedCurve } from "./helper";
import { EdPrivateKey } from "./private_key";
import { EdPublicKey } from "./public_key";
export class EdCrypto {
public static publicKeyUsages = ["verify"];
public static privateKeyUsages = ["sign", "deriveKey", "deriveBits"];
public static checkLib() {
if (typeof (elliptic) === "undefined") {
throw new core.OperationError("Cannot implement EC mechanism. Add 'https://peculiarventures.github.io/pv-webcrypto-tests/src/elliptic.js' script to your project");
}
}
public static concat(...buf: Uint8Array[]) {
const res = new Uint8Array(buf.map((item) => item.length).reduce((prev, cur) => prev + cur));
let offset = 0;
buf.forEach((item, index) => {
for (let i = 0; i < item.length; i++) {
res[offset + i] = item[i];
}
offset += item.length;
});
return res;
}
public static async generateKey(algorithm: types.EcKeyGenParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<types.CryptoKeyPair> {
this.checkLib();
// const curve = algorithm.namedCurve.toLowerCase() === "x25519" ? "curve25519" : "ed25519"; // "x25519" | "ed25519"
const curve = "ed25519";
let edKey: EllipticJS.EllipticKeyPair;
if (curve === "ed25519") {
const raw = nativeCrypto.getRandomValues(new Uint8Array(32));
const eddsa = new elliptic.eddsa(curve);
edKey = eddsa.keyFromSecret(raw);
} else {
edKey = elliptic.ec(curve).genKeyPair();
edKey.getPublic(); // Fills internal `pub` field
}
// set key params
const prvKey = new EdPrivateKey(
algorithm,
extractable,
keyUsages.filter((usage) => this.privateKeyUsages.indexOf(usage) !== -1),
edKey,
);
const pubKey = new EdPublicKey(
algorithm,
true,
keyUsages.filter((usage) => this.publicKeyUsages.indexOf(usage) !== -1),
edKey,
);
return {
privateKey: prvKey,
publicKey: pubKey,
};
}
public static async sign(algorithm: types.Algorithm, key: EdPrivateKey, data: Uint8Array): Promise<ArrayBuffer> {
this.checkLib();
const array = b2a(data);
const signature = key.data.sign(array).toHex();
return Convert.FromHex(signature);
}
public static async verify(algorithm: types.EcdsaParams, key: EdPublicKey, signature: Uint8Array, data: Uint8Array): Promise<boolean> {
this.checkLib();
const array = b2a(data);
const ok = key.data.verify(array, Convert.ToHex(signature));
return ok;
}
public static async deriveBits(algorithm: types.EcdhKeyDeriveParams, baseKey: EdPrivateKey, length: number): Promise<ArrayBuffer> {
this.checkLib();
const key = new Uint8Array(Convert.FromHex(baseKey.data.getSecret("hex")));
// key[0] &= 248;
// key[31] &= 127;
// key[31] |= 64;
// key.reverse();
// @ts-ignore
const ecdh = new elliptic.ec("curve25519");
const privateKey = ecdh.keyFromPrivate(Convert.ToHex(key), "hex");
const publicHex = (algorithm.public as EdPublicKey).data.getPublic("hex") as string;
const publicView = new Uint8Array(Convert.FromHex(publicHex));
// publicView.reverse();
// const publicKey = ecdh.keyFromPublic(Convert.ToHex(publicView), "hex").getPublic();
const publicKey = (algorithm.public as EdPublicKey).data.getPublic();
const shared = privateKey.derive(publicKey);
let array = new Uint8Array(shared.toArray());
// Padding
let len = array.length;
len = (len > 32 ? (len > 48 ? 66 : 48) : 32);
if (array.length < len) {
array = EdCrypto.concat(new Uint8Array(len - array.length), array);
}
const buf = array.slice(0, length / 8).buffer;
return buf;
}
public static async exportKey(format: types.KeyFormat, key: EdPrivateKey | EdPublicKey): Promise<types.JsonWebKey | ArrayBuffer> {
this.checkLib();
switch (format.toLowerCase()) {
case "jwk":
return JsonSerializer.toJSON(key);
case "pkcs8": {
// const raw = Convert.FromHex(/^x/i.test(key.algorithm.namedCurve)
// ? key.data.getPrivate("hex")
// : key.data.getSecret("hex"));
const raw = Convert.FromHex(key.data.getSecret("hex"));
const keyInfo = new core.asn1.PrivateKeyInfo();
keyInfo.privateKeyAlgorithm.algorithm = getOidByNamedCurve(key.algorithm.namedCurve);
keyInfo.privateKey = AsnConvert.serialize(new OctetString(raw));
return AsnConvert.serialize(keyInfo);
}
case "spki": {
const raw = Convert.FromHex(key.data.getPublic("hex"));
const keyInfo = new core.asn1.PublicKeyInfo();
keyInfo.publicKeyAlgorithm.algorithm = getOidByNamedCurve(key.algorithm.namedCurve);
keyInfo.publicKey = raw;
return AsnConvert.serialize(keyInfo);
}
case "raw": {
return Convert.FromHex(key.data.getPublic("hex"));
}
default:
throw new core.OperationError("format: Must be 'jwk', 'raw', pkcs8' or 'spki'");
}
}
public static async importKey(format: types.KeyFormat, keyData: types.JsonWebKey | ArrayBuffer, algorithm: types.EcKeyImportParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<CryptoKey> {
this.checkLib();
switch (format.toLowerCase()) {
case "jwk": {
const jwk = keyData as types.JsonWebKey;
if (jwk.d) {
const asnKey = JsonParser.fromJSON(keyData, { targetSchema: core.asn1.CurvePrivateKey });
return this.importPrivateKey(asnKey, algorithm, extractable, keyUsages);
} else {
if (!jwk.x) {
throw new TypeError("keyData: Cannot get required 'x' field");
}
return this.importPublicKey(Convert.FromBase64Url(jwk.x), algorithm, extractable, keyUsages);
}
}
case "raw": {
return this.importPublicKey(keyData as ArrayBuffer, algorithm, extractable, keyUsages);
}
case "spki": {
const keyInfo = AsnConvert.parse(new Uint8Array(keyData as ArrayBuffer), core.asn1.PublicKeyInfo);
return this.importPublicKey(keyInfo.publicKey, algorithm, extractable, keyUsages);
}
case "pkcs8": {
const keyInfo = AsnConvert.parse(new Uint8Array(keyData as ArrayBuffer), core.asn1.PrivateKeyInfo);
const asnKey = AsnConvert.parse(keyInfo.privateKey, core.asn1.CurvePrivateKey);
return this.importPrivateKey(asnKey, algorithm, extractable, keyUsages);
}
default:
throw new core.OperationError("format: Must be 'jwk', 'raw', 'pkcs8' or 'spki'");
}
}
protected static importPrivateKey(asnKey: core.asn1.CurvePrivateKey, algorithm: types.EcKeyImportParams, extractable: boolean, keyUsages: types.KeyUsage[]) {
const key = new EdPrivateKey(
Object.assign({}, algorithm),
extractable,
keyUsages, null as any); // key.data inits in key.fromJSON
key.fromJSON({
crv: algorithm.namedCurve,
d: Convert.ToBase64Url(asnKey.d),
});
return key;
}
protected static async importPublicKey(asnKey: ArrayBuffer, algorithm: types.EcKeyImportParams, extractable: boolean, keyUsages: types.KeyUsage[]) {
const key = new EdPublicKey(
Object.assign({}, algorithm),
extractable,
keyUsages, null as any); // key.data inits in key.fromJSON
key.fromJSON({
crv: algorithm.namedCurve,
x: Convert.ToBase64Url(asnKey),
});
return key;
}
}

View File

@ -0,0 +1,39 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import { CryptoKey } from "../../key";
import { EdCrypto } from "./crypto";
import { EdPrivateKey } from "./private_key";
import { EdPublicKey } from "./public_key";
export class EcdhEsProvider extends core.EcdhEsProvider {
public override namedCurves: string[] = ["X25519"];
public async onGenerateKey(algorithm: types.EcKeyGenParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<types.CryptoKeyPair> {
const keys = await EdCrypto.generateKey(
{
name: this.name,
namedCurve: algorithm.namedCurve.replace(/^x/i, "X"),
},
extractable,
keyUsages);
return keys;
}
public async onDeriveBits(algorithm: types.EcdhKeyDeriveParams, baseKey: EdPrivateKey, length: number): Promise<ArrayBuffer> {
const bits = await EdCrypto.deriveBits({ ...algorithm, public: algorithm.public as EdPublicKey }, baseKey, length);
return bits;
}
public async onExportKey(format: types.KeyFormat, key: EdPrivateKey | EdPublicKey): Promise<ArrayBuffer | types.JsonWebKey> {
return EdCrypto.exportKey(format, key);
}
public async onImportKey(format: types.KeyFormat, keyData: ArrayBuffer | types.JsonWebKey, algorithm: types.EcKeyImportParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<CryptoKey> {
const key = await EdCrypto.importKey(format, keyData, { ...algorithm, name: this.name }, extractable, keyUsages);
return key;
}
}

View File

@ -0,0 +1,40 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import { EdCrypto } from "./crypto";
import { EdPrivateKey } from "./private_key";
import { EdPublicKey } from "./public_key";
export class EdDsaProvider extends core.EdDsaProvider {
public override namedCurves: string[] = ["Ed25519"];
public async onGenerateKey(algorithm: types.EcKeyGenParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<types.CryptoKeyPair> {
const keys = await EdCrypto.generateKey(
{
name: this.name,
namedCurve: algorithm.namedCurve.replace(/^ed/i, "Ed"),
},
extractable,
keyUsages);
return keys;
}
public async onSign(algorithm: types.EcdsaParams, key: EdPrivateKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return EdCrypto.sign(algorithm, key, new Uint8Array(data));
}
public async onVerify(algorithm: types.EcdsaParams, key: EdPublicKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean> {
return EdCrypto.verify(algorithm, key, new Uint8Array(signature), new Uint8Array(data));
}
public async onExportKey(format: types.KeyFormat, key: EdPrivateKey | EdPublicKey): Promise<ArrayBuffer | types.JsonWebKey> {
return EdCrypto.exportKey(format, key);
}
public async onImportKey(format: types.KeyFormat, keyData: ArrayBuffer | types.JsonWebKey, algorithm: types.EcKeyImportParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<core.CryptoKey> {
const key = await EdCrypto.importKey(format, keyData, { ...algorithm, name: this.name }, extractable, keyUsages);
return key;
}
}

View File

@ -0,0 +1,32 @@
import * as core from "@peculiar/webcrypto-core";
const edOIDs: { [key: string]: string; } = {
// Ed448
[core.asn1.idEd448]: "Ed448",
"ed448": core.asn1.idEd448,
// X448
[core.asn1.idX448]: "X448",
"x448": core.asn1.idX448,
// Ed25519
[core.asn1.idEd25519]: "Ed25519",
"ed25519": core.asn1.idEd25519,
// X25519
[core.asn1.idX25519]: "X25519",
"x25519": core.asn1.idX25519,
};
export function getNamedCurveByOid(oid: string) {
const namedCurve = edOIDs[oid];
if (!namedCurve) {
throw new core.OperationError(`Cannot convert OID(${oid}) to WebCrypto named curve`);
}
return namedCurve;
}
export function getOidByNamedCurve(namedCurve: string) {
const oid = edOIDs[namedCurve.toLowerCase()];
if (!oid) {
throw new core.OperationError(`Cannot convert WebCrypto named curve '${namedCurve}' to OID`);
}
return oid;
}

View File

@ -0,0 +1,4 @@
export * from "./eddsa";
export * from "./ecdh_es";
export * from "./private_key";
export * from "./public_key";

View File

@ -0,0 +1,50 @@
import { IJsonConvertible } from "@peculiar/json-schema";
import * as core from "@peculiar/webcrypto-core";
import { CryptoKey } from "../../key";
import * as elliptic from "elliptic";
import { Convert } from "pvtsutils";
import { EcKeyAlgorithm, KeyUsage, JsonWebKey } from "@peculiar/webcrypto-types";
export class EdPrivateKey extends CryptoKey implements IJsonConvertible {
declare public algorithm: EcKeyAlgorithm;
public constructor(algorithm: EcKeyAlgorithm, extractable: boolean, usages: KeyUsage[], public data: EllipticJS.EllipticKeyPair) {
super(algorithm, extractable, "private", usages);
}
public toJSON(): JsonWebKey {
const json = {
kty: "OKP",
crv: this.algorithm.namedCurve,
key_ops: this.usages,
ext: this.extractable,
};
return Object.assign(json, {
d: Convert.ToBase64Url(Convert.FromHex(/^ed/i.test(json.crv) ? this.data.getSecret("hex") : this.data.getPrivate("hex"))),
});
}
public fromJSON(json: JsonWebKey) {
if (!json.d) {
throw new core.OperationError(`Cannot get private data from JWK. Property 'd' is required`);
}
if (!json.crv) {
throw new core.OperationError(`Cannot get named curve from JWK. Property 'crv' is required`);
}
const hexPrivateKey = Convert.ToHex(Convert.FromBase64Url(json.d));
if (true || /^ed/i.test(json.crv!)) {
// const eddsa = new elliptic.eddsa(json.crv.toLowerCase());
const eddsa = new elliptic.eddsa("ed25519");
this.data = eddsa.keyFromSecret(hexPrivateKey);
} else {
const ecdhEs = elliptic.ec(json.crv!.replace(/^x/i, "curve"));
this.data = ecdhEs.keyFromPrivate(hexPrivateKey, "hex");
}
return this;
}
}

View File

@ -0,0 +1,49 @@
import * as jsonSchema from "@peculiar/json-schema";
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import * as elliptic from "elliptic";
import * as pvtsutils from "pvtsutils";
import { CryptoKey } from "../../key";
export class EdPublicKey extends CryptoKey implements jsonSchema.IJsonConvertible {
declare public algorithm: types.EcKeyAlgorithm;
public constructor(algorithm: types.EcKeyAlgorithm, extractable: boolean, usages: types.KeyUsage[], public data: EllipticJS.EllipticKeyPair) {
super(algorithm, extractable, "public", usages);
}
public toJSON() {
const json: types.JsonWebKey = {
kty: "OKP",
crv: this.algorithm.namedCurve,
key_ops: this.usages,
ext: this.extractable,
};
return Object.assign(json, {
x: pvtsutils.Convert.ToBase64Url(pvtsutils.Convert.FromHex(this.data.getPublic("hex"))),
});
}
public fromJSON(json: types.JsonWebKey) {
if (!json.crv) {
// TODO use core.RequiredPropertyError
throw new core.OperationError(`Cannot get named curve from JWK. Property 'crv' is required`);
}
if (!json.x) {
throw new core.OperationError(`Cannot get property from JWK. Property 'x' is required`);
}
const hexPublicKey = pvtsutils.Convert.ToHex(pvtsutils.Convert.FromBase64Url(json.x));
if (/^ed/i.test(json.crv)) {
const eddsa = new elliptic.eddsa(json.crv.toLowerCase());
this.data = eddsa.keyFromPublic(hexPublicKey, "hex");
} else {
const ecdhEs = elliptic.ec(json.crv.replace(/^x/i, "curve"));
this.data = ecdhEs.keyFromPublic(hexPublicKey, "hex");
}
return this;
}
}

View File

@ -0,0 +1,100 @@
import * as jsonSchema from "@peculiar/json-schema";
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import * as asmCrypto from "asmcrypto.js";
import * as pvtsutils from "pvtsutils";
import { nativeCrypto } from "../../native";
import { HmacCryptoKey } from "./key";
export class HmacProvider extends core.HmacProvider {
public async onGenerateKey(algorithm: types.HmacKeyGenParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<HmacCryptoKey> {
const length = algorithm.length || this.getDefaultLength((algorithm.hash as types.Algorithm).name);
// get random bytes for key
const raw = nativeCrypto.getRandomValues(new Uint8Array(length >> 3));
const key = new HmacCryptoKey(algorithm, extractable, keyUsages, raw);
return key;
}
public override async onSign(algorithm: types.Algorithm, key: HmacCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
let fn: typeof asmCrypto.HmacSha1 | typeof asmCrypto.HmacSha256 | typeof asmCrypto.HmacSha512;
switch (key.algorithm.hash.name.toUpperCase()) {
case "SHA-1":
fn = asmCrypto.HmacSha1;
break;
case "SHA-256":
fn = asmCrypto.HmacSha256;
break;
case "SHA-512":
fn = asmCrypto.HmacSha512;
break;
default:
throw new core.OperationError("key.algorithm.hash: Is not recognized");
}
const result = new fn(key.data)
.process(pvtsutils.BufferSourceConverter.toUint8Array(data))
.finish().result;
if (!result) {
throw new core.OperationError("HMAC signing result is empty");
}
return pvtsutils.BufferSourceConverter.toArrayBuffer(result);
}
public override async onVerify(algorithm: types.Algorithm, key: HmacCryptoKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean> {
const signature2 = await this.onSign(algorithm, key, data);
return pvtsutils.Convert.ToHex(signature2) === pvtsutils.Convert.ToHex(signature);
}
public async onImportKey(format: types.KeyFormat, keyData: types.JsonWebKey | ArrayBuffer, algorithm: types.HmacImportParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<HmacCryptoKey> {
let key: HmacCryptoKey;
switch (format.toLowerCase()) {
case "jwk":
key = jsonSchema.JsonParser.fromJSON(keyData, { targetSchema: HmacCryptoKey });
break;
case "raw":
if (!pvtsutils.BufferSourceConverter.isBufferSource(keyData)) {
throw new TypeError("keyData: Is not ArrayBuffer or ArrayBufferView");
}
key = new HmacCryptoKey(algorithm, extractable, keyUsages, pvtsutils.BufferSourceConverter.toUint8Array(keyData));
break;
default:
throw new core.OperationError("format: Must be 'jwk' or 'raw'");
}
key.algorithm = {
hash: { name: (algorithm.hash as types.Algorithm).name },
name: this.name,
length: key.data.length << 3,
};
key.extractable = extractable;
key.usages = keyUsages;
return key;
}
public async onExportKey(format: types.KeyFormat, key: HmacCryptoKey): Promise<types.JsonWebKey | ArrayBuffer> {
switch (format.toLowerCase()) {
case "jwk":
const jwk = jsonSchema.JsonSerializer.toJSON(key) as types.JsonWebKey;
return jwk;
case "raw":
return new Uint8Array(key.data).buffer;
default:
throw new core.OperationError("format: Must be 'jwk' or 'raw'");
}
}
public override checkCryptoKey(key: types.CryptoKey, keyUsage?: types.KeyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(key instanceof HmacCryptoKey)) {
throw new TypeError("key: Is not HMAC CryptoKey");
}
}
}

View File

@ -0,0 +1 @@
export * from "./hmac";

View File

@ -0,0 +1,56 @@
import * as jsonSchema from "@peculiar/json-schema";
import * as types from "@peculiar/webcrypto-types";
import * as pvtsutils from "pvtsutils";
import { CryptoKey } from "../../key";
export const JsonBase64UrlConverter: jsonSchema.IJsonConverter<Buffer, string> = {
fromJSON: (value: string) => Buffer.from(pvtsutils.Convert.FromBase64Url(value)),
toJSON: (value: Buffer) => pvtsutils.Convert.ToBase64Url(value),
};
export class HmacCryptoKey extends CryptoKey {
@jsonSchema.JsonProp({ name: "ext", type: jsonSchema.JsonPropTypes.Boolean, optional: true })
public override extractable!: boolean;
declare public readonly type: "secret";
@jsonSchema.JsonProp({ name: "key_ops", type: jsonSchema.JsonPropTypes.String, repeated: true, optional: true })
public override usages!: types.KeyUsage[];
@jsonSchema.JsonProp({ name: "k", converter: JsonBase64UrlConverter })
public data: Uint8Array;
declare public algorithm: types.HmacKeyAlgorithm;
@jsonSchema.JsonProp({ type: jsonSchema.JsonPropTypes.String })
protected readonly kty: string = "oct";
@jsonSchema.JsonProp({ type: jsonSchema.JsonPropTypes.String })
protected get alg() {
const hash = this.algorithm.hash.name.toUpperCase();
return `HS${hash.replace("SHA-", "")}`;
}
protected set alg(value: string) {
// nothing, cause set is needed for json-schema, but is not used by module
}
constructor();
constructor(
algorithm: types.KeyAlgorithm,
extractable: boolean,
usages: types.KeyUsage[],
data: Uint8Array,
);
constructor(
algorithm = { name: "HMAC" },
extractable = false,
usages: types.KeyUsage[] = [],
data = new Uint8Array(0),
) {
super(algorithm, extractable, "secret", usages);
this.data = data;
}
}

View File

@ -0,0 +1,8 @@
export * from "./aes";
export * from "./rsa";
export * from "./ec";
export * from "./ed";
export * from "./sha";
export * from "./pbkdf";
export * from "./des";
export * from "./hmac";

View File

@ -0,0 +1 @@
export * from "./pbkdf2";

View File

@ -0,0 +1,10 @@
import * as types from "@peculiar/webcrypto-types";
import { CryptoKey } from "../../key";
export class PbkdfCryptoKey extends CryptoKey {
constructor(algorithm: types.KeyAlgorithm, extractable: boolean, usages: types.KeyUsage[], public raw: Uint8Array) {
super(algorithm, extractable, "secret", usages);
}
}

View File

@ -0,0 +1,45 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import * as asmCrypto from "asmcrypto.js";
import * as pvtsutils from "pvtsutils";
import { PbkdfCryptoKey } from "./key";
export class Pbkdf2Provider extends core.Pbkdf2Provider {
public async onImportKey(format: types.KeyFormat, keyData: types.JsonWebKey | ArrayBuffer, algorithm: types.Algorithm, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<PbkdfCryptoKey> {
return new PbkdfCryptoKey(
algorithm,
extractable,
keyUsages,
pvtsutils.BufferSourceConverter.toUint8Array(keyData as ArrayBuffer),
);
}
public async onDeriveBits(algorithm: types.Pbkdf2Params, baseKey: PbkdfCryptoKey, length: number): Promise<ArrayBuffer> {
let result: Uint8Array;
const salt = pvtsutils.BufferSourceConverter.toUint8Array(algorithm.salt);
const password = baseKey.raw;
switch ((algorithm.hash as types.Algorithm).name.toUpperCase()) {
case "SHA-1":
result = asmCrypto.Pbkdf2HmacSha1(password, salt, algorithm.iterations, length >> 3);
break;
case "SHA-256":
result = asmCrypto.Pbkdf2HmacSha256(password, salt, algorithm.iterations, length >> 3);
break;
case "SHA-512":
result = asmCrypto.Pbkdf2HmacSha512(password, salt, algorithm.iterations, length >> 3);
break;
default:
throw new core.OperationError(`algorithm.hash: '${(algorithm.hash as types.Algorithm).name}' hash algorithm is not supported`);
}
return pvtsutils.BufferSourceConverter.toArrayBuffer(result);
}
public override checkCryptoKey(key: types.CryptoKey, keyUsage: types.KeyUsage): asserts key is PbkdfCryptoKey {
super.checkCryptoKey(key, keyUsage);
if (!(key instanceof PbkdfCryptoKey)) {
throw new TypeError("key: Is not PbkdfCryptoKey");
}
}
}

View File

@ -0,0 +1,228 @@
import * as asn1Schema from "@peculiar/asn1-schema";
import * as jsonSchema from "@peculiar/json-schema";
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import { Crypto } from "../../crypto";
import { concat } from "../../helper";
import { nativeCrypto, nativeSubtle } from "../../native";
import { RsaCryptoKey } from "./key";
export type AsmCryptoRsaKey = Uint8Array[];
export class RsaCrypto {
public static RsaSsa = "RSASSA-PKCS1-v1_5";
public static RsaPss = "RSA-PSS";
public static RsaOaep = "RSA-OAEP";
public static privateUsages: types.KeyUsage[] = ["sign", "decrypt", "unwrapKey"];
public static publicUsages: types.KeyUsage[] = ["verify", "encrypt", "wrapKey"];
/**
* Tests whether the specified object is RsaCryptoKey and throws a TypeError if it is not
* @param key The object the test expects to be RsaCryptoKey
*/
public static checkCryptoKey(key: any): asserts key is RsaCryptoKey {
if (!(key instanceof RsaCryptoKey)) {
throw new TypeError("key: Is not RsaCryptoKey");
}
}
public static async generateKey(algorithm: types.RsaHashedKeyGenParams | types.RsaKeyGenParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<types.CryptoKeyPair> {
const alg: types.RsaHashedKeyGenParams = {
name: "RSA-PSS",
hash: "SHA-256",
publicExponent: algorithm.publicExponent,
modulusLength: algorithm.modulusLength,
};
// generate keys using native crypto
if (!nativeSubtle) {
throw new core.OperationError("Native SubtleCrypto is unavailable");
}
const keys = (await nativeSubtle.generateKey(alg, true, ["sign", "verify"])) as types.CryptoKeyPair;
const crypto = new Crypto();
// create private key
const pkcs8 = await crypto.subtle.exportKey("pkcs8", keys.privateKey);
const privateKey = await crypto.subtle.importKey("pkcs8", pkcs8, algorithm, extractable, keyUsages.filter((o) => this.privateUsages.includes(o)));
// create public key
const spki = await crypto.subtle.exportKey("spki", keys.publicKey);
const publicKey = await crypto.subtle.importKey("spki", spki, algorithm, true, keyUsages.filter((o) => this.publicUsages.includes(o)));
return { privateKey, publicKey };
}
public static async exportKey(format: types.KeyFormat, key: RsaCryptoKey): Promise<types.JsonWebKey | ArrayBuffer> {
switch (format) {
case "pkcs8":
return this.exportPkcs8Key(key);
case "spki":
return this.exportSpkiKey(key);
case "jwk":
return this.exportJwkKey(key);
default:
throw new core.OperationError("format: Must be 'jwk', 'pkcs8' or 'spki'");
}
}
public static async importKey(format: types.KeyFormat, keyData: types.JsonWebKey | ArrayBuffer, algorithm: types.RsaHashedImportParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<RsaCryptoKey> {
let asmKey: AsmCryptoRsaKey;
switch (format) {
case "pkcs8":
asmKey = this.importPkcs8Key(keyData as ArrayBuffer);
break;
case "spki":
asmKey = this.importSpkiKey(keyData as ArrayBuffer);
break;
case "jwk":
asmKey = this.importJwkKey(keyData as types.JsonWebKey);
break;
default:
throw new core.OperationError("format: Must be 'jwk', 'pkcs8' or 'spki'");
}
const key = new RsaCryptoKey(
{
publicExponent: asmKey[1][1] === 1
? asmKey[1].slice(1)
: asmKey[1].slice(3),
modulusLength: asmKey[0].byteLength << 3,
...algorithm,
} as types.RsaHashedKeyAlgorithm,
extractable,
asmKey.length === 2 ? "public" : "private",
keyUsages,
asmKey,
);
return key;
}
public static randomNonZeroValues(data: Uint8Array) {
data = nativeCrypto.getRandomValues(data);
return data.map((n) => {
while (!n) {
n = nativeCrypto.getRandomValues(new Uint8Array(1))[0];
}
return n;
});
}
private static exportPkcs8Key(key: RsaCryptoKey) {
const keyInfo = new core.asn1.PrivateKeyInfo();
keyInfo.privateKeyAlgorithm.algorithm = "1.2.840.113549.1.1.1";
keyInfo.privateKeyAlgorithm.parameters = null;
keyInfo.privateKey = asn1Schema.AsnConvert.serialize(this.exportAsmKey(key.data));
return asn1Schema.AsnConvert.serialize(keyInfo);
}
private static importPkcs8Key(data: ArrayBuffer) {
const keyInfo = asn1Schema.AsnConvert.parse(data, core.asn1.PrivateKeyInfo);
const privateKey = asn1Schema.AsnConvert.parse(keyInfo.privateKey, core.asn1.RsaPrivateKey);
return this.importAsmKey(privateKey);
}
private static importSpkiKey(data: ArrayBuffer) {
const keyInfo = asn1Schema.AsnConvert.parse(data, core.asn1.PublicKeyInfo);
const publicKey = asn1Schema.AsnConvert.parse(keyInfo.publicKey, core.asn1.RsaPublicKey);
return this.importAsmKey(publicKey);
}
private static exportSpkiKey(key: RsaCryptoKey) {
const publicKey = new core.asn1.RsaPublicKey();
publicKey.modulus = key.data[0].buffer;
publicKey.publicExponent = key.data[1][1] === 1
? key.data[1].buffer.slice(1)
: key.data[1].buffer.slice(3);
const keyInfo = new core.asn1.PublicKeyInfo();
keyInfo.publicKeyAlgorithm.algorithm = "1.2.840.113549.1.1.1";
keyInfo.publicKeyAlgorithm.parameters = null;
keyInfo.publicKey = asn1Schema.AsnConvert.serialize(publicKey);
return asn1Schema.AsnConvert.serialize(keyInfo);
}
private static importJwkKey(data: types.JsonWebKey) {
let key: core.asn1.RsaPrivateKey | core.asn1.RsaPublicKey;
if (data.d) {
// private
key = jsonSchema.JsonParser.fromJSON(data, { targetSchema: core.asn1.RsaPrivateKey });
} else {
// public
key = jsonSchema.JsonParser.fromJSON(data, { targetSchema: core.asn1.RsaPublicKey });
}
return this.importAsmKey(key);
}
private static exportJwkKey(key: RsaCryptoKey) {
const asnKey = this.exportAsmKey(key.data);
const jwk = jsonSchema.JsonSerializer.toJSON(asnKey) as types.JsonWebKey;
jwk.ext = true;
jwk.key_ops = key.usages;
jwk.kty = "RSA";
jwk.alg = this.getJwkAlgorithm(key.algorithm);
return jwk;
}
private static getJwkAlgorithm(algorithm: types.RsaHashedKeyAlgorithm) {
switch (algorithm.name.toUpperCase()) {
case "RSA-OAEP":
const mdSize = /(\d+)$/.exec(algorithm.hash.name)![1];
return `RSA-OAEP${mdSize !== "1" ? `-${mdSize}` : ""}`;
case "RSASSA-PKCS1-V1_5":
return `RS${/(\d+)$/.exec(algorithm.hash.name)![1]}`;
case "RSA-PSS":
return `PS${/(\d+)$/.exec(algorithm.hash.name)![1]}`;
case "RSAES-PKCS1-V1_5":
return `PS1`;
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}
private static exportAsmKey(asmKey: AsmCryptoRsaKey): core.asn1.RsaPrivateKey | core.asn1.RsaPublicKey {
let key: core.asn1.RsaPrivateKey | core.asn1.RsaPublicKey;
if (asmKey.length > 2) {
// private
const privateKey = new core.asn1.RsaPrivateKey();
privateKey.privateExponent = asmKey[2].buffer;
privateKey.prime1 = asmKey[3].buffer;
privateKey.prime2 = asmKey[4].buffer;
privateKey.exponent1 = asmKey[5].buffer;
privateKey.exponent2 = asmKey[6].buffer;
privateKey.coefficient = asmKey[7].buffer;
key = privateKey;
} else {
// public
key = new core.asn1.RsaPublicKey();
}
key.modulus = asmKey[0].buffer;
key.publicExponent = asmKey[1][1] === 1
? asmKey[1].buffer.slice(1)
: asmKey[1].buffer.slice(3);
return key;
}
private static importAsmKey(key: core.asn1.RsaPrivateKey | core.asn1.RsaPublicKey) {
const expPadding = new Uint8Array(4 - key.publicExponent.byteLength);
const asmKey: AsmCryptoRsaKey = [
new Uint8Array(key.modulus),
concat(expPadding, new Uint8Array(key.publicExponent)),
];
if (key instanceof core.asn1.RsaPrivateKey) {
asmKey.push(new Uint8Array(key.privateExponent));
asmKey.push(new Uint8Array(key.prime1));
asmKey.push(new Uint8Array(key.prime2));
asmKey.push(new Uint8Array(key.exponent1));
asmKey.push(new Uint8Array(key.exponent2));
asmKey.push(new Uint8Array(key.coefficient));
}
return asmKey;
}
}

View File

@ -0,0 +1,5 @@
export * from "./crypto";
export * from "./rsa_oaep";
export * from "./rsa_pss";
export * from "./rsa_ssa";
export * from "./rsa_es";

View File

@ -0,0 +1,12 @@
import * as types from "@peculiar/webcrypto-types";
import { CryptoKey } from "../../key";
import { AsmCryptoRsaKey } from "./crypto";
export class RsaCryptoKey extends CryptoKey {
declare public algorithm: types.RsaHashedKeyAlgorithm;
constructor(algorithm: types.RsaHashedKeyAlgorithm, extractable: boolean, type: types.KeyType, usages: types.KeyUsage[], public data: AsmCryptoRsaKey) {
super(algorithm, extractable, type, usages);
}
}

View File

@ -0,0 +1,119 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import * as asmCrypto from "asmcrypto.js";
import * as pvtsutils from "pvtsutils";
import { Crypto } from "../../crypto";
import { RsaCrypto } from "./crypto";
import { RsaCryptoKey } from "./key";
export type RsaPkcs1Params = types.Algorithm;
export type RsaPkcs1SignParams = types.HashedAlgorithm;
export class RsaEsProvider extends core.ProviderCrypto {
public name = "RSAES-PKCS1-v1_5";
public usages = {
publicKey: ["encrypt", "wrapKey"] as types.KeyUsages,
privateKey: ["decrypt", "unwrapKey"] as types.KeyUsages,
};
public hashAlgorithms = ["SHA-1", "SHA-256", "SHA-384", "SHA-512"];
public override async onGenerateKey(algorithm: types.RsaKeyGenParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<types.CryptoKeyPair> {
return RsaCrypto.generateKey(algorithm, extractable, keyUsages);
}
public override checkGenerateKeyParams(algorithm: types.RsaKeyGenParams) {
// public exponent
this.checkRequiredProperty(algorithm, "publicExponent");
if (!(algorithm.publicExponent && algorithm.publicExponent instanceof Uint8Array)) {
throw new TypeError("publicExponent: Missing or not a Uint8Array");
}
const publicExponent = pvtsutils.Convert.ToBase64(algorithm.publicExponent);
if (!(publicExponent === "Aw==" || publicExponent === "AQAB")) {
throw new TypeError("publicExponent: Must be [3] or [1,0,1]");
}
// modulus length
this.checkRequiredProperty(algorithm, "modulusLength");
switch (algorithm.modulusLength) {
case 1024:
case 2048:
case 4096:
break;
default:
throw new TypeError("modulusLength: Must be 1024, 2048, or 4096");
}
}
public override async onDecrypt(algorithm: RsaPkcs1Params, key: RsaCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
// EM = 0x00 || 0x02 || PS || 0x00 || M
const EM = new asmCrypto.RSA(key.data).decrypt(new asmCrypto.BigNumber(pvtsutils.BufferSourceConverter.toUint8Array(data))).result;
const k = key.algorithm.modulusLength >> 3;
if (data.byteLength !== k) {
throw new core.CryptoError("Decryption error. Encrypted message size doesn't match to key length");
}
// If the first octet of EM does not have hexadecimal value 0x00, if
// the second octet of EM does not have hexadecimal value 0x02, if
// there is no octet with hexadecimal value 0x00 to separate PS from
// M, or if the length of PS is less than 8 octets, output
// "decryption error" and stop.
let offset = 0;
if (EM[offset++] || EM[offset++] !== 2) {
throw new core.CryptoError("Decryption error");
}
do {
if (EM[offset++] === 0) {
break;
}
} while (offset < EM.length);
if (offset < 11) {
throw new core.CryptoError("Decryption error. PS is less than 8 octets.");
}
if (offset === EM.length) {
throw new core.CryptoError("Decryption error. There is no octet with hexadecimal value 0x00 to separate PS from M");
}
return EM.buffer.slice(offset);
}
public override async onEncrypt(algorithm: RsaPkcs1Params, key: RsaCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
const k = key.algorithm.modulusLength >> 3;
if (data.byteLength > k - 11) {
throw new core.CryptoError("Message too long");
}
// EM = 0x00 || 0x02 || PS || 0x00 || M
const psLen = k - data.byteLength - 3;
const PS = RsaCrypto.randomNonZeroValues(new Uint8Array(psLen));
const EM = new Uint8Array(k);
EM[0] = 0;
EM[1] = 2;
EM.set(PS, 2); // PS
EM[2 + psLen] = 0;
EM.set(new Uint8Array(data), 3 + psLen);
const result = new asmCrypto.RSA(key.data).encrypt(new asmCrypto.BigNumber(EM)).result;
return pvtsutils.BufferSourceConverter.toArrayBuffer(result);
}
public override async onExportKey(format: types.KeyFormat, key: RsaCryptoKey): Promise<types.JsonWebKey | ArrayBuffer> {
return RsaCrypto.exportKey(format, key);
}
public override async onImportKey(format: types.KeyFormat, keyData: types.JsonWebKey | ArrayBuffer, algorithm: types.RsaHashedImportParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<types.CryptoKey> {
const key = await RsaCrypto.importKey(format, keyData, { ...algorithm, name: this.name }, extractable, keyUsages);
return key;
}
public override checkCryptoKey(key: types.CryptoKey, keyUsage: types.KeyUsage): asserts key is RsaCryptoKey {
super.checkCryptoKey(key, keyUsage);
RsaCrypto.checkCryptoKey(key);
}
private async prepareSignData(algorithm: RsaPkcs1SignParams, data: ArrayBuffer) {
const crypto = new Crypto();
return crypto.subtle.digest(algorithm.hash, data);
}
}

View File

@ -0,0 +1,53 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import * as asmCrypto from "asmcrypto.js";
import * as pvtsutils from "pvtsutils";
import { ShaCrypto } from "../sha/crypto";
import { RsaCrypto } from "./crypto";
import { RsaCryptoKey } from "./key";
export class RsaOaepProvider extends core.RsaOaepProvider {
public async onGenerateKey(algorithm: types.RsaHashedKeyGenParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<types.CryptoKeyPair> {
return RsaCrypto.generateKey(algorithm, extractable, keyUsages);
}
public async onExportKey(format: types.KeyFormat, key: RsaCryptoKey): Promise<types.JsonWebKey | ArrayBuffer> {
return RsaCrypto.exportKey(format, key);
}
public async onImportKey(format: types.KeyFormat, keyData: types.JsonWebKey | ArrayBuffer, algorithm: types.RsaHashedImportParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<RsaCryptoKey> {
return RsaCrypto.importKey(format, keyData, algorithm, extractable, keyUsages);
}
public async onEncrypt(algorithm: types.RsaOaepParams, key: RsaCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return this.cipher(algorithm, key, data);
}
public async onDecrypt(algorithm: types.RsaOaepParams, key: RsaCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
return this.cipher(algorithm, key, data);
}
public override checkCryptoKey(key: types.CryptoKey, keyUsage: types.KeyUsage): asserts key is RsaCryptoKey {
super.checkCryptoKey(key, keyUsage);
RsaCrypto.checkCryptoKey(key);
}
private cipher(algorithm: types.RsaOaepParams, key: RsaCryptoKey, data: ArrayBuffer) {
const digest = ShaCrypto.getDigest(key.algorithm.hash.name);
let label: Uint8Array | undefined;
if (algorithm.label) {
label = pvtsutils.BufferSourceConverter.toUint8Array(algorithm.label);
}
const cipher = new asmCrypto.RSA_OAEP(key.data, digest, label);
let res: Uint8Array;
const u8Data = pvtsutils.BufferSourceConverter.toUint8Array(data);
if (key.type === "public") {
res = cipher.encrypt(u8Data);
} else {
res = cipher.decrypt(u8Data);
}
return pvtsutils.BufferSourceConverter.toArrayBuffer(res);
}
}

View File

@ -0,0 +1,44 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import * as asmCrypto from "asmcrypto.js";
import * as pvtsutils from "pvtsutils";
import { ShaCrypto } from "../sha/crypto";
import { RsaCrypto } from "./crypto";
import { RsaCryptoKey } from "./key";
export class RsaPssProvider extends core.RsaPssProvider {
public async onGenerateKey(algorithm: types.RsaHashedKeyGenParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<types.CryptoKeyPair> {
return RsaCrypto.generateKey(algorithm, extractable, keyUsages);
}
public async onExportKey(format: types.KeyFormat, key: RsaCryptoKey): Promise<types.JsonWebKey | ArrayBuffer> {
return RsaCrypto.exportKey(format, key);
}
public async onImportKey(format: types.KeyFormat, keyData: types.JsonWebKey | ArrayBuffer, algorithm: types.RsaHashedImportParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<RsaCryptoKey> {
return RsaCrypto.importKey(format, keyData, algorithm, extractable, keyUsages);
}
public async onSign(algorithm: types.RsaPssParams, key: RsaCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
const rsa = new asmCrypto.RSA_PSS(key.data, ShaCrypto.getDigest(key.algorithm.hash.name), algorithm.saltLength);
const result = rsa.sign(pvtsutils.BufferSourceConverter.toUint8Array(data));
return pvtsutils.BufferSourceConverter.toArrayBuffer(result);
}
public async onVerify(algorithm: types.RsaPssParams, key: RsaCryptoKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean> {
const rsa = new asmCrypto.RSA_PSS(key.data, ShaCrypto.getDigest(key.algorithm.hash.name), algorithm.saltLength);
try {
rsa.verify(pvtsutils.BufferSourceConverter.toUint8Array(signature), pvtsutils.BufferSourceConverter.toUint8Array(data));
} catch {
return false;
}
return true;
}
public override checkCryptoKey(key: types.CryptoKey, keyUsage: types.KeyUsage): asserts key is RsaCryptoKey {
super.checkCryptoKey(key, keyUsage);
RsaCrypto.checkCryptoKey(key);
}
}

View File

@ -0,0 +1,44 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import * as asmCrypto from "asmcrypto.js";
import * as pvtsutils from "pvtsutils";
import { ShaCrypto } from "../sha/crypto";
import { RsaCrypto } from "./crypto";
import { RsaCryptoKey } from "./key";
export class RsaSsaProvider extends core.RsaSsaProvider {
public async onGenerateKey(algorithm: types.RsaHashedKeyGenParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<types.CryptoKeyPair> {
return RsaCrypto.generateKey(algorithm, extractable, keyUsages);
}
public async onExportKey(format: types.KeyFormat, key: RsaCryptoKey): Promise<types.JsonWebKey | ArrayBuffer> {
return RsaCrypto.exportKey(format, key);
}
public async onImportKey(format: types.KeyFormat, keyData: types.JsonWebKey | ArrayBuffer, algorithm: types.RsaHashedImportParams, extractable: boolean, keyUsages: types.KeyUsage[]): Promise<RsaCryptoKey> {
return RsaCrypto.importKey(format, keyData, algorithm, extractable, keyUsages);
}
public async onSign(algorithm: types.Algorithm, key: RsaCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
const rsa = new asmCrypto.RSA_PKCS1_v1_5(key.data, ShaCrypto.getDigest(key.algorithm.hash.name));
const result = rsa.sign(pvtsutils.BufferSourceConverter.toUint8Array(data));
return pvtsutils.BufferSourceConverter.toArrayBuffer(result);
}
public async onVerify(algorithm: types.Algorithm, key: RsaCryptoKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean> {
const rsa = new asmCrypto.RSA_PKCS1_v1_5(key.data, ShaCrypto.getDigest(key.algorithm.hash.name));
try {
rsa.verify(pvtsutils.BufferSourceConverter.toUint8Array(signature), pvtsutils.BufferSourceConverter.toUint8Array(data));
} catch {
return false;
}
return true;
}
public override checkCryptoKey(key: types.CryptoKey, keyUsage: types.KeyUsage): asserts key is RsaCryptoKey {
super.checkCryptoKey(key, keyUsage);
RsaCrypto.checkCryptoKey(key);
}
}

View File

@ -0,0 +1,32 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import * as asmCrypto from "asmcrypto.js";
import * as pvtsutils from "pvtsutils";
export class ShaCrypto {
public static getDigest(name: string) {
switch (name) {
case "SHA-1":
return new asmCrypto.Sha1();
case "SHA-256":
return new asmCrypto.Sha256();
case "SHA-512":
return new asmCrypto.Sha512();
default:
throw new core.AlgorithmError("keyAlgorithm.hash: Is not recognized");
}
}
public static async digest(algorithm: types.Algorithm, data: ArrayBuffer): Promise<ArrayBuffer> {
const mech = this.getDigest(algorithm.name);
const result = mech
.process(pvtsutils.BufferSourceConverter.toUint8Array(data))
.finish().result;
if (!result) {
throw new core.OperationError("SHA digest result is empty");
}
return pvtsutils.BufferSourceConverter.toArrayBuffer(result);
}
}

View File

@ -0,0 +1,8 @@
export * from "./sha_1";
export * from "./sha_256";
export * from "./sha_512";
export * from "./sha3_256";
export * from "./sha3_384";
export * from "./sha3_512";
export * from "./shake128";
export * from "./shake256";

View File

@ -0,0 +1,13 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import { hash256 } from "@stablelib/sha3";
export class Sha3256Provider extends core.ProviderCrypto {
public name = "SHA3-256";
public usages: types.ProviderKeyUsage = [];
public override async onDigest(algorithm: types.Algorithm, data: ArrayBuffer): Promise<ArrayBuffer> {
return hash256(new Uint8Array(data)).buffer;
}
}

View File

@ -0,0 +1,13 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import { hash384 } from "@stablelib/sha3";
export class Sha3384Provider extends core.ProviderCrypto {
public name = "SHA3-384";
public usages: types.ProviderKeyUsage = [];
public override async onDigest(algorithm: types.Algorithm, data: ArrayBuffer): Promise<ArrayBuffer> {
return hash384(new Uint8Array(data)).buffer;
}
}

View File

@ -0,0 +1,13 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import { hash512 } from "@stablelib/sha3";
export class Sha3512Provider extends core.ProviderCrypto {
public name = "SHA3-512";
public usages: types.ProviderKeyUsage = [];
public override async onDigest(algorithm: types.Algorithm, data: ArrayBuffer): Promise<ArrayBuffer> {
return hash512(new Uint8Array(data)).buffer;
}
}

View File

@ -0,0 +1,13 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import { ShaCrypto } from "./crypto";
export class Sha1Provider extends core.ProviderCrypto {
public name = "SHA-1";
public usages: types.ProviderKeyUsage = [];
public override async onDigest(algorithm: types.Algorithm, data: ArrayBuffer): Promise<ArrayBuffer> {
return ShaCrypto.digest(algorithm, data);
}
}

View File

@ -0,0 +1,5 @@
import { Sha1Provider } from "./sha_1";
export class Sha256Provider extends Sha1Provider {
public override name = "SHA-256";
}

View File

@ -0,0 +1,5 @@
import { Sha1Provider } from "./sha_1";
export class Sha512Provider extends Sha1Provider {
public override name = "SHA-512";
}

View File

@ -0,0 +1,14 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import { SHAKE128 } from "@stablelib/sha3";
export class Shake128Provider extends core.Shake128Provider {
public async onDigest(algorithm: Required<types.ShakeParams>, data: ArrayBuffer): Promise<ArrayBuffer> {
const output = new Uint8Array(algorithm.length);
new SHAKE128().update(new Uint8Array(data)).stream(output);
return output.buffer;
}
}

View File

@ -0,0 +1,14 @@
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import { SHAKE256 } from "@stablelib/sha3";
export class Shake256Provider extends core.Shake256Provider {
public async onDigest(algorithm: Required<types.ShakeParams>, data: ArrayBuffer): Promise<ArrayBuffer> {
const output = new Uint8Array(algorithm.length);
new SHAKE256().update(new Uint8Array(data)).stream(output);
return output.buffer;
}
}

View File

@ -0,0 +1,25 @@
import * as types from "@peculiar/webcrypto-types";
declare const self: any;
let window: any = {};
if (typeof self !== "undefined") {
window = self;
}
export let nativeCrypto: types.Crypto =
window["msCrypto"] // IE
|| window.crypto // other browsers
|| {}; // if crypto is empty
export let nativeSubtle: types.SubtleCrypto | null = null;
try {
nativeSubtle = nativeCrypto?.subtle || (nativeCrypto as any)?.["webkitSubtle"] || null;
} catch (err) {
console.warn("Cannot get subtle from crypto", err);
// Safari throws error on crypto.webkitSubtle in Worker
}
export function setCrypto(crypto: types.Crypto) {
nativeCrypto = crypto;
nativeSubtle = crypto.subtle;
}

23
packages/web/src/shim.ts Normal file
View File

@ -0,0 +1,23 @@
import { Crypto, nativeCrypto } from ".";
import { Debug } from "./debug";
import "./init";
declare const self: any;
const window = self as any;
if (nativeCrypto) {
Object.freeze(nativeCrypto.getRandomValues);
}
try {
// Replace original crypto by liner
delete (self as any).crypto;
window.crypto = new Crypto();
Object.freeze(window.crypto);
} catch (e) {
Debug.error(e);
}
export const crypto = window.crypto;
export * from ".";

474
packages/web/src/subtle.ts Normal file
View File

@ -0,0 +1,474 @@
import * as asn1Schema from "@peculiar/asn1-schema";
import * as jsonSchema from "@peculiar/json-schema";
import * as core from "@peculiar/webcrypto-core";
import * as types from "@peculiar/webcrypto-types";
import * as pvtsutils from "pvtsutils";
import { Debug } from "./debug";
import { Browser, BrowserInfo } from "./helper";
import { CryptoKey } from "./key";
import {
AesCbcProvider, AesCtrProvider, AesEcbProvider, AesGcmProvider, AesKwProvider,
DesCbcProvider, DesEde3CbcProvider,
EcCrypto, EcdhProvider,
EcdsaProvider,
HmacProvider,
Pbkdf2Provider,
RsaEsProvider, RsaOaepProvider, RsaPssProvider, RsaSsaProvider,
Sha1Provider, Sha256Provider, Sha512Provider,
EdDsaProvider, EcdhEsProvider, Sha3256Provider, Sha3384Provider, Sha3512Provider, Shake128Provider, Shake256Provider,
} from "./mechs";
import { getOidByNamedCurve } from "./mechs/ec/helper";
import { nativeSubtle } from "./native";
import { WrappedNativeCryptoKey } from "./wrapped_native_key";
type SubtleMethods = keyof types.SubtleCrypto;
function errorToString(error: unknown) {
return error instanceof Error ? error.message : "Unknown error";
}
export class SubtleCrypto extends core.SubtleCrypto {
private static readonly methods: SubtleMethods[] = ["digest", "importKey", "exportKey", "sign", "verify", "generateKey", "encrypt", "decrypt", "deriveBits", "deriveKey", "wrapKey", "unwrapKey"];
/**
* Returns true if key is CryptoKey and is not liner key
* > WARN Some browsers doesn't have CryptKey class in `self`.
* @param key
*/
private static isAnotherKey(key: any): key is types.CryptoKey {
if (typeof key === "object"
&& typeof key.type === "string"
&& typeof key.extractable === "boolean"
&& typeof key.algorithm === "object") {
return !(key instanceof CryptoKey);
}
return false;
}
public readonly browserInfo = BrowserInfo();
public constructor() {
super();
//#region AES
this.providers.set(new AesCbcProvider());
this.providers.set(new AesCtrProvider());
this.providers.set(new AesEcbProvider());
this.providers.set(new AesGcmProvider());
this.providers.set(new AesKwProvider());
//#endregion
//#region DES
this.providers.set(new DesCbcProvider());
this.providers.set(new DesEde3CbcProvider());
//#endregion
//#region RSA
this.providers.set(new RsaSsaProvider());
this.providers.set(new RsaPssProvider());
this.providers.set(new RsaOaepProvider());
this.providers.set(new RsaEsProvider());
//#endregion
//#region EC
this.providers.set(new EcdsaProvider());
this.providers.set(new EcdhProvider());
//#endregion
//#region SHA
this.providers.set(new Sha1Provider());
this.providers.set(new Sha256Provider());
this.providers.set(new Sha512Provider());
//#endregion
//#region PBKDF
this.providers.set(new Pbkdf2Provider());
//#endregion
//#region HMAC
this.providers.set(new HmacProvider());
//#endregion
//#region EdDSA
this.providers.set(new EdDsaProvider());
//#endregion
//#region ECDH-ES
// TODO Elliptic.js has got issue (https://github.com/indutny/elliptic/issues/243). Uncomment the next line after fix
this.providers.set(new EcdhEsProvider());
//#endregion
//#region SHA3
this.providers.set(new Sha3256Provider());
this.providers.set(new Sha3384Provider());
this.providers.set(new Sha3512Provider());
//#endregion
//#region SHAKE
this.providers.set(new Shake128Provider());
this.providers.set(new Shake256Provider());
//#endregion
}
public override digest(algorithm: types.DigestAlgorithms, data: pvtsutils.BufferSource, ...args: any[]): Promise<ArrayBuffer>;
public override digest(...args: any[]): Promise<ArrayBuffer> {
return this.wrapNative("digest", ...args);
}
public override async importKey(format: types.KeyFormat, keyData: types.JsonWebKey | pvtsutils.BufferSource, algorithm: types.AlgorithmIdentifier, extractable: boolean, keyUsages: types.KeyUsage[], ...args: any[]): Promise<types.CryptoKey>;
public override async importKey(...args: any[]): Promise<types.CryptoKey> {
this.fixFirefoxEcImportPkcs8(args);
return this.wrapNative("importKey", ...args);
}
public override async exportKey(format: "raw" | "spki" | "pkcs8", key: types.CryptoKey, ...args: any[]): Promise<ArrayBuffer>;
public override async exportKey(format: "jwk", key: types.CryptoKey, ...args: any[]): Promise<types.JsonWebKey>;
public override async exportKey(format: types.KeyFormat, key: types.CryptoKey, ...args: any[]): Promise<ArrayBuffer | types.JsonWebKey>;
public override async exportKey(...args: any[]): Promise<ArrayBuffer | types.JsonWebKey> {
return await this.fixFirefoxEcExportPkcs8(args) ||
await this.wrapNative("exportKey", ...args);
}
public override async generateKey(algorithm: types.RsaHashedKeyGenParams | types.EcKeyGenParams, extractable: boolean, keyUsages: types.KeyUsage[], ...args: any[]): Promise<types.CryptoKeyPair>;
public override async generateKey(algorithm: types.AesKeyGenParams | types.HmacKeyGenParams | types.Pbkdf2Params, extractable: boolean, keyUsages: types.KeyUsage[], ...args: any[]): Promise<types.CryptoKey>;
public override async generateKey(algorithm: types.AlgorithmIdentifier, extractable: boolean, keyUsages: Iterable<types.KeyUsage>, ...args: any[]): Promise<types.CryptoKeyPair | types.CryptoKey>;
public override async generateKey(...args: any[]): Promise<types.CryptoKeyPair | types.CryptoKey> {
return this.wrapNative("generateKey", ...args);
}
public override async sign(algorithm: types.SignAlgorithms, key: types.CryptoKey, data: pvtsutils.BufferSource, ...args: any[]): Promise<ArrayBuffer>;
public override async sign(...args: any[]): Promise<ArrayBuffer> {
return this.wrapNative("sign", ...args);
}
public override async verify(algorithm: types.SignAlgorithms, key: types.CryptoKey, signature: pvtsutils.BufferSource, data: pvtsutils.BufferSource, ...args: any[]): Promise<boolean>;
public override async verify(...args: any[]): Promise<boolean> {
return this.wrapNative("verify", ...args);
}
public override async encrypt(algorithm: types.AlgorithmIdentifier, key: types.CryptoKey, data: pvtsutils.BufferSource, ...args: any[]): Promise<ArrayBuffer>;
public override async encrypt(...args: any[]): Promise<ArrayBuffer> {
return this.wrapNative("encrypt", ...args);
}
public override async decrypt(algorithm: types.AlgorithmIdentifier, key: types.CryptoKey, data: pvtsutils.BufferSource, ...args: any[]): Promise<ArrayBuffer>;
public override async decrypt(...args: any[]): Promise<ArrayBuffer> {
return this.wrapNative("decrypt", ...args);
}
public override async wrapKey(format: types.KeyFormat, key: types.CryptoKey, wrappingKey: types.CryptoKey, wrapAlgorithm: types.AlgorithmIdentifier, ...args: any[]): Promise<ArrayBuffer>;
public override async wrapKey(...args: any[]): Promise<ArrayBuffer> {
return this.wrapNative("wrapKey", ...args);
}
public override async unwrapKey(format: types.KeyFormat, wrappedKey: pvtsutils.BufferSource, unwrappingKey: types.CryptoKey, unwrapAlgorithm: types.AlgorithmIdentifier, unwrappedKeyAlgorithm: types.AlgorithmIdentifier, extractable: boolean, keyUsages: types.KeyUsage[], ...args: any[]): Promise<types.CryptoKey>;
public override async unwrapKey(...args: any[]): Promise<types.CryptoKey> {
return this.wrapNative("unwrapKey", ...args);
}
public override async deriveBits(algorithm: types.AlgorithmIdentifier, baseKey: types.CryptoKey, length: number, ...args: any[]): Promise<ArrayBuffer>;
public override async deriveBits(...args: any[]): Promise<ArrayBuffer> {
return this.wrapNative("deriveBits", ...args);
}
public override async deriveKey(algorithm: types.AlgorithmIdentifier, baseKey: types.CryptoKey, derivedKeyType: types.AlgorithmIdentifier, extractable: boolean, keyUsages: types.KeyUsage[], ...args: any[]): Promise<types.CryptoKey>;
public override async deriveKey(...args: any[]): Promise<types.CryptoKey> {
return this.wrapNative("deriveKey", ...args);
}
private async wrapNative(method: SubtleMethods, ...args: any[]) {
if (~["generateKey", "unwrapKey", "deriveKey", "importKey"].indexOf(method)) {
this.fixAlgorithmName(args);
}
try {
if (method !== "digest" || !args.some((a) => a instanceof CryptoKey)) {
const nativeArgs = this.fixNativeArguments(method, args);
Debug.info(`Call native '${method}' method`, nativeArgs);
if (!nativeSubtle) {
throw new Error("Native SubtleCrypto is empty");
}
const res = await (nativeSubtle as any)[method].apply(nativeSubtle, nativeArgs);
return this.fixNativeResult(method, args, res);
}
} catch (e) {
Debug.warn(`Error on native '${method}' calling. ${errorToString(e)}`, e);
}
if (method === "wrapKey") {
try {
Debug.info(`Trying to wrap key by using native functions`, args);
// wrapKey(format, key, wrappingKey, wrapAlgorithm);
// indexes 0 1 2 3
const data = await this.exportKey(args[0], args[1]);
const keyData = (args[0] === "jwk") ? pvtsutils.Convert.FromUtf8String(JSON.stringify(data)) : data as ArrayBuffer;
const res = await this.encrypt(args[3], args[2], keyData);
return res;
} catch (e) {
Debug.warn(`Cannot wrap key by native functions. ${errorToString(e)}`, e);
}
}
if (method === "unwrapKey") {
try {
Debug.info(`Trying to unwrap key by using native functions`, args);
// unwrapKey(format, wrappedKey, unwrappingKey, unwrapAlgorithm, unwrappedKeyAlgorithm, extractable, keyUsages);
// indexes 0 1 2 3 4 5 6
const data = await this.decrypt(args[3], args[2], args[1]);
const keyData = (args[0] === "jwk") ? JSON.parse(pvtsutils.Convert.ToUtf8String(data)) : data;
const res = await this.importKey(args[0], keyData, args[4], args[5], args[6]);
return res;
} catch (e) {
Debug.warn(`Cannot unwrap key by native functions. ${errorToString(e)}`, e);
}
}
if (method === "deriveKey") {
try {
Debug.info(`Trying to derive key by using native functions`, args);
const data = await this.deriveBits(args[0], args[1], args[2].length);
const res = await this.importKey("raw", data, args[2], args[3], args[4]);
return res;
} catch (e) {
Debug.warn(`Cannot derive key by native functions. ${errorToString(e)}`, e);
}
}
if (method === "deriveBits" || method === "deriveKey") {
// Cast public keys from algorithm
for (const arg of args) {
if (typeof arg === "object" && arg.public && SubtleCrypto.isAnotherKey(arg.public)) {
arg.public = await this.castKey(arg.public);
}
}
}
// Cast native keys to liner keys
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (SubtleCrypto.isAnotherKey(arg)) {
args[i] = await this.castKey(arg);
}
}
const fn = super[method] as any;
if (typeof fn === "function") {
return fn.apply(this, args);
}
throw new Error("Incorrect type of 'method'. Must be 'function'.");
}
private fixNativeArguments(method: SubtleMethods, args: any[]) {
const res = [...args];
if (method === "importKey") {
if (this.browserInfo.name === Browser.IE && res[0]?.toLowerCase?.() === "jwk" && !pvtsutils.BufferSourceConverter.isBufferSource(res[1])) {
// IE11 uses ArrayBuffer instead of JSON object
res[1] = pvtsutils.Convert.FromUtf8String(JSON.stringify(res[1]));
}
}
if (this.browserInfo.name === Browser.IE && args[1] instanceof WrappedNativeCryptoKey) {
// Fix algs for IE11
switch (method) {
case "sign":
case "verify":
case "encrypt":
case "decrypt":
res[0] = { ...this.prepareAlgorithm(res[0]), hash: (res[1]?.algorithm as types.RsaHashedKeyAlgorithm)?.hash?.name };
break;
case "wrapKey":
case "unwrapKey":
res[4] = { ...this.prepareAlgorithm(res[4]), hash: (res[3]?.algorithm as types.RsaHashedKeyAlgorithm)?.hash?.name };
break;
}
}
for (let i = 0; i < res.length; i++) {
const arg = res[i];
if (arg instanceof WrappedNativeCryptoKey) {
// Convert wrapped key to Native CryptoKey
res[i] = arg.getNative();
}
}
return res;
}
private fixNativeResult(method: SubtleMethods, args: any[], res: any): any {
if (this.browserInfo.name === Browser.IE) {
if (method === "exportKey") {
if (args[0]?.toLowerCase?.() === "jwk" && res instanceof ArrayBuffer) {
// IE11 uses ArrayBuffer instead of JSON object
return JSON.parse(pvtsutils.Convert.ToUtf8String(res));
}
}
// wrap IE11 native key
if ("privateKey" in res) {
const privateKeyUsages = ["sign", "decrypt", "unwrapKey", "deriveKey", "deriveBits"];
const publicKeyUsages = ["verify", "encrypt", "wrapKey"];
return {
privateKey: this.wrapNativeKey(res.privateKey, args[0], args[1], args[2].filter((o: string) => privateKeyUsages.includes(o))),
publicKey: this.wrapNativeKey(res.publicKey, args[0], args[1], args[2].filter((o: string) => publicKeyUsages.includes(o))),
};
} else if ("extractable" in res) {
let algorithm: types.Algorithm;
let usages: types.KeyUsage[];
switch (method) {
case "importKey":
algorithm = args[2];
usages = args[4];
break;
case "unwrapKey":
algorithm = args[4];
usages = args[6];
break;
case "generateKey":
algorithm = args[0];
usages = args[2];
break;
default:
throw new core.OperationError("Cannot wrap native key. Unsupported method in use");
}
return this.wrapNativeKey(res, algorithm, res.extractable, usages);
}
}
return res;
}
private wrapNativeKey(key: types.CryptoKey, algorithm: types.AlgorithmIdentifier, extractable: boolean, keyUsages: types.KeyUsage[]): types.CryptoKey {
if (this.browserInfo.name === Browser.IE) {
const algs = [
"RSASSA-PKCS1-v1_5", "RSA-PSS", "RSA-OAEP",
"AES-CBC", "AES-CTR", "AES-KW", "HMAC",
];
const index = algs.map((o) => o.toLowerCase()).indexOf(key.algorithm.name.toLowerCase());
if (index !== -1) {
const alg = this.prepareAlgorithm(algorithm);
const newAlg: any = {
...key.algorithm,
name: algs[index],
};
if (core.SubtleCrypto.isHashedAlgorithm(alg)) {
newAlg.hash = {
name: (alg.hash as any).name.toUpperCase(),
};
}
Debug.info(`Wrapping ${algs[index]} crypto key to WrappedNativeCryptoKey`);
return new WrappedNativeCryptoKey(newAlg, extractable, key.type, keyUsages, key);
}
}
return key;
}
private async castKey(key: types.CryptoKey) {
Debug.info("Cast native CryptoKey to linter key.", key);
if (!key.extractable) {
throw new Error("Cannot cast unextractable crypto key");
}
const provider = this.getProvider(key.algorithm.name);
const jwk = await this.exportKey("jwk", key);
return provider.importKey("jwk", jwk, key.algorithm, true, key.usages);
}
/**
* Fixes name of the algorithms. Edge doesn't normilize algorithm names in keys
* @param args
*/
private fixAlgorithmName(args: any[]) {
if (this.browserInfo.name === Browser.Edge) {
for (let i = 0; i < args.length; i++) {
const arg = args[0];
if (typeof arg === "string") {
// algorithm
for (const algorithm of this.providers.algorithms) {
if (algorithm.toLowerCase() === arg.toLowerCase()) {
args[i] = algorithm;
break;
}
}
} else if (typeof arg === "object" && typeof arg.name === "string") {
// algorithm.name
for (const algorithm of this.providers.algorithms) {
if (algorithm.toLowerCase() === arg.name.toLowerCase()) {
arg.name = algorithm;
}
if ((typeof arg.hash === "string" && algorithm.toLowerCase() === arg.hash.toLowerCase())
|| (typeof arg.hash === "object" && typeof arg.hash.name === "string" && algorithm.toLowerCase() === arg.hash.name.toLowerCase())) {
arg.hash = { name: algorithm };
}
}
}
}
}
}
/**
* Firefox doesn't support import PKCS8 key for ECDSA/ECDH
*/
private fixFirefoxEcImportPkcs8(args: any[]) {
const preparedAlgorithm = this.prepareAlgorithm(args[2]) as types.EcKeyImportParams;
const algName = preparedAlgorithm.name.toUpperCase();
if (this.browserInfo.name === Browser.Firefox
&& args[0] === "pkcs8"
&& ~["ECDSA", "ECDH"].indexOf(algName)
&& ~["P-256", "P-384", "P-521"].indexOf(preparedAlgorithm.namedCurve)) {
if (!pvtsutils.BufferSourceConverter.isBufferSource(args[1])) {
throw new TypeError("data: Is not ArrayBuffer or ArrayBufferView");
}
const preparedData = pvtsutils.BufferSourceConverter.toArrayBuffer(args[1]);
// Convert PKCS8 to JWK
const keyInfo = asn1Schema.AsnConvert.parse(preparedData, core.asn1.PrivateKeyInfo);
const privateKey = asn1Schema.AsnConvert.parse(keyInfo.privateKey, core.asn1.EcPrivateKey);
const jwk: types.JsonWebKey = jsonSchema.JsonSerializer.toJSON(privateKey);
jwk.ext = true;
jwk.key_ops = args[4];
jwk.crv = preparedAlgorithm.namedCurve;
jwk.kty = "EC";
args[0] = "jwk";
args[1] = jwk;
}
}
/**
* Firefox doesn't support export PKCS8 key for ECDSA/ECDH
*/
private async fixFirefoxEcExportPkcs8(args: any[]) {
try {
if (this.browserInfo.name === Browser.Firefox
&& args[0] === "pkcs8"
&& ~["ECDSA", "ECDH"].indexOf(args[1].algorithm.name)
&& ~["P-256", "P-384", "P-521"].indexOf(args[1].algorithm.namedCurve)) {
const jwk = await this.exportKey("jwk", args[1]);
// Convert JWK to PKCS8
const ecKey = jsonSchema.JsonParser.fromJSON(jwk, { targetSchema: core.asn1.EcPrivateKey });
const keyInfo = new core.asn1.PrivateKeyInfo();
keyInfo.privateKeyAlgorithm.algorithm = EcCrypto.ASN_ALGORITHM;
keyInfo.privateKeyAlgorithm.parameters = asn1Schema.AsnConvert.serialize(
new core.asn1.ObjectIdentifier(getOidByNamedCurve(args[1].algorithm.namedCurve)),
);
keyInfo.privateKey = asn1Schema.AsnConvert.serialize(ecKey);
return asn1Schema.AsnConvert.serialize(keyInfo);
}
} catch (err) {
Debug.error(err);
return null;
}
}
}

28
packages/web/src/typings/des.d.ts vendored Normal file
View File

@ -0,0 +1,28 @@
declare module "des.js" {
type DesOperationType = "encrypt" | "decrypt" | string;
class Cipher {
public update(data: Uint8Array): number[];
public final(): number[];
}
interface IDesCreateParams {
key: Uint8Array;
type: DesOperationType;
iv?: Uint8Array;
}
class DES extends Cipher {
public static create(params: IDesCreateParams): DES;
}
class EDE extends DES {
public static create(params: IDesCreateParams): EDE;
}
class CBC {
public static instantiate(type: typeof DES): typeof DES;
}
}

102
packages/web/src/typings/elliptic.d.ts vendored Normal file
View File

@ -0,0 +1,102 @@
declare namespace EllipticJS {
class EC {
constructor();
genKeyPair(): EllipticKeyPair;
keyFromPrivate(hexString: string, encoding: "hex" | "der"): EllipticKeyPair;
keyFromPrivate(hexString: string | number[] | ArrayBuffer): EllipticKeyPair;
keyFromPublic(hexString: string | number[] | ArrayBuffer, enc?: string): EllipticKeyPair;
}
class BN {
toArray(): number[];
toBytes(): ArrayBuffer;
}
class Point {
x: BN;
y: BN;
}
type EncodeFormat = "hex" | "der";
class EllipticKeyPair {
getSecret(enc?: string): any;
getPrivate(enc?: string): any;
getPublic(enc: "der"): number[];
getPublic(enc: "hex"): string;
getPublic(): Point;
getPublic(enc?: EncodeFormat): string | number[] | Point;
priv?: any;
pub?: Point;
sign(data: number[]): any;
verify(data: number[], hexSignature: string): boolean;
verify(data: number[], signature: object): boolean;
derive(point: any): BN;
}
class EllipticModule {
version: string;
utils: {
assert: Function;
toArray: Function;
zero2: Function;
toHex: Function;
encode: Function;
getNAF: Function;
getJSF: Function;
cachedProperty: Function;
parseBytes: Function;
intFromLE: Function;
};
hmacDRBG: Function;
curves: {
PresetCurve: any;
p192: any;
p224: any;
p256: any;
p384: any;
p521: any;
curve25519: any;
ed25519: any;
secp256k1: any;
};
ec: typeof EC;
eddsa: any;
}
}
declare const elliptic: {
ec: (namedCurve: string) => EllipticJS.EC;
};
declare module "elliptic" {
const version: string;
const utils: {
assert: Function;
toArray: Function;
zero2: Function;
toHex: Function;
encode: Function;
getNAF: Function;
getJSF: Function;
cachedProperty: Function;
parseBytes: Function;
intFromLE: Function;
};
const hmacDRBG: Function;
const curves: {
PresetCurve: any;
p192: any;
p224: any;
p256: any;
p384: any;
p521: any;
curve25519: any;
ed25519: any;
secp256k1: any;
};
function ec(namedCurve: string): EllipticJS.EC;
const eddsa: any;
}

4
packages/web/src/typings/index.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
interface AlgorithmConverter {
jwk2alg(alg: string): Algorithm;
alg2jwk(alg: Algorithm): string;
}

View File

@ -0,0 +1,5 @@
import * as types from "@peculiar/webcrypto-types";
export function isAlgorithm<T extends types.Algorithm>(algorithm: types.Algorithm, name: string): algorithm is T {
return algorithm.name.toUpperCase() === name.toUpperCase();
}

View File

@ -0,0 +1,23 @@
import * as types from "@peculiar/webcrypto-types";
import { CryptoKey } from "./key";
export class WrappedNativeCryptoKey extends CryptoKey {
#nativeKey: types.CryptoKey;
constructor(
algorithm: types.KeyAlgorithm,
extractable: boolean,
type: types.KeyType,
usages: types.KeyUsage[],
nativeKey: types.CryptoKey) {
super(algorithm, extractable, type, usages);
this.#nativeKey = nativeKey;
}
// @internal
public getNative() {
return this.#nativeKey;
}
}

View File

@ -0,0 +1,901 @@
import * as types from "@peculiar/webcrypto-types";
import * as pvtsutils from "pvtsutils";
import { Browser } from "../src/helper";
import { browser, testCrypto, webCrypto } from "./utils";
context("AES", () => {
testCrypto(webCrypto, [
//#region AES-CBC
{
name: "AES-128-CBC",
actions: {
generateKey: [
{
algorithm: { name: "AES-CBC", length: 128 } as types.AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
],
encrypt: [
{
algorithm: {
name: "AES-CBC",
iv: pvtsutils.Convert.FromUtf8String("1234567890abcdef"),
} as types.AesCbcParams,
data: pvtsutils.Convert.FromUtf8String("test message"),
encData: pvtsutils.Convert.FromHex("d5df3ea1598defe7446420802baef28e"),
key: {
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef"),
algorithm: { name: "AES-CBC" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
},
],
import: [
{
name: "raw",
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef"),
algorithm: "AES-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "wrong key size",
error: true,
format: "raw",
data: pvtsutils.Convert.FromUtf8String("12345678"),
algorithm: "AES-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A128CBC",
k: "MTIzNDU2Nzg5MGFiY2RlZg",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
wrapKey: [
{
key: {
format: "raw",
algorithm: "AES-CBC",
data: pvtsutils.Convert.FromBase64Url("AQIDBAUGBwgJAAECAwQFBg"),
extractable: true,
keyUsages: ["wrapKey", "unwrapKey"],
},
wKey: {
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef"),
algorithm: "AES-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
algorithm: {
name: "AES-CBC",
iv: pvtsutils.Convert.FromUtf8String("1234567890abcdef"),
} as types.AesCbcParams,
wrappedKey: pvtsutils.Convert.FromHex("c630c4bf95977db13f386cc950b18e98521d54c4fda0ba15b2884d2695638bd9"),
},
],
},
},
{
name: "AES-192-CBC",
actions: {
generateKey: [
{
algorithm: { name: "AES-CBC", length: 192 } as types.AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
],
encrypt: [
{
algorithm: {
name: "AES-CBC",
iv: pvtsutils.Convert.FromUtf8String("1234567890abcdef"),
} as types.AesCbcParams,
data: pvtsutils.Convert.FromUtf8String("test message"),
encData: pvtsutils.Convert.FromHex("67d0b3022149829bf009ad4aff19963a"),
key: {
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef12345678"),
algorithm: { name: "AES-CBC" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
},
],
import: [
{
name: "raw",
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef12345678"),
algorithm: "AES-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A192CBC",
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
{
name: "AES-256-CBC",
actions: {
generateKey: [
{
algorithm: { name: "AES-CBC", length: 256 } as types.AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
],
encrypt: [
{
algorithm: {
name: "AES-CBC",
iv: pvtsutils.Convert.FromUtf8String("1234567890abcdef"),
} as types.AesCbcParams,
data: pvtsutils.Convert.FromUtf8String("test message"),
encData: pvtsutils.Convert.FromHex("d827c1c6aee9f0f552c62f30ddee83af"),
key: {
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef1234567809abcdef"),
algorithm: { name: "AES-CBC" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
},
],
import: [
{
name: "raw",
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef1234567890abcdef"),
algorithm: "AES-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A256CBC",
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWY",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
//#endregion
//#region AES-CTR
{
// skip: browser.name === Browser.Edge,
name: "AES-128-CTR",
actions: {
generateKey: [
{
algorithm: { name: "AES-CTR", length: 128 } as types.AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
encrypt: [
{
algorithm: {
name: "AES-CTR",
counter: pvtsutils.Convert.FromUtf8String("1234567890abcdef"),
length: 128,
} as types.AesCtrParams,
data: pvtsutils.Convert.FromUtf8String("test message"),
encData: pvtsutils.Convert.FromHex("e1d561c49ce4eb2f448f8a00"),
key: {
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef"),
algorithm: { name: "AES-CTR" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
},
],
import: [
{
name: "raw",
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef"),
algorithm: "AES-CTR",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A128CTR",
k: "MTIzNDU2Nzg5MGFiY2RlZg",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-CTR",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
{
name: "AES-192-CTR",
skip: browser.name === Browser.Chrome // Chrome doesn't implement this alg
|| browser.name === Browser.Edge,
actions: {
generateKey: [
{
algorithm: { name: "AES-CTR", length: 192 } as types.AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
],
encrypt: [
{
algorithm: {
name: "AES-CTR",
counter: pvtsutils.Convert.FromUtf8String("1234567890abcdef"),
length: 128,
} as types.AesCtrParams,
data: pvtsutils.Convert.FromUtf8String("test message"),
encData: pvtsutils.Convert.FromHex("55a00e2851f00aba53bbd02c"),
key: {
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef12345678"),
algorithm: { name: "AES-CTR" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
},
],
import: [
{
name: "raw",
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef12345678"),
algorithm: "AES-CTR",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A192CTR",
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-CTR",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
{
name: "AES-256-CTR",
skip: browser.name === Browser.Edge,
actions: {
generateKey: [
{
algorithm: { name: "AES-CTR", length: 256 } as types.AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
],
encrypt: [
{
algorithm: {
name: "AES-CTR",
counter: pvtsutils.Convert.FromUtf8String("1234567890abcdef"),
length: 128,
} as types.AesCtrParams,
data: pvtsutils.Convert.FromUtf8String("test message"),
encData: pvtsutils.Convert.FromHex("8208d011a20162c8af7a9ce5"),
key: {
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef1234567809abcdef"),
algorithm: { name: "AES-CTR" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
},
],
import: [
{
name: "raw",
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef1234567890abcdef"),
algorithm: "AES-CTR",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A256CTR",
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWY",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-CTR",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
//#endregion
//#region AES-GCM
{
name: "AES-128-GCM",
actions: {
generateKey: [
{
algorithm: { name: "AES-GCM", length: 128 } as types.AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
encrypt: [
{
algorithm: {
name: "AES-GCM",
iv: pvtsutils.Convert.FromUtf8String("1234567890ab"),
} as types.AesGcmParams,
data: pvtsutils.Convert.FromUtf8String("test message"),
encData: pvtsutils.Convert.FromHex("68d645649ddf8152a253304d698185072f28cdcf7644ac6064bcb240"),
key: {
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef"),
algorithm: { name: "AES-GCM" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
},
],
import: [
{
name: "raw",
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef"),
algorithm: "AES-GCM",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A128GCM",
k: "MTIzNDU2Nzg5MGFiY2RlZg",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-GCM",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
{
name: "AES-192-GCM",
actions: {
generateKey: [
{
algorithm: { name: "AES-GCM", length: 192 } as types.AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
],
encrypt: [
{
algorithm: {
name: "AES-GCM",
iv: pvtsutils.Convert.FromUtf8String("1234567890ab"),
} as types.AesGcmParams,
data: pvtsutils.Convert.FromUtf8String("test message"),
encData: pvtsutils.Convert.FromHex("d8eab579ed2418f41ca9c4567226f54cb391d3ca2cb6819dace35691"),
key: {
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef12345678"),
algorithm: { name: "AES-GCM" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
},
],
import: [
{
name: "raw",
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef12345678"),
algorithm: "AES-GCM",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A192GCM",
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-GCM",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
{
name: "AES-256-GCM",
actions: {
generateKey: [
{
algorithm: { name: "AES-GCM", length: 256 } as types.AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
],
encrypt: [
{
algorithm: {
name: "AES-GCM",
iv: pvtsutils.Convert.FromUtf8String("1234567890ab"),
} as types.AesGcmParams,
data: pvtsutils.Convert.FromUtf8String("test message"),
encData: pvtsutils.Convert.FromHex("f961f2aadbe689ffce86fcaf2619ab647950afcf19e55b71b857c79d"),
key: {
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef1234567809abcdef"),
algorithm: { name: "AES-GCM" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
},
],
import: [
{
name: "raw",
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef1234567890abcdef"),
algorithm: "AES-GCM",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A256GCM",
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWY",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-GCM",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
//#endregion
//#region AES-KW
{
name: "AES-128-KW",
skip: typeof module !== "undefined", // skip for nodejs
actions: {
generateKey: [
{
algorithm: { name: "AES-KW", length: 128 } as types.AesKeyGenParams,
extractable: true,
keyUsages: ["wrapKey", "unwrapKey"],
},
],
wrapKey: [
{
skip: browser.name === Browser.Firefox, // Firefox: Operation is not supported on unwrapKey
key: {
format: "raw",
algorithm: "AES-KW",
data: pvtsutils.Convert.FromHex("000102030405060708090A0B0C0D0E0F"),
extractable: true,
keyUsages: ["wrapKey", "unwrapKey"],
},
wKey: {
format: "raw",
data: pvtsutils.Convert.FromHex("00112233445566778899AABBCCDDEEFF"),
algorithm: "AES-KW",
extractable: true,
keyUsages: ["wrapKey", "unwrapKey"],
},
algorithm: {
name: "AES-KW",
},
wrappedKey: pvtsutils.Convert.FromHex("1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5"),
},
],
import: [
{
name: "raw",
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef"),
algorithm: "AES-KW",
extractable: true,
keyUsages: ["wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A128KW",
k: "MTIzNDU2Nzg5MGFiY2RlZg",
ext: true,
key_ops: ["wrapKey", "unwrapKey"],
},
algorithm: "AES-KW",
extractable: true,
keyUsages: ["wrapKey", "unwrapKey"],
},
],
},
},
{
name: "AES-192-KW",
skip: typeof module !== "undefined" // skip for nodejs
|| browser.name === Browser.Chrome, // Chrome doesn't support AES-192-KW
actions: {
generateKey: [
{
algorithm: { name: "AES-KW", length: 192 } as types.AesKeyGenParams,
extractable: true,
keyUsages: ["wrapKey", "unwrapKey"],
},
],
wrapKey: [
{
skip: browser.name === Browser.Firefox, // Firefox: Operation is not supported on unwrapKey
key: {
format: "raw",
algorithm: "AES-KW",
data: pvtsutils.Convert.FromHex("000102030405060708090A0B0C0D0E0F1011121314151617"),
extractable: true,
keyUsages: ["wrapKey", "unwrapKey"],
},
wKey: {
format: "raw",
data: pvtsutils.Convert.FromHex("00112233445566778899AABBCCDDEEFF0001020304050607"),
algorithm: "AES-KW",
extractable: true,
keyUsages: ["wrapKey", "unwrapKey"],
},
algorithm: {
name: "AES-KW",
},
wrappedKey: pvtsutils.Convert.FromHex("031D33264E15D33268F24EC260743EDCE1C6C7DDEE725A936BA814915C6762D2"),
},
],
import: [
{
name: "raw",
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef12345678"),
algorithm: "AES-KW",
extractable: true,
keyUsages: ["wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A192KW",
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4",
ext: true,
key_ops: ["wrapKey", "unwrapKey"],
},
algorithm: "AES-KW",
extractable: true,
keyUsages: ["wrapKey", "unwrapKey"],
},
],
},
},
{
name: "AES-256-KW",
skip: typeof module !== "undefined", // skip for nodejs
actions: {
generateKey: [
{
algorithm: { name: "AES-KW", length: 256 } as types.AesKeyGenParams,
extractable: true,
keyUsages: ["wrapKey", "unwrapKey"],
},
],
wrapKey: [
{
skip: browser.name === Browser.Firefox, // Firefox: Operation is not supported on unwrapKey
key: {
format: "raw",
algorithm: "AES-KW",
data: pvtsutils.Convert.FromHex("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"),
extractable: true,
keyUsages: ["wrapKey", "unwrapKey"],
},
wKey: {
format: "raw",
data: pvtsutils.Convert.FromHex("00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F"),
algorithm: "AES-KW",
extractable: true,
keyUsages: ["wrapKey", "unwrapKey"],
},
algorithm: {
name: "AES-KW",
},
wrappedKey: pvtsutils.Convert.FromHex("28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21"),
},
],
import: [
{
name: "raw",
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef1234567890abcdef"),
algorithm: "AES-KW",
extractable: true,
keyUsages: ["wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A256KW",
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWY",
ext: true,
key_ops: ["wrapKey", "unwrapKey"],
},
algorithm: "AES-KW",
extractable: true,
keyUsages: ["wrapKey", "unwrapKey"],
},
],
},
},
//#endregion
//#region AES-ECB
{
name: "AES-128-ECB",
actions: {
generateKey: [
{
algorithm: { name: "AES-ECB", length: 128 } as types.AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
],
encrypt: [
{
algorithm: {
name: "AES-ECB",
} as types.Algorithm,
data: pvtsutils.Convert.FromUtf8String("test message"),
encData: pvtsutils.Convert.FromHex("c6ec2f91a9f48e10062ae41e86cb299f"),
key: {
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef"),
algorithm: { name: "AES-ECB" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
},
],
import: [
{
name: "raw",
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef"),
algorithm: "AES-ECB",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A128ECB",
k: "MTIzNDU2Nzg5MGFiY2RlZg",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-ECB",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
wrapKey: [
{
key: {
format: "raw",
algorithm: "AES-ECB",
data: pvtsutils.Convert.FromBase64Url("AQIDBAUGBwgJAAECAwQFBg"),
extractable: true,
keyUsages: ["wrapKey", "unwrapKey"],
},
wKey: {
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef"),
algorithm: "AES-ECB",
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
algorithm: {
name: "AES-ECB",
} as types.Algorithm,
wrappedKey: pvtsutils.Convert.FromHex("039ec14b350bd92efd02dac2c01cdee6ea9953cfbdc067f20f5f47bb4459da79"),
},
],
},
},
{
name: "AES-192-ECB",
actions: {
generateKey: [
{
algorithm: { name: "AES-ECB", length: 192 } as types.AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
],
encrypt: [
{
algorithm: {
name: "AES-ECB",
} as types.Algorithm,
data: pvtsutils.Convert.FromUtf8String("test message"),
encData: pvtsutils.Convert.FromHex("8c9f297827ad6aaa9e7501e79fb45ca5"),
key: {
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef12345678"),
algorithm: { name: "AES-ECB" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
},
],
import: [
{
name: "raw",
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef12345678"),
algorithm: "AES-ECB",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A192ECB",
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-ECB",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
{
name: "AES-256-ECB",
actions: {
generateKey: [
{
algorithm: { name: "AES-ECB", length: 256 } as types.AesKeyGenParams,
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
],
encrypt: [
{
algorithm: {
name: "AES-ECB",
} as types.Algorithm,
data: pvtsutils.Convert.FromUtf8String("test message"),
encData: pvtsutils.Convert.FromHex("84ccef71a364b112eb2b3b8b99587a95"),
key: {
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef1234567809abcdef"),
algorithm: { name: "AES-ECB" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
},
},
],
import: [
{
name: "raw",
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef1234567890abcdef"),
algorithm: "AES-ECB",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "A256ECB",
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWY",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "AES-ECB",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
//#endregion
]);
});

View File

@ -0,0 +1,125 @@
import * as types from "@peculiar/webcrypto-types";
import * as pvtsutils from "pvtsutils";
import { testCrypto, webCrypto } from "./utils";
context("DES", () => {
testCrypto(webCrypto, [
{
name: "DES-CBC",
actions: {
generateKey: [
{
algorithm: { name: "DES-CBC", length: 64 } as types.DesKeyGenParams,
extractable: false,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
encrypt: [
{
algorithm: {
name: "DES-CBC",
iv: pvtsutils.Convert.FromUtf8String("12345678"),
} as types.DesParams,
data: pvtsutils.Convert.FromUtf8String("test message"),
encData: pvtsutils.Convert.FromHex("3af3f901ff01fe0102dfbbf37d9bdb94"),
key: {
format: "raw",
algorithm: { name: "DES-CBC" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
data: pvtsutils.Convert.FromUtf8String("12345678"),
},
},
],
import: [
{
name: "raw",
format: "raw",
data: pvtsutils.Convert.FromUtf8String("12345678"),
algorithm: "DES-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "DES-CBC",
k: "MTIzNDU2Nzg",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "DES-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
{
name: "DES-EDE3-CBC",
actions: {
generateKey: [
{
algorithm: { name: "DES-EDE3-CBC", length: 192 } as types.DesKeyGenParams,
extractable: false,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
encrypt: [
{
algorithm: {
name: "DES-EDE3-CBC",
iv: pvtsutils.Convert.FromUtf8String("12345678"),
} as types.DesParams,
data: pvtsutils.Convert.FromUtf8String("test message"),
encData: pvtsutils.Convert.FromHex("b9ef20e7db926490e4ff8680d99d2141"),
key: {
format: "raw",
algorithm: { name: "DES-EDE3-CBC" },
extractable: true,
keyUsages: ["encrypt", "decrypt"],
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef12345678"),
},
},
],
import: [
{
name: "raw",
format: "raw",
data: pvtsutils.Convert.FromUtf8String("1234567890abcdef12345678"),
algorithm: "DES-EDE3-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "wrong key size",
error: true,
format: "raw",
data: pvtsutils.Convert.FromUtf8String("12345678"),
algorithm: "DES-EDE3-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
{
name: "jwk",
format: "jwk",
data: {
kty: "oct",
alg: "3DES-CBC",
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4",
ext: true,
key_ops: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
algorithm: "DES-EDE3-CBC",
extractable: true,
keyUsages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"],
},
],
},
},
]);
});

View File

@ -0,0 +1,555 @@
import * as types from "@peculiar/webcrypto-types";
import * as assert from "assert";
import * as pvtsutils from "pvtsutils";
import { Browser } from "../src/helper";
import { crypto } from "../src/lib";
import { browser, ITestGenerateKeyAction, testCrypto, webCrypto } from "./utils";
context("EC", () => {
testCrypto(webCrypto, [
{
name: "ECDSA",
actions: {
generateKey: ["P-256", "P-384", "P-521", "K-256"].map((namedCurve) => {
return {
name: namedCurve,
algorithm: {
name: "ECDSA",
namedCurve,
} as types.EcKeyGenParams,
extractable: false,
keyUsages: ["sign", "verify"],
} as ITestGenerateKeyAction;
}),
import: [
{
name: "JWK public key P-256",
format: "jwk",
data: {
crv: "P-256",
ext: true,
key_ops: ["verify"],
kty: "EC",
x: "dJ9C3NyXDa3fMeZ477NWdp9W6faytA7A_U1ub-tyRcs",
y: "aS0_VVe_SeIm8w5TBWjUEco7us6EJUMPKKJaIh36Lho",
},
algorithm: {
name: "ECDSA",
namedCurve: "P-256",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
{
name: "JWK public key P-384",
format: "jwk",
data: {
crv: "P-384",
ext: true,
key_ops: ["verify"],
kty: "EC",
x: "eHlLZ4jnt_Drs-qoVxK-SZZvhNhi34jLCgyaEZ9XI6bdlK3y1ettm8K5SnLtDhWO",
y: "qbr3pOOViYDQ2wWG-_9pwQ0S8cHV0LP-x9JO5dl-dsFYtbGix9YH7fRNOl8GkP-6",
},
algorithm: {
name: "ECDSA",
namedCurve: "P-384",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
{
name: "JWK public key P-521",
format: "jwk",
data: {
crv: "P-521",
ext: true,
key_ops: ["verify"],
kty: "EC",
x: "Adqn62IVQX8LIauAXrUtxH05DHlRygKcsP9qWAnd9tfJvpaG7bzIs16WMEUe1V-f4AxbQJceU4xCP8dJppK_fzdC",
y: "AEo3s1eExCOvpuBtBWnWlr7TuFhq_fMzqX9eqDHiy8qWl4I_koQtMePodrAc85mVrJAjvsa77Y3Ul3QtIWpXXBqa",
},
algorithm: {
name: "ECDSA",
namedCurve: "P-521",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
//#region SPKI
{
skip: browser.name === Browser.Firefox, // Firefox uses 1.3.132.112 instead of 1.2.840.10045.2.1 for algorithm
name: "SPKI P-256",
format: "spki",
data: pvtsutils.Convert.FromBase64("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoZMMqyfA16N6bvloFHmalk/SGMisr3zSXFZdR8F9UkaY7hF13hHiQtwp2YO+1zd7jwYi1Y7SMA9iUrC+ap2OCw=="),
algorithm: {
name: "ECDSA",
namedCurve: "P-256",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
{
skip: browser.name === Browser.Firefox, // Firefox uses 1.3.132.112 instead of 1.2.840.10045.2.1 for algorithm
name: "SPKI P-384",
format: "spki",
data: pvtsutils.Convert.FromBase64("MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8Kf5Wv21nksy0LuMlkMZv9sxTVAmzNWt81b6MVlYuzxl9D2/obwoVp86pTe4BM79gWWj8pfLc1XrjaIyMSrV8+05IejRLB3i4c0KTGA6QARGm3/AOm0MbTt6kMQF7drL"),
algorithm: {
name: "ECDSA",
namedCurve: "P-384",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
{
skip: browser.name === Browser.Firefox, // Firefox uses 1.3.132.112 instead of 1.2.840.10045.2.1 for algorithm
name: "SPKI P-521",
format: "spki",
data: pvtsutils.Convert.FromBase64("MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQB+/g37ii0T5iLHCAaXcYRRoNpT0LhfeAr88OwQY4cUpQm1S9lkR0EVUtyuYrYsMB8FarhAZYsLtOiyhjl/Y5f+lQAZ6veWILhbDcbrSNhTPSp3wamAm8QT3EjPUkJlYjHefuAUBIYS9pl5FWjK1pI9fkYe3bdAemkjP1ccHVzqZU9sjg="),
algorithm: {
name: "ECDSA",
namedCurve: "P-521",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
//#endregion
//#region RAW
{
name: "RAW P-256",
format: "raw",
data: pvtsutils.Convert.FromBase64("BEehen4AavxgJkx5EPZpBeopzgZuY+1i3cMR9iYdZj+IY7/h98Q/GboC2BKS6lT0hEyt6y1DFFXj8ytuof4zXR4="),
algorithm: {
name: "ECDSA",
namedCurve: "P-256",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
{
name: "RAW P-384",
format: "raw",
data: pvtsutils.Convert.FromBase64("BGYoCpP3Qv4o0s2GWg5xFnasdkI8h6K/LeBm4TV+9HCsqnoXFUJDM5SDeZ0rcCAUUuaPJVn5sedPEKEGW80zmLM1rBOG2RzaBq+uhEJkLpibongnzMZNX2LB58wGJ05f2g=="),
algorithm: {
name: "ECDSA",
namedCurve: "P-384",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
{
name: "RAW P-521",
format: "raw",
data: pvtsutils.Convert.FromBase64("BABIiZ3f90HQsl4CYHt7Q1WnOIOs+dxeecfQrew/z+73yI/bUrMlmR3mOVARtvg7ZPX7h3lSSqzA1Vv6iv7bPYekcwDKQPeLJkem//H7zY8xtKY+YrYnLUVv6vPE9jyk2vYkj8QPxQRdeIT5bzY2BzTiTcLHDwi2+w2Eonkt7M+zb4G6xw=="),
algorithm: {
name: "ECDSA",
namedCurve: "P-521",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
//#endregion
//#region JWK Private key
{
name: "JWK private key P-256",
format: "jwk",
data: {
crv: "P-256",
d: "RIrfLaesGcEeNy7fOoVIkgMiImJOFw1Y44kdrtK_49I",
ext: true,
key_ops: ["sign"],
kty: "EC",
x: "wJls5KwIfRDxJEvyAlo3G84qNY0HjvsujyxDSMYAlm4",
y: "I61bQbFgnzfDom68P86kRo98fTrV_9HLeqa4gYnGOdw",
},
algorithm: {
name: "ECDSA",
namedCurve: "P-256",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["sign"],
},
{
name: "JWK private key P-384",
format: "jwk",
data: {
crv: "P-384",
d: "4YQRcOD-4LMLEr-qsRhQ1oq8hfPKa66BfGVUv3LUlsf2OU3aFG5FxabG5xFUoAE2",
ext: true,
key_ops: ["sign"],
kty: "EC",
x: "XKewC5QCVW9w-SFyZd3z1vlmCqbYYuJmoGRzKtjwkpYQD_RhNAc3ck29d_t0QmaT",
y: "6oSrri3ry1_8c2NKM8aiaJcjwd146ITViezQ7-BpsE1-wDH18P1QkbmR3-Ho54We",
},
algorithm: {
name: "ECDSA",
namedCurve: "P-384",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["sign"],
},
{
name: "JWK private key P-521",
format: "jwk",
data: {
crv: "P-521",
d: "AItxxufCXVzwPVePNe9Acy8HfbmYeUVkiEyFXdsYRnHxqgDpwucVnIJ44-ZWRpuWu5Ep5KVV3vY9Hp8nJfksi7z2",
ext: true,
key_ops: ["sign"],
kty: "EC",
x: "AJGuTezC-8F-d_0bBpS502OK0z63vo87Dw99a3NUm6gm5pQC1rwu7LcblGqFWOuFBZhsF8I6OFjYvsR-z3u7hhCA",
y: "AFQT8BB9hBf7UwwBUV4im8bFJ7_MD0qOZMVetmdbooMjfec1q3wU5cSoy4LvCnWAaFqu5havUxwnAUuPUWGG_InR",
},
algorithm: {
name: "ECDSA",
namedCurve: "P-521",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["sign"],
},
//#endregion
//#region PKCS8
{
name: "PKCS8 P-256",
format: "pkcs8",
data: pvtsutils.Convert.FromBase64("MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgiVEY5OFo3J7g1BnSw/WEWykY/alrhNmpEBLy/7cNnuGhRANCAAQ4SFnMDGYc5kWv7D0gtgUj/Bzbu0B6Bq6XK1vqOo//2m8FS1D4kYKV4KDfFRWehKEtrMBjjkW6OZcM/n0qZ6Uw"),
algorithm: {
name: "ECDSA",
namedCurve: "P-256",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["sign"],
},
{
name: "PKCS8 P-384",
format: "pkcs8",
data: pvtsutils.Convert.FromBase64("MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCY18ajGPCgLv4aF1UkkohMEaB5MU1MyfkuFQSQVDYHLWFTn8f9czce7aTIDjkCx0OhZANiAAR1fni8TC1N1NdXvx25kJyK3y3rpVVaAmA44Wm9jIFseGmSzm/EgmKOFclSzQdEpSC6jxi3olIJ4iYetjl36Ygfwed/xqrsiV6BUb/ny2mimzk3r0M9H6yvbEVQFd7rEAA="),
algorithm: {
name: "ECDSA",
namedCurve: "P-384",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["sign"],
},
{
name: "PKCS8 P-521",
format: "pkcs8",
data: pvtsutils.Convert.FromBase64("MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIAbHGkGfik5q0l+ZMI70dbpTGWeKy1+c3mG98wHmnpU+d2bArcYDOXcoqg5Ic/pnmtHvxmk+El33u3XogGONKPlouhgYkDgYYABAH16CoJzEx+Oncpeam6ysUG17y9ttNm5Eg8WqD+BJkP9ju3R22I5PVyYYYZ3ICc1IyDGxFCS7leO1N7tqQLaLi8NAEFTkwCy1G6AAK7LbSa1hNC2fUAaC9L8QJNUNJpjgYiXPDmEnaRNT1XXL00Bjo5iMpE2Ddc/Kp6ktTAo2jOMnfmow=="),
algorithm: {
name: "ECDSA",
namedCurve: "P-521",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["sign"],
},
//#endregion
],
sign: [
{
name: "P-256",
key: {
privateKey: {
format: "pkcs8",
data: pvtsutils.Convert.FromBase64("MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgsY5TBHM+9mLXGpFaPmrigl6+jl0XWzazxu1lbwb5KRahRANCAATqDP2L/xxSOlckG+j6oPHfzBE4WpmjA/YE9sP2rXpXW1qe9I/GJ7wjlOTXpqHUxQeBbps8jSvV+A7DzQqzjOst"),
algorithm: {
name: "ECDSA",
namedCurve: "P-256",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["sign"],
},
publicKey: {
format: "spki",
data: pvtsutils.Convert.FromBase64("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE6gz9i/8cUjpXJBvo+qDx38wROFqZowP2BPbD9q16V1tanvSPxie8I5Tk16ah1MUHgW6bPI0r1fgOw80Ks4zrLQ=="),
algorithm: {
name: "ECDSA",
namedCurve: "P-256",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
},
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]),
signature: pvtsutils.Convert.FromBase64("gsTh0IcWfzj3hjjourRgzTIsNa+wcDEDlKnkEA4Jv8ygLF2IDIOXpCD7ocCGo7xlSMGTme78CyrPqWGSz95mZg=="),
algorithm: {
name: "ECDSA",
hash: "SHA-256",
} as types.EcdsaParams,
},
{
name: "K-256",
key: {
privateKey: {
format: "pkcs8",
data: pvtsutils.Convert.FromBase64("MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQg0h6+W+/4eFVP+i79hrzYeiEJ6UrveFYhuhoXRW+g/LGhRANCAASiJU6MaFN5fshUv6X5rCf/RjLQ0nAXj06gBdo3ruYiKZf8daAcYImniAq81PjF0j6eTwCy4bYbkyfBQtrtCTKR"),
algorithm: {
name: "ECDSA",
namedCurve: "K-256",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["sign"],
},
publicKey: {
format: "spki",
data: pvtsutils.Convert.FromBase64("MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEoiVOjGhTeX7IVL+l+awn/0Yy0NJwF49OoAXaN67mIimX/HWgHGCJp4gKvNT4xdI+nk8AsuG2G5MnwULa7QkykQ=="),
algorithm: {
name: "ECDSA",
namedCurve: "K-256",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["verify"],
},
},
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]),
signature: pvtsutils.Convert.FromBase64("lqUTZHqf9v9KcOCw5r5wR1sCt9RPA0ONVW6vqejpoALehd6vtAb+ybVrDEtyUDpBFw9UIRIW6GnXRrAz4KaO4Q=="),
algorithm: {
name: "ECDSA",
hash: "SHA-256",
} as types.EcdsaParams,
},
],
deriveBits: [
{
name: "P-256 128",
key: {
privateKey: {
format: "pkcs8",
data: pvtsutils.Convert.FromBase64("MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgQA7bkTNYlIYVb9+DavBlJ3b08f0892or3XwfscA3tLGhRANCAARzsy+ZcbrNchF7SrpL0hYnGp6ICX77jXUrpMYkq0BuzfaPFWcu9YZH5ASUzQJGz9eCK3mDXEbLCuiHRw3dwkFs"),
algorithm: {
name: "ECDH",
namedCurve: "P-256",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["deriveBits"],
},
publicKey: {
format: "spki",
data: pvtsutils.Convert.FromBase64("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7MvmXG6zXIRe0q6S9IWJxqeiAl++411K6TGJKtAbs32jxVnLvWGR+QElM0CRs/Xgit5g1xGywroh0cN3cJBbA=="),
algorithm: {
name: "ECDH",
namedCurve: "P-256",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: [],
},
},
data: pvtsutils.Convert.FromBase64("Jlc1/Zqi/8mH1oQT8+YfCA=="),
algorithm: {
name: "ECDH",
},
length: 128,
},
{
name: "P-384 192",
key: {
privateKey: {
format: "pkcs8",
data: pvtsutils.Convert.FromBase64("MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDAFOXcWxQ+YqPdUqc9Iar3ZDf012ZtQAFajBMApKpd2WPQccBmyPzvDZJSWKe3d5jShZANiAAQ4Z43bP7d5fUFIBorLA1pBFTwDLb6XA7J871VUwyu64q8L5qidV7iBZK3P+9m7eMMQWm0drWPvrEszE+4jEsS4HIbBeuduBU+6R46Orv+V6VXU1hAXKSdMFZOCzdbDFlE="),
algorithm: {
name: "ECDH",
namedCurve: "P-384",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["deriveBits"],
},
publicKey: {
format: "spki",
data: pvtsutils.Convert.FromBase64("MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEOGeN2z+3eX1BSAaKywNaQRU8Ay2+lwOyfO9VVMMruuKvC+aonVe4gWStz/vZu3jDEFptHa1j76xLMxPuIxLEuByGwXrnbgVPukeOjq7/lelV1NYQFyknTBWTgs3WwxZR"),
algorithm: {
name: "ECDH",
namedCurve: "P-384",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: [],
},
},
data: pvtsutils.Convert.FromBase64("2EKT/nmV68wIXFMZiCv4CyOEhWzpwdQ5"),
algorithm: {
name: "ECDH",
},
length: 192,
},
{
name: "P-521 256",
key: {
privateKey: {
format: "pkcs8",
data: pvtsutils.Convert.FromBase64("MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIB6PyCXpJ4TWPpwlGAmayLz5ecYHT+1ilxD64HytpTaViUS72sEzG1JMApD31+STX0zeVcARfG+yh71dXLCTlqqHGhgYkDgYYABADgIblBbth8vnOZt/HLU9VdUJHmenwRRADVZWL+P5IeCDQs6B87API41R3+91xFDHnjst9VKksYl/NJIIfl6b9cmABO6z80mTz3+0klquIpSQLidK2aFaFbqiGnMdCO+AZfwxu2qBx+1f5MwbHXUW5HXsfmEvzBUC9xCQKLpQ8oZYBrSg=="),
algorithm: {
name: "ECDH",
namedCurve: "P-521",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["deriveBits"],
},
publicKey: {
format: "spki",
data: pvtsutils.Convert.FromBase64("MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQA4CG5QW7YfL5zmbfxy1PVXVCR5np8EUQA1WVi/j+SHgg0LOgfOwDyONUd/vdcRQx547LfVSpLGJfzSSCH5em/XJgATus/NJk89/tJJariKUkC4nStmhWhW6ohpzHQjvgGX8MbtqgcftX+TMGx11FuR17H5hL8wVAvcQkCi6UPKGWAa0o="),
algorithm: {
name: "ECDH",
namedCurve: "P-521",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: [],
},
},
data: pvtsutils.Convert.FromBase64("AS2ene28pmWYdJwW6dyTXUe1eq1p2i8QEIo/rXSiJRo="),
algorithm: {
name: "ECDH",
},
length: 256,
},
{
name: "K-256 128",
key: {
privateKey: {
format: "pkcs8",
data: pvtsutils.Convert.FromBase64("MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQghgLhhrz/EYuB0G08/UoM5nV9jS7Pl/rtIcXeJkc2b3uhRANCAARgMfEiAPcF7pmEuLRGRRFXEKSwcJwqURKK/Pqo8MaqU0cl7eNQmLJ7mFpBtTDY8hr9xxJeIP9sI/u83A1F5ag7"),
algorithm: {
name: "ECDH",
namedCurve: "K-256",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["deriveBits"],
},
publicKey: {
format: "spki",
data: pvtsutils.Convert.FromBase64("MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEYDHxIgD3Be6ZhLi0RkURVxCksHCcKlESivz6qPDGqlNHJe3jUJiye5haQbUw2PIa/ccSXiD/bCP7vNwNReWoOw=="),
algorithm: {
name: "ECDH",
namedCurve: "K-256",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: [],
},
},
data: pvtsutils.Convert.FromBase64("3+2JX3D4/veBGJXnvU+aTg=="),
algorithm: {
name: "ECDH",
},
length: 128,
},
],
deriveKey: [
{
name: "P-256 128",
key: {
privateKey: {
format: "pkcs8",
data: pvtsutils.Convert.FromBase64("MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgQA7bkTNYlIYVb9+DavBlJ3b08f0892or3XwfscA3tLGhRANCAARzsy+ZcbrNchF7SrpL0hYnGp6ICX77jXUrpMYkq0BuzfaPFWcu9YZH5ASUzQJGz9eCK3mDXEbLCuiHRw3dwkFs"),
algorithm: {
name: "ECDH",
namedCurve: "P-256",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["deriveKey"],
},
publicKey: {
format: "spki",
data: pvtsutils.Convert.FromBase64("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7MvmXG6zXIRe0q6S9IWJxqeiAl++411K6TGJKtAbs32jxVnLvWGR+QElM0CRs/Xgit5g1xGywroh0cN3cJBbA=="),
algorithm: {
name: "ECDH",
namedCurve: "P-256",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: [],
},
},
algorithm: {
name: "ECDH",
},
derivedKeyType: {
name: "AES-CBC",
length: 128,
} as types.AesKeyAlgorithm,
keyUsages: ["encrypt", "decrypt"],
format: "raw",
keyData: pvtsutils.Convert.FromBase64("Jlc1/Zqi/8mH1oQT8+YfCA=="),
},
{
name: "P-384 192",
key: {
privateKey: {
format: "pkcs8",
data: pvtsutils.Convert.FromBase64("MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDAFOXcWxQ+YqPdUqc9Iar3ZDf012ZtQAFajBMApKpd2WPQccBmyPzvDZJSWKe3d5jShZANiAAQ4Z43bP7d5fUFIBorLA1pBFTwDLb6XA7J871VUwyu64q8L5qidV7iBZK3P+9m7eMMQWm0drWPvrEszE+4jEsS4HIbBeuduBU+6R46Orv+V6VXU1hAXKSdMFZOCzdbDFlE="),
algorithm: {
name: "ECDH",
namedCurve: "P-384",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["deriveKey"],
},
publicKey: {
format: "spki",
data: pvtsutils.Convert.FromBase64("MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEOGeN2z+3eX1BSAaKywNaQRU8Ay2+lwOyfO9VVMMruuKvC+aonVe4gWStz/vZu3jDEFptHa1j76xLMxPuIxLEuByGwXrnbgVPukeOjq7/lelV1NYQFyknTBWTgs3WwxZR"),
algorithm: {
name: "ECDH",
namedCurve: "P-384",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: [],
},
},
algorithm: {
name: "ECDH",
},
derivedKeyType: {
name: "AES-GCM",
length: 192,
} as types.AesKeyAlgorithm,
keyUsages: ["encrypt", "decrypt"],
format: "raw",
keyData: pvtsutils.Convert.FromBase64("2EKT/nmV68wIXFMZiCv4CyOEhWzpwdQ5"),
},
{
name: "P-521 256",
key: {
privateKey: {
format: "pkcs8",
data: pvtsutils.Convert.FromBase64("MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIB6PyCXpJ4TWPpwlGAmayLz5ecYHT+1ilxD64HytpTaViUS72sEzG1JMApD31+STX0zeVcARfG+yh71dXLCTlqqHGhgYkDgYYABADgIblBbth8vnOZt/HLU9VdUJHmenwRRADVZWL+P5IeCDQs6B87API41R3+91xFDHnjst9VKksYl/NJIIfl6b9cmABO6z80mTz3+0klquIpSQLidK2aFaFbqiGnMdCO+AZfwxu2qBx+1f5MwbHXUW5HXsfmEvzBUC9xCQKLpQ8oZYBrSg=="),
algorithm: {
name: "ECDH",
namedCurve: "P-521",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: ["deriveKey"],
},
publicKey: {
format: "spki",
data: pvtsutils.Convert.FromBase64("MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQA4CG5QW7YfL5zmbfxy1PVXVCR5np8EUQA1WVi/j+SHgg0LOgfOwDyONUd/vdcRQx547LfVSpLGJfzSSCH5em/XJgATus/NJk89/tJJariKUkC4nStmhWhW6ohpzHQjvgGX8MbtqgcftX+TMGx11FuR17H5hL8wVAvcQkCi6UPKGWAa0o="),
algorithm: {
name: "ECDH",
namedCurve: "P-521",
} as types.EcKeyImportParams,
extractable: true,
keyUsages: [],
},
},
algorithm: {
name: "ECDH",
},
derivedKeyType: {
name: "AES-CBC",
length: 256,
} as types.AesKeyAlgorithm,
keyUsages: ["encrypt", "decrypt"],
format: "raw",
keyData: pvtsutils.Convert.FromBase64("AS2ene28pmWYdJwW6dyTXUe1eq1p2i8QEIo/rXSiJRo="),
},
],
},
},
]);
it("sig", async () => {
const data = new Uint8Array(10);
const keys = await crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "brainpoolP512r1" }, false, ["sign", "verify"]);
const spki = await crypto.subtle.exportKey("spki", keys.publicKey);
const signature = await crypto.subtle.sign({ ...keys.privateKey.algorithm, hash: "SHA-256" }, keys.privateKey, data);
const ok = await crypto.subtle.verify({ name: "ECDSA", hash: "SHA-256" }, keys.publicKey, signature, data);
assert.strictEqual(ok, true);
});
});

View File

@ -0,0 +1,79 @@
import { Crypto as NodeCrypto } from "@peculiar/webcrypto";
import { Crypto as WebCrypto } from "@peculiar/webcrypto-web";
import * as assert from "assert";
const nodeCrypto = new NodeCrypto();
const webCrypto = new WebCrypto();
context("ED", () => {
context("generate/export/import/sign/verify", () => {
const alg = { name: "EdDSA", namedCurve: "Ed25519" };
const data = Buffer.from("Some message to sign");
it("pkcs8/spki", async () => {
const linerKeys = await webCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const pkcs8 = await webCrypto.subtle.exportKey("pkcs8", linerKeys.privateKey);
const spki = await webCrypto.subtle.exportKey("spki", linerKeys.publicKey);
const nodePrivateKey = await nodeCrypto.subtle.importKey("pkcs8", pkcs8, alg, false, ["sign"]);
const nodePublicKey = await nodeCrypto.subtle.importKey("spki", spki, alg, false, ["verify"]);
const linerPrivateKey = await webCrypto.subtle.importKey("pkcs8", pkcs8, alg, false, ["sign"]);
const linerPublicKey = await webCrypto.subtle.importKey("spki", spki, alg, false, ["verify"]);
const nodeSignature = await nodeCrypto.subtle.sign(alg, nodePrivateKey, data);
const linerSignature = await webCrypto.subtle.sign(alg, linerPrivateKey, data);
assert.strictEqual(Buffer.from(linerSignature).toString("hex"), Buffer.from(nodeSignature).toString("hex"));
const nodeOk = await nodeCrypto.subtle.verify(alg, nodePublicKey, nodeSignature, data);
const linerOk = await webCrypto.subtle.verify(alg, linerPublicKey, nodeSignature, data);
assert.strictEqual(linerOk, nodeOk);
});
it("jwk", async () => {
const linerKeys = await webCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const privateJwk = await webCrypto.subtle.exportKey("jwk", linerKeys.privateKey);
const publicJwk = await webCrypto.subtle.exportKey("jwk", linerKeys.publicKey);
const nodePrivateKey = await nodeCrypto.subtle.importKey("jwk", privateJwk, alg, false, ["sign"]);
const nodePublicKey = await nodeCrypto.subtle.importKey("jwk", publicJwk, alg, false, ["verify"]);
const linerPrivateKey = await webCrypto.subtle.importKey("jwk", privateJwk, alg, false, ["sign"]);
const linerPublicKey = await webCrypto.subtle.importKey("jwk", publicJwk, alg, false, ["verify"]);
const nodeSignature = await nodeCrypto.subtle.sign(alg, nodePrivateKey, data);
const linerSignature = await webCrypto.subtle.sign(alg, linerPrivateKey, data);
assert.strictEqual(Buffer.from(linerSignature).toString("hex"), Buffer.from(nodeSignature).toString("hex"));
const nodeOk = await nodeCrypto.subtle.verify(alg, nodePublicKey, nodeSignature, data);
const linerOk = await webCrypto.subtle.verify(alg, linerPublicKey, nodeSignature, data);
assert.strictEqual(linerOk, nodeOk);
});
it("pkcs8/raw", async () => {
const linerKeys = await webCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const pkcs8 = await webCrypto.subtle.exportKey("pkcs8", linerKeys.privateKey);
const raw = await webCrypto.subtle.exportKey("raw", linerKeys.publicKey);
const nodePrivateKey = await nodeCrypto.subtle.importKey("pkcs8", pkcs8, alg, false, ["sign"]);
const nodePublicKey = await nodeCrypto.subtle.importKey("raw", raw, alg, false, ["verify"]);
const linerPrivateKey = await webCrypto.subtle.importKey("pkcs8", pkcs8, alg, false, ["sign"]);
const linerPublicKey = await webCrypto.subtle.importKey("raw", raw, alg, false, ["verify"]);
const nodeSignature = await nodeCrypto.subtle.sign(alg, nodePrivateKey, data);
const linerSignature = await webCrypto.subtle.sign(alg, linerPrivateKey, data);
assert.strictEqual(Buffer.from(linerSignature).toString("hex"), Buffer.from(nodeSignature).toString("hex"));
const nodeOk = await nodeCrypto.subtle.verify(alg, nodePublicKey, nodeSignature, data);
const linerOk = await webCrypto.subtle.verify(alg, linerPublicKey, nodeSignature, data);
assert.strictEqual(linerOk, nodeOk);
});
});
});

View File

@ -0,0 +1,175 @@
import * as types from "@peculiar/webcrypto-types";
import { Convert } from "pvtsutils";
import { ITestGenerateKeyAction, testCrypto, webCrypto } from "./utils";
context("HMAC", () => {
testCrypto(webCrypto, [
{
name: "HMAC",
actions: {
generateKey: [
{
name: "default length",
algorithm: {
name: "HMAC",
hash: "SHA-256",
} as types.HmacKeyGenParams,
extractable: true,
keyUsages: ["sign", "verify"],
},
...["SHA-1", "SHA-256", "SHA-384", "SHA-512"].map((hash) => {
return {
name: hash,
algorithm: {
name: "HMAC",
hash,
length: 128,
},
extractable: true,
keyUsages: ["sign", "verify"],
} as ITestGenerateKeyAction;
}),
{
name: "length:160",
algorithm: {
name: "HMAC",
hash: "SHA-256",
length: 160,
},
extractable: true,
keyUsages: ["sign", "verify"],
} as ITestGenerateKeyAction,
],
sign: [
{
name: "HMAC-SHA256 with length param which is less than hash size",
key: {
format: "raw",
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6]),
algorithm: {
name: "HMAC",
hash: "SHA-256",
length: 128,
} as types.HmacImportParams,
extractable: false,
keyUsages: ["sign", "verify"],
},
algorithm: { name: "HMAC" },
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]),
signature: Convert.FromBase64("9yMF9ReX1EhdBWTRjSR+AC21NA05H9W8vx0HZGVmgNc="),
},
{
name: "HMAC-SHA256 without length param",
key: {
format: "raw",
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6]),
algorithm: {
name: "HMAC",
hash: "SHA-256",
} as types.HmacImportParams,
extractable: false,
keyUsages: ["sign", "verify"],
},
algorithm: { name: "HMAC" },
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]),
signature: Convert.FromHex("ad05febab44cd369e27433bbf00e63e6271f6a350614bec453f5d0efd6503a31"),
},
],
import: [
{ // JWK SHA-1
name: "JWK SHA-1",
format: "jwk",
data: {
alg: "HS1",
ext: true,
k: "AQIDBAUGBwgJAAECAwQFBg",
key_ops: ["sign", "verify"],
kty: "oct",
},
algorithm: {
name: "HMAC",
hash: "SHA-1",
length: 128,
} as types.HmacImportParams,
extractable: true,
keyUsages: ["sign", "verify"],
},
{ // JWK SHA-256
name: "JWK SHA-256",
format: "jwk",
data: {
alg: "HS256",
ext: true,
k: "AQIDBAUGBwgJAAECAwQFBg",
key_ops: ["sign", "verify"],
kty: "oct",
},
algorithm: {
name: "HMAC",
hash: "SHA-256",
} as types.HmacImportParams,
extractable: true,
keyUsages: ["sign", "verify"],
},
{ // JWK SHA-384
name: "JWK SHA-384",
format: "jwk",
data: {
alg: "HS384",
ext: true,
k: "AQIDBAUGBwgJAAECAwQFBg",
key_ops: ["sign", "verify"],
kty: "oct",
},
algorithm: {
name: "HMAC",
hash: "SHA-384",
} as types.HmacImportParams,
extractable: true,
keyUsages: ["sign", "verify"],
},
{ // JWK SHA-512
name: "JWK SHA-512",
format: "jwk",
data: {
alg: "HS512",
ext: true,
k: "AQIDBAUGBwgJAAECAwQFBg",
key_ops: ["sign", "verify"],
kty: "oct",
},
algorithm: {
name: "HMAC",
hash: "SHA-512",
} as types.HmacImportParams,
extractable: true,
keyUsages: ["sign", "verify"],
},
{ // raw 128
name: "raw 128",
format: "raw",
data: Convert.FromBase64Url("AQIDBAUGBwgJAAECAwQFBg"),
algorithm: {
name: "HMAC",
hash: "SHA-512",
} as types.HmacImportParams,
extractable: true,
keyUsages: ["sign", "verify"],
},
{ // raw 160
name: "raw 160",
format: "raw",
data: new Uint8Array(20),
algorithm: {
name: "HMAC",
hash: "SHA-512",
} as types.HmacImportParams,
extractable: true,
keyUsages: ["sign", "verify"],
},
],
},
},
]);
});

View File

@ -0,0 +1,61 @@
import * as types from "@peculiar/webcrypto-types";
import * as pvtsutils from "pvtsutils";
import { testCrypto, webCrypto } from "./utils";
context("PBKDF", () => {
testCrypto(webCrypto, [
{
name: "PBKDF2",
actions: {
deriveBits: [
{
key: {
format: "raw",
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]),
algorithm: {
name: "PBKDF2",
},
extractable: false,
keyUsages: ["deriveBits"],
},
algorithm: {
name: "PBKDF2",
salt: new Uint8Array([1, 2, 3, 4]),
hash: "SHA-256",
iterations: 1000,
} as types.Pbkdf2Params,
data: pvtsutils.Convert.FromBase64("3GK58/4RT+UPLooz5HT1MQ=="),
length: 128,
},
],
deriveKey: [
{
key: {
format: "raw",
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]),
algorithm: {
name: "PBKDF2",
},
extractable: false,
keyUsages: ["deriveKey"],
},
algorithm: {
name: "PBKDF2",
salt: new Uint8Array([1, 2, 3, 4]),
hash: "SHA-256",
iterations: 1000,
} as types.Pbkdf2Params,
derivedKeyType: {
name: "AES-CBC",
length: 128,
} as types.AesDerivedKeyParams,
keyUsages: ["encrypt"],
format: "raw",
keyData: pvtsutils.Convert.FromBase64("3GK58/4RT+UPLooz5HT1MQ=="),
},
],
},
},
]);
});

View File

@ -0,0 +1,420 @@
import * as types from "@peculiar/webcrypto-types";
import * as pvtsutils from "pvtsutils";
import { Browser } from "../src/helper";
import { browser, ITestGenerateKeyAction, testCrypto, webCrypto } from "./utils";
context("RSA", () => {
testCrypto(webCrypto, [
// RSASSA-PKCS1-v1_5
{
name: "RSASSA-PKCS1-v1_5",
actions: {
generateKey: (() => {
const res: ITestGenerateKeyAction[] = [];
["SHA-1", "SHA-256"].forEach((hash) =>
["SHA-1", "SHA-256", "SHA-512"].forEach((hash) =>
[new Uint8Array([3]), new Uint8Array([1, 0, 1])].forEach((publicExponent) =>
[1024, 2048].forEach((modulusLength) => {
res.push({
name: `h:${hash} e:${pvtsutils.Convert.ToHex(publicExponent)} n:${modulusLength}`,
skip: false,
algorithm: {
name: "RSASSA-PKCS1-v1_5",
hash,
publicExponent,
modulusLength,
} as types.RsaHashedKeyGenParams,
extractable: false,
keyUsages: ["sign", "verify"],
} as ITestGenerateKeyAction);
}),
),
),
);
return res;
})(),
sign: [
{
name: "SHA-256, e:010001, n:2048",
algorithm: {
name: "RSASSA-PKCS1-v1_5",
},
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]),
signature: pvtsutils.Convert.FromBase64("f8OvbYnwX5YPVPjWkOTalYTFJjS1Ks7iNmPdLEby/kK6BEGk5uPvY/ebcok6sTQpQXJXJFJbOcMrZftmJXpm1szcgOdNgVW6FDc3722a9Mzvk/YfvNUCQRNEMON9lYKdpOLSXAFpXR5ovZytbFQ2w2ztpKkJvNY2QZQlizcZKSg="),
key: {
publicKey: {
format: "jwk",
algorithm: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" } as types.RsaHashedImportParams,
data: {
alg: "RS256",
e: "AQAB",
ext: true,
key_ops: ["verify"],
kty: "RSA",
n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0",
},
extractable: true,
keyUsages: ["verify"],
},
privateKey: {
format: "jwk",
algorithm: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" } as types.RsaHashedImportParams,
data: {
alg: "RS256",
d: "YZzAFCqJ26kElAO92CZEIBmBhw6MN7cjJy8nMgoHzNx9TH4rI_M71Zf6_DqRYIwWPNd7N-X1DSErNB0A6jUNXr42l3ChBsBB31vjHqQKx95-M6iXVgjJFTzxirNjUuCm_skFYIcXS5oEaXjy5XI3dT8KAEf1M2UA6__LwGrAD8E",
dp: "pOolqL7HwnmWLn7GDX8zGkm0Q1IAj-ouBL7ZZbaTm3wETLtwu-dGsQheEdzP_mfL_CTiCAwGuQBcSItimD0DdQ",
dq: "FTSY59AnkgmB7TsErWNBE3xlVB_pMpE2xWyCBCz96gyDOUOFDz8vlSV-clhjawJeRd1n30nZOPSBtOHozhwZmQ",
e: "AQAB",
ext: true,
key_ops: ["sign"],
kty: "RSA",
n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0",
p: "6jFtmBJJQFIlQUXXZYIgvH70Y9a03oWKjNuF2veb5Zf09EtLNE86NpnIm463OnoHJPW0m8wHFXZZfcYVTIPR_w",
q: "0GttDMl1kIzSV2rNzGXpOS8tUqr5Lz0EtVZwIb9GJPMmJ0P3gZ801zEgZZ4-esU7cLUf-BSZEAmfnKA80G2jIw",
qi: "FByTxX4G2eXkk1xe0IuiEv7I5NS-CnFyp8iB4XLG0rabnfcIZFKpf__X0sNyVOAVo5-jJMuUYjCRTdaXNAWhkg",
},
extractable: true,
keyUsages: ["sign"],
},
},
},
{
name: "SHA-1 e:03 n:1024",
algorithm: {
name: "RSASSA-PKCS1-v1_5",
},
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]),
signature: pvtsutils.Convert.FromHex("2f4cab4f67ca544934e462fd324ea0b52f9040f1453c8c425e818411bf54c3c0cd1d7f2a1d04a820ce28fec996b94a0971d481ec8adee2ee0d8b003c2cb75862d7699a73b798d7fab788956ae17388fed764e7a1a944abf9799534b66e830a5c5f4ea7253b937af6b4fcbd11310da3daebf1f3181041bdd550cbe4ea8ff2e1ed"),
key: {
publicKey: {
format: "spki",
algorithm: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-1" } as types.Algorithm,
data: pvtsutils.Convert.FromBase64("MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDL51DUp2Jxqjr18k5mpAvFBzTLtzK4qL6Pq8H4nXU+8gheGYP2+Vi3J+PSLVTIKk7jPNJ2gQtgnA27TNZxYA0QplEyxq0WQwTMp8vz/PAJYjsLNx8O4g433Ve60dUzZWjjbawX8JeggET37m2EoCsgHXJPe3puloMfD0qRR3BoZwIBAw=="),
extractable: true,
keyUsages: ["verify"],
},
privateKey: {
format: "pkcs8",
algorithm: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-1" } as types.Algorithm,
data: pvtsutils.Convert.FromBase64("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMvnUNSnYnGqOvXyTmakC8UHNMu3Mriovo+rwfiddT7yCF4Zg/b5WLcn49ItVMgqTuM80naBC2CcDbtM1nFgDRCmUTLGrRZDBMyny/P88AliOws3Hw7iDjfdV7rR1TNlaONtrBfwl6CARPfubYSgKyAdck97em6Wgx8PSpFHcGhnAgEDAoGAIfvizhvlvZxfKP23u8YB9iveIfPdyXF1F/H1qW+Tin2sD67rU9Q5c9v7TbI4zAcNJd94aRWB5W9Xnzd5EuVXgnnU/wz54Bk6zXMLq/L6oouSLzcRVwz0riaXBa007OTejfS+jVhCAlMM4hqYnCxrRr4BBIEi+WidyHKSs8ynSE8CQQD9BRizPsw8eZXDcJz1TVrNYVk4ZGgWfmgGkdyeSh2A5Smdcmvzcm32dNVH9fqL9P33qoJUw+CoSRKuEB/szIjjAkEAzk4fxZMJbypmMhVPVcLfT2yWtFKcfdO67zu8JE2Ih0xmE8Jb65kkl4LWBuPhCbJ5scGyH+S1eodZsco6jrgtrQJBAKiuEHd/MtL7uSz1vfjePIjrkNBC8A7+8ARhPb7cE6tDcROhnUz28/mjONqj/F1N/qUcVuMtQHAwtx61ap3dsJcCQQCJiWqDt1ufcZl2uN+Ogeo08w8i4b2pN9H00n1tiQWviEQNLD1Hu226VzlZ7UCxIaZ2gSFqmHj8WjvL3CcJ0B5zAkEAlmRgnALghAcJ/WfTMphPKJXhY+H+CgkeE3si2ZgPW1YaDAyhp/xdQabkgbFy70Nq32fuJyxDDS4WhF0aOYz6pw=="),
extractable: true,
keyUsages: ["sign"],
},
},
},
],
import: [
{ // public key JWK
name: "public key JWK",
format: "jwk",
algorithm: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" } as types.Algorithm,
data: {
alg: "RS256",
e: "AQAB",
ext: true,
key_ops: ["verify"],
kty: "RSA",
n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0",
},
extractable: true,
keyUsages: ["verify"],
},
{ // public key SPKI
name: "public key SPKI",
format: "spki",
algorithm: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" } as types.Algorithm,
data: pvtsutils.Convert.FromBase64("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+qm93G7JnqspidZOP9nMMEVkAACWl7mGmiJgepraPmQru/xTkRo9jZsuJv2bgHjSP6fcVX3FQIaKmVZ2owkkpP7g+MY7kTdLg32SMWG7nuehhPvPvfTYnSwld6gVtfGWAT7gbnk7GWbnYgPb9El6w/mfNwZOuJDChFusk/k4S3QIDAQAB"),
extractable: true,
keyUsages: ["verify"],
},
{ // private key JWK
name: "private key JWK",
format: "jwk",
algorithm: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" } as types.Algorithm,
data: {
alg: "RS256",
d: "YZzAFCqJ26kElAO92CZEIBmBhw6MN7cjJy8nMgoHzNx9TH4rI_M71Zf6_DqRYIwWPNd7N-X1DSErNB0A6jUNXr42l3ChBsBB31vjHqQKx95-M6iXVgjJFTzxirNjUuCm_skFYIcXS5oEaXjy5XI3dT8KAEf1M2UA6__LwGrAD8E",
dp: "pOolqL7HwnmWLn7GDX8zGkm0Q1IAj-ouBL7ZZbaTm3wETLtwu-dGsQheEdzP_mfL_CTiCAwGuQBcSItimD0DdQ",
dq: "FTSY59AnkgmB7TsErWNBE3xlVB_pMpE2xWyCBCz96gyDOUOFDz8vlSV-clhjawJeRd1n30nZOPSBtOHozhwZmQ",
e: "AQAB",
ext: true,
key_ops: ["sign"],
kty: "RSA",
n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0",
p: "6jFtmBJJQFIlQUXXZYIgvH70Y9a03oWKjNuF2veb5Zf09EtLNE86NpnIm463OnoHJPW0m8wHFXZZfcYVTIPR_w",
q: "0GttDMl1kIzSV2rNzGXpOS8tUqr5Lz0EtVZwIb9GJPMmJ0P3gZ801zEgZZ4-esU7cLUf-BSZEAmfnKA80G2jIw",
qi: "FByTxX4G2eXkk1xe0IuiEv7I5NS-CnFyp8iB4XLG0rabnfcIZFKpf__X0sNyVOAVo5-jJMuUYjCRTdaXNAWhkg",
},
extractable: true,
keyUsages: ["sign"],
},
{
skip: browser.name === Browser.Edge, // Edge returns PKCS8 with KeyUsages extension
name: "private key pkcs8",
format: "pkcs8",
algorithm: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" } as types.Algorithm,
data: pvtsutils.Convert.FromBase64("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAL6qb3cbsmeqymJ1k4/2cwwRWQAAJaXuYaaImB6mto+ZCu7/FORGj2Nmy4m/ZuAeNI/p9xVfcVAhoqZVnajCSSk/uD4xjuRN0uDfZIxYbue56GE+8+99NidLCV3qBW18ZYBPuBueTsZZudiA9v0SXrD+Z83Bk64kMKEW6yT+ThLdAgMBAAECgYACR4hYnLCn059iyPQQKwqaENUHDnlkv/JT6tsitqyFD/fU/qCxz/Qj5JU3Wt3wfPv04n+tNjxlEFng8jIV0+jK+6jlqkd0AcfquIkrEMdY/GET5F41UQ9JOIXWvLwNJ7nMLvD0Eucf9AzxuQ3hw6e+CquDsRusZaiYAYlW+hHA4wJBAOoxbZgSSUBSJUFF12WCILx+9GPWtN6Fiozbhdr3m+WX9PRLSzRPOjaZyJuOtzp6ByT1tJvMBxV2WX3GFUyD0f8CQQDQa20MyXWQjNJXas3MZek5Ly1SqvkvPQS1VnAhv0Yk8yYnQ/eBnzTXMSBlnj56xTtwtR/4FJkQCZ+coDzQbaMjAkEApOolqL7HwnmWLn7GDX8zGkm0Q1IAj+ouBL7ZZbaTm3wETLtwu+dGsQheEdzP/mfL/CTiCAwGuQBcSItimD0DdQJAFTSY59AnkgmB7TsErWNBE3xlVB/pMpE2xWyCBCz96gyDOUOFDz8vlSV+clhjawJeRd1n30nZOPSBtOHozhwZmQJAFByTxX4G2eXkk1xe0IuiEv7I5NS+CnFyp8iB4XLG0rabnfcIZFKpf//X0sNyVOAVo5+jJMuUYjCRTdaXNAWhkg=="),
extractable: true,
keyUsages: ["sign"],
},
{
name: "pkcs8 e:03 n:1024",
skip: browser.name === Browser.Edge,
format: "pkcs8",
algorithm: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-1" } as types.Algorithm,
data: pvtsutils.Convert.FromBase64("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMvnUNSnYnGqOvXyTmakC8UHNMu3Mriovo+rwfiddT7yCF4Zg/b5WLcn49ItVMgqTuM80naBC2CcDbtM1nFgDRCmUTLGrRZDBMyny/P88AliOws3Hw7iDjfdV7rR1TNlaONtrBfwl6CARPfubYSgKyAdck97em6Wgx8PSpFHcGhnAgEDAoGAIfvizhvlvZxfKP23u8YB9iveIfPdyXF1F/H1qW+Tin2sD67rU9Q5c9v7TbI4zAcNJd94aRWB5W9Xnzd5EuVXgnnU/wz54Bk6zXMLq/L6oouSLzcRVwz0riaXBa007OTejfS+jVhCAlMM4hqYnCxrRr4BBIEi+WidyHKSs8ynSE8CQQD9BRizPsw8eZXDcJz1TVrNYVk4ZGgWfmgGkdyeSh2A5Smdcmvzcm32dNVH9fqL9P33qoJUw+CoSRKuEB/szIjjAkEAzk4fxZMJbypmMhVPVcLfT2yWtFKcfdO67zu8JE2Ih0xmE8Jb65kkl4LWBuPhCbJ5scGyH+S1eodZsco6jrgtrQJBAKiuEHd/MtL7uSz1vfjePIjrkNBC8A7+8ARhPb7cE6tDcROhnUz28/mjONqj/F1N/qUcVuMtQHAwtx61ap3dsJcCQQCJiWqDt1ufcZl2uN+Ogeo08w8i4b2pN9H00n1tiQWviEQNLD1Hu226VzlZ7UCxIaZ2gSFqmHj8WjvL3CcJ0B5zAkEAlmRgnALghAcJ/WfTMphPKJXhY+H+CgkeE3si2ZgPW1YaDAyhp/xdQabkgbFy70Nq32fuJyxDDS4WhF0aOYz6pw=="),
extractable: true,
keyUsages: ["sign"],
},
{
name: "spki e:03 n:1024",
format: "spki",
algorithm: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-1" } as types.Algorithm,
data: pvtsutils.Convert.FromBase64("MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDL51DUp2Jxqjr18k5mpAvFBzTLtzK4qL6Pq8H4nXU+8gheGYP2+Vi3J+PSLVTIKk7jPNJ2gQtgnA27TNZxYA0QplEyxq0WQwTMp8vz/PAJYjsLNx8O4g433Ve60dUzZWjjbawX8JeggET37m2EoCsgHXJPe3puloMfD0qRR3BoZwIBAw=="),
extractable: true,
keyUsages: ["verify"],
},
],
},
},
// RSA-PSS
{
name: "RSA-PSS",
actions: {
generateKey: ["SHA-1", "SHA-256", "SHA-384", "SHA-512"].map((hash) => {
return {
name: hash,
algorithm: {
name: "RSA-PSS",
hash,
publicExponent: new Uint8Array([1, 0, 1]),
modulusLength: 1024,
} as types.RsaHashedKeyGenParams,
extractable: false,
keyUsages: ["sign", "verify"],
} as ITestGenerateKeyAction;
}),
sign: [
{
algorithm: {
name: "RSA-PSS",
saltLength: 64,
} as types.RsaPssParams,
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]),
signature: pvtsutils.Convert.FromBase64("OYz/7fv71ELOs5kuz5IiYq1NsXuOazl22xqIFjiY++hYFzJMWaR+ZI0WPoMOifvb1PNKmdQ4dY+QbpYC1vdzlAKfkLe22l5htLyQaXzjD/yeMZYrL0KmrabC9ayL6bxrMW+ccePStkbrF1Jn0LT09l22aX/r1y3SPrl0b+zwo/Q="),
key: {
publicKey: {
format: "jwk",
algorithm: { name: "RSA-PSS", hash: "SHA-256" } as types.Algorithm,
data: {
alg: "PS256",
e: "AQAB",
ext: true,
key_ops: ["verify"],
kty: "RSA",
n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0",
},
extractable: true,
keyUsages: ["verify"],
},
privateKey: {
format: "jwk",
algorithm: { name: "RSA-PSS", hash: "SHA-256" } as types.Algorithm,
data: {
alg: "PS256",
d: "YZzAFCqJ26kElAO92CZEIBmBhw6MN7cjJy8nMgoHzNx9TH4rI_M71Zf6_DqRYIwWPNd7N-X1DSErNB0A6jUNXr42l3ChBsBB31vjHqQKx95-M6iXVgjJFTzxirNjUuCm_skFYIcXS5oEaXjy5XI3dT8KAEf1M2UA6__LwGrAD8E",
dp: "pOolqL7HwnmWLn7GDX8zGkm0Q1IAj-ouBL7ZZbaTm3wETLtwu-dGsQheEdzP_mfL_CTiCAwGuQBcSItimD0DdQ",
dq: "FTSY59AnkgmB7TsErWNBE3xlVB_pMpE2xWyCBCz96gyDOUOFDz8vlSV-clhjawJeRd1n30nZOPSBtOHozhwZmQ",
e: "AQAB",
ext: true,
key_ops: ["sign"],
kty: "RSA",
n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0",
p: "6jFtmBJJQFIlQUXXZYIgvH70Y9a03oWKjNuF2veb5Zf09EtLNE86NpnIm463OnoHJPW0m8wHFXZZfcYVTIPR_w",
q: "0GttDMl1kIzSV2rNzGXpOS8tUqr5Lz0EtVZwIb9GJPMmJ0P3gZ801zEgZZ4-esU7cLUf-BSZEAmfnKA80G2jIw",
qi: "FByTxX4G2eXkk1xe0IuiEv7I5NS-CnFyp8iB4XLG0rabnfcIZFKpf__X0sNyVOAVo5-jJMuUYjCRTdaXNAWhkg",
},
extractable: true,
keyUsages: ["sign"],
},
},
},
],
},
},
// RSA-OAEP
{
name: "RSA-OAEP",
actions: {
generateKey: ["SHA-1", "SHA-256", "SHA-384", "SHA-512"].map((hash) => {
return {
name: hash,
algorithm: {
name: "RSA-OAEP",
hash,
publicExponent: new Uint8Array([1, 0, 1]),
modulusLength: 1024,
} as types.RsaHashedKeyGenParams,
extractable: false,
keyUsages: ["encrypt", "decrypt"],
} as ITestGenerateKeyAction;
}),
encrypt: [
{
name: "with label",
algorithm: {
name: "RSA-OAEP",
label: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]),
} as types.RsaOaepParams,
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]),
encData: pvtsutils.Convert.FromBase64("aHu8PBZuctYecfINKgUdB8gBoLyUUFxTZDTzTHUk9KKxtYywYml48HoijBG5DyaIWUUbOIdPgap9C8pFG2iYShQnE9Aj3gzKLHacBbFw1P79+Ei/Tm0j/THiXqCplBZC4dIp4jhTDepmdrlXZcY0slmjG+h8h8TpSmWKP3pEGGk="),
key: {
publicKey: {
format: "jwk",
algorithm: { name: "RSA-OAEP", hash: "SHA-256" } as types.Algorithm,
data: {
alg: "RSA-OAEP-256",
e: "AQAB",
ext: true,
key_ops: ["encrypt"],
kty: "RSA",
n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0",
},
extractable: true,
keyUsages: ["encrypt"],
},
privateKey: {
format: "jwk",
algorithm: { name: "RSA-OAEP", hash: "SHA-256" } as types.Algorithm,
data: {
alg: "RSA-OAEP-256",
d: "YZzAFCqJ26kElAO92CZEIBmBhw6MN7cjJy8nMgoHzNx9TH4rI_M71Zf6_DqRYIwWPNd7N-X1DSErNB0A6jUNXr42l3ChBsBB31vjHqQKx95-M6iXVgjJFTzxirNjUuCm_skFYIcXS5oEaXjy5XI3dT8KAEf1M2UA6__LwGrAD8E",
dp: "pOolqL7HwnmWLn7GDX8zGkm0Q1IAj-ouBL7ZZbaTm3wETLtwu-dGsQheEdzP_mfL_CTiCAwGuQBcSItimD0DdQ",
dq: "FTSY59AnkgmB7TsErWNBE3xlVB_pMpE2xWyCBCz96gyDOUOFDz8vlSV-clhjawJeRd1n30nZOPSBtOHozhwZmQ",
e: "AQAB",
ext: true,
key_ops: ["decrypt"],
kty: "RSA",
n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0",
p: "6jFtmBJJQFIlQUXXZYIgvH70Y9a03oWKjNuF2veb5Zf09EtLNE86NpnIm463OnoHJPW0m8wHFXZZfcYVTIPR_w",
q: "0GttDMl1kIzSV2rNzGXpOS8tUqr5Lz0EtVZwIb9GJPMmJ0P3gZ801zEgZZ4-esU7cLUf-BSZEAmfnKA80G2jIw",
qi: "FByTxX4G2eXkk1xe0IuiEv7I5NS-CnFyp8iB4XLG0rabnfcIZFKpf__X0sNyVOAVo5-jJMuUYjCRTdaXNAWhkg",
},
extractable: true,
keyUsages: ["decrypt"],
},
},
},
{
name: "without label",
algorithm: {
name: "RSA-OAEP",
} as types.RsaOaepParams,
data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]),
encData: pvtsutils.Convert.FromBase64("d91eZMLqHTOIGC+GqfSj13x8aQHkTKqxImwmybFFpR/00n5y4e7tL7XX49izZO/wwgCYkDCentX7BGoPhOv/4RhW9vVlfrjFAFdwZFAOFlumt+9jp2QjBDnwxuoCO/IOhjFFf7rq5hTBUB9eoHsSMp42LA6F/Q3IuxFLaejOWGw="),
key: {
publicKey: {
format: "jwk",
algorithm: { name: "RSA-OAEP", hash: "SHA-256" } as types.Algorithm,
data: {
alg: "RSA-OAEP-256",
e: "AQAB",
ext: true,
key_ops: ["encrypt"],
kty: "RSA",
n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0",
},
extractable: true,
keyUsages: ["encrypt"],
},
privateKey: {
format: "jwk",
algorithm: { name: "RSA-OAEP", hash: "SHA-256" } as types.Algorithm,
data: {
alg: "RSA-OAEP-256",
d: "YZzAFCqJ26kElAO92CZEIBmBhw6MN7cjJy8nMgoHzNx9TH4rI_M71Zf6_DqRYIwWPNd7N-X1DSErNB0A6jUNXr42l3ChBsBB31vjHqQKx95-M6iXVgjJFTzxirNjUuCm_skFYIcXS5oEaXjy5XI3dT8KAEf1M2UA6__LwGrAD8E",
dp: "pOolqL7HwnmWLn7GDX8zGkm0Q1IAj-ouBL7ZZbaTm3wETLtwu-dGsQheEdzP_mfL_CTiCAwGuQBcSItimD0DdQ",
dq: "FTSY59AnkgmB7TsErWNBE3xlVB_pMpE2xWyCBCz96gyDOUOFDz8vlSV-clhjawJeRd1n30nZOPSBtOHozhwZmQ",
e: "AQAB",
ext: true,
key_ops: ["decrypt"],
kty: "RSA",
n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0",
p: "6jFtmBJJQFIlQUXXZYIgvH70Y9a03oWKjNuF2veb5Zf09EtLNE86NpnIm463OnoHJPW0m8wHFXZZfcYVTIPR_w",
q: "0GttDMl1kIzSV2rNzGXpOS8tUqr5Lz0EtVZwIb9GJPMmJ0P3gZ801zEgZZ4-esU7cLUf-BSZEAmfnKA80G2jIw",
qi: "FByTxX4G2eXkk1xe0IuiEv7I5NS-CnFyp8iB4XLG0rabnfcIZFKpf__X0sNyVOAVo5-jJMuUYjCRTdaXNAWhkg",
},
extractable: true,
keyUsages: ["decrypt"],
},
},
},
],
},
},
// RSAES-PKCS1-v1_5
{
name: "RSAES-PKCS1-v1_5",
actions: {
generateKey: [
{
algorithm: {
name: "RSAES-PKCS1-v1_5",
publicExponent: new Uint8Array([1, 0, 1]),
modulusLength: 1024,
} as types.RsaKeyGenParams,
extractable: false,
keyUsages: ["encrypt", "decrypt"],
} as ITestGenerateKeyAction,
],
encrypt: [
{
algorithm: {
name: "RSAES-PKCS1-v1_5",
} as types.Algorithm,
data: pvtsutils.Convert.FromHex("01435e62ad3ec4850720e34f8cab620e203749f2315b203d"),
encData: pvtsutils.Convert.FromHex("76e5ea6e1df52471454f790923f60e2baa7adf5017fe0a36c0af3e32f6390d570e1d592375ba6035fdf4ffa70764b797ab54d0ab1efe89cf31d7fc98240a4d08c2476b7eb4c2d92355b8bf60e3897c3fcbfe09f20c7b159d9a9c4a6b2ce5021dd313e492afa762c24930f97f03a429f7b2b1e1d6088651d60e323835807c6fefe7952f74e5da29e8e327ea46e69a0a6684272f022bf18ec602ffcd10a62666b35a51ec7c7d101096f663ddfa0924a86bdbcde0433b4f71dc42bfd9facf329558026f8667f1a71c3365e09843a12339d8aaf31987b0d800e53fd0835e990096cb145e278153faf1188cd5713c6fcd289cb77d80515e1d200139b8ccac4d3bcebc"),
key: {
publicKey: {
format: "jwk",
algorithm: { name: "RSAES-PKCS1-v1_5" } as types.Algorithm,
data: {
alg: "RS1",
e: "AQAB",
ext: true,
key_ops: ["encrypt"],
kty: "RSA",
n: "xr8ELXq5dGFycys8jrc8vVPkWl2GzuRgyOxATtjcNIy5MD7j1XVsUH62VVdIVUUGt0IQ7K288ij3gkIPcIkRO6GmV0vbQAqHrjSHYUAtKQXbIgNRIuJGZvO5AXsxSo1X-tfhOxe140pseOkaehz1bGduhdcYWNR3xLmp7i-GQTRDo-v6CQXtFvSUwG_EIOXnl1trN2Q1Yw4wA1dbtY9FDz69uH-dEWTx7BFCAXVTQMjNe7BTvgGeQcX7XZIw5e2pd0pXjdIgb0xMgziwmc5bbABrGlhK7TmKqA47RlWzY_Lcj7VcTUfMfh7YKKichGTUbqxlgsRTma_e-0-vgDEz6w",
},
extractable: true,
keyUsages: ["encrypt"],
},
privateKey: {
format: "jwk",
algorithm: { name: "RSAES-PKCS1-v1_5" } as types.Algorithm,
data: {
kty: "RSA",
alg: "RS1",
key_ops: ["decrypt"],
ext: true,
n: "xr8ELXq5dGFycys8jrc8vVPkWl2GzuRgyOxATtjcNIy5MD7j1XVsUH62VVdIVUUGt0IQ7K288ij3gkIPcIkRO6GmV0vbQAqHrjSHYUAtKQXbIgNRIuJGZvO5AXsxSo1X-tfhOxe140pseOkaehz1bGduhdcYWNR3xLmp7i-GQTRDo-v6CQXtFvSUwG_EIOXnl1trN2Q1Yw4wA1dbtY9FDz69uH-dEWTx7BFCAXVTQMjNe7BTvgGeQcX7XZIw5e2pd0pXjdIgb0xMgziwmc5bbABrGlhK7TmKqA47RlWzY_Lcj7VcTUfMfh7YKKichGTUbqxlgsRTma_e-0-vgDEz6w",
e: "AQAB",
d: "kZ2IoQ3G7UcshMdL8kC85vadW7wktldLtkqqf1qSVIo6cOfTJCWJe5yrWPG_VIJjfkeQgOh2hHKRjcV67HfwwWEZr-IrPMu6R1_DRPSxYdohiNUnUEi7TlkJ1tT882OF74rWQeaIZIS13wzjUk7_XjKWHsfO1d6t9dwWbiYx1nj4syQCcUrvHIgVXCfL85Tyu3NHqpxOdbzRb2OLmkv5ciHFExm4ai98xAgsEXbNvZQeSOOfKNsiCb-NjBXLYrbaDIsakAEV75893JubfeD51UHn7dPT8M8MmKEvrTOKCscShf01scTDHfx_hiOXK3XG4tVx9l2YGEkt3xCedljocQ",
p: "_dWMJ57SECcBbOjPRCvT97ypDyw9ydvnSZXTsn9c7ScxvUxBk6-wuMtgsLI8OWkhZGDBLyVrn-I3RMAN-A5QI_adoGdK7fq5lFWmQYvb1u1xUaGEInVFsM3BW7RBBF8N7OzHwULEQLTXb4jkpgwyCynsX0OEbVVvVerqrcr7osM",
q: "yHEjuQe9TNo-leMrL6cu-yDPfA85M8xQuBM59Cwz06-ggBRi9EOpbV-CrejGUbVlE9QmKGqIBT8C3NVBQwybzlgUihgIpnVgkb01lLEf13ohQ_GWV1mS8ybznjMgaVtVF5Lva4WixIDlXbOu4svVQpkr-KRpKvEMUCTsX-Sxx7k",
dp: "jMP4TaCN7dczuyoAh1Wm3yQIvRlTyrXgtbYZCEwJRJsPwmKfmz87Sb-_hz3QmCXtFrVxbKvb23agH8hB9uY5GziQgXvG2eLJN7Gn2YGuEKrsxNBFbraKR1pTeH-l7r6oAlPtEwfrvdaMApZv9oWc2wQMyWev8NIIRCVar7Z5hfE",
dq: "wi2g3sJZp9cRpGEDWFHM2KnrdxLEZqK7W-f8T8h2mM9eXFXjmyDlRLivP0zuuv9QoUn3gVXa2cI2QrsxUwQm-Fop47Hux1uUpvs2qgqBf1yoV0r2Sz7Sdk442fxLnOVG5OSKno5Cpbz89q54cOvoeHEswN59p4UHWai7eRZzB7k",
qi: "k9hlEyvZCWj8Fvxrknj5WHgaLrSqaVku3PVod2wUJox3aZ8vUsGmmD27lfiWwVKNRmgxLiazY40pLPu07SEmlJgF8QjzDb33k5Pcn9wRuezcCi-53LBRK6-EptZ-UjEINBlM_Cx_WOuxs7P77pwcCo2NV76ilxP5PP_34SUZ0ts",
},
extractable: true,
keyUsages: ["decrypt"],
},
},
},
],
},
},
]);
});

View File

@ -0,0 +1,82 @@
import * as types from "@peculiar/webcrypto-types";
import * as pvtsutils from "pvtsutils";
import { testCrypto } from "./utils";
import { webCrypto } from "./utils";
context("SHA", () => {
const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]);
testCrypto(webCrypto, [
{
name: "SHA",
actions: {
digest: [
{
name: "SHA-1",
algorithm: "SHA-1",
data,
hash: pvtsutils.Convert.FromBase64("6JrVqWMcPv3e1+Psznm00P7c4b8="),
},
{
name: "SHA-256",
algorithm: "SHA-256",
data,
hash: pvtsutils.Convert.FromBase64("monGjExeKLjEpVZ2c9Ri//UV20YRb5kAYk0JxHT1k/s="),
},
{
name: "SHA-384",
skip: typeof module !== "undefined", // skip for nodejs
algorithm: "SHA-384",
data,
hash: pvtsutils.Convert.FromBase64("E9WqubQC9JnxffIniWwf0soI91o5z0Kbvk+s/32Fi3z28kAh+Fcne7Hgy1nnW4rR"),
},
{
name: "SHA-512",
algorithm: "SHA-512",
data,
hash: pvtsutils.Convert.FromBase64("OtPzaXlFDU9TNmJE7PEBD0+RIdaIgoX/FBBP1a3thdSKoXG/HjOhEmAvkrenCIsph4kBL7h7kFYyEkGhn7dOCw=="),
},
],
},
},
{
name: "SHAKE",
actions: {
digest: [
{
name: "shake128 default",
algorithm: "shake128",
data,
hash: pvtsutils.Convert.FromHex("83eb77696796112190033833050fbd57"),
},
{
name: "shake128 128 byte length",
algorithm: {
name: "shake128",
length: 128,
} as types.ShakeParams,
data,
hash: pvtsutils.Convert.FromHex("83eb77696796112190033833050fbd57c6b678d762053e931c978d9c1586b5c4c09fb0cfa40f68094cd6520bec7c21ac47072053243ba42283322a4aeebe23f7675f96c7fa22a9f8b4d63b0b6634dca3b6a6138870c1afc3ada61a3bd816d576b4783101205a1ddf364210c05d6c72ef861936828c446e3c3584d0607d53e46e"),
},
{
name: "shake256 default",
algorithm: "shake256",
data,
hash: pvtsutils.Convert.FromHex("5719c4fb8351b11f091815582a33cb5f7caba174f2dd7429d3298383e67af205"),
},
{
name: "shake256 256 byte length",
algorithm: {
name: "shake256",
length: 256,
} as types.ShakeParams,
data,
hash: pvtsutils.Convert.FromHex("5719c4fb8351b11f091815582a33cb5f7caba174f2dd7429d3298383e67af20588ce4967a3867f6d7fde600336b14188dba8f14b999970223395e53de9d09285ee861c1817a1e2c66c894d230944ec16e0f65b605fb7ee707b114702905037df89dfa9910dd850e1b789eb6efbfc5002a335d9270a9bb66d409df65d8b0755e5081918f8d0d9e49f4aca83d5a097bde0ccd5cecbe2724f22e5aab61fb43cd22f108aa5db02cb122c84a860037a4bb292b3f2a6a1193c642c61ab83f9e6310c896fdf3487c23863c9c7b7cb806ffeff44bc21fbd2c4e65ee6c76bf4336e4a11008368ae9264eab5f728bb2924f3410dd1d821b0d8a18b30a7420ad469f1bfd04d"),
},
],
},
},
]);
});

View File

@ -0,0 +1,371 @@
import * as types from "@peculiar/webcrypto-types";
import * as assert from "assert";
import * as pvtsutils from "pvtsutils";
import { BrowserInfo } from "../../src/helper";
// fix type errors
type Crypto = any;
export const browser = BrowserInfo();
export interface ITestMochaFunction {
skip?: boolean;
only?: boolean;
}
export interface ITestAction extends ITestMochaFunction {
name?: string;
error?: any;
}
export interface ITestGenerateKeyAction extends ITestAction {
algorithm: types.Algorithm;
extractable: boolean;
keyUsages: types.KeyUsage[];
}
export interface IImportKeyParams {
format: types.KeyFormat;
data: types.JsonWebKey | types.BufferSource;
algorithm: types.AlgorithmIdentifier;
extractable: boolean;
keyUsages: types.KeyUsage[];
}
export interface IImportKeyPairParams {
privateKey: IImportKeyParams;
publicKey: IImportKeyParams;
}
export interface ITestEncryptAction extends ITestAction {
algorithm: types.Algorithm;
data: types.BufferSource;
encData: types.BufferSource;
key: IImportKeyParams | IImportKeyPairParams;
}
export interface ITestSignAction extends ITestAction {
algorithm: types.Algorithm;
data: types.BufferSource;
signature: types.BufferSource;
key: IImportKeyParams | IImportKeyPairParams;
}
export interface ITestDeriveBitsAction extends ITestAction {
algorithm: types.Algorithm;
key: IImportKeyParams | IImportKeyPairParams;
data: types.BufferSource;
length: number;
}
export interface ITestDeriveKeyAction extends ITestAction {
algorithm: types.Algorithm;
key: IImportKeyParams | IImportKeyPairParams;
derivedKeyType: types.Algorithm;
keyUsages: types.KeyUsage[];
format: types.KeyFormat;
keyData: types.BufferSource | types.JsonWebKey;
}
export interface ITestWrapKeyAction extends ITestAction {
key: IImportKeyParams | IImportKeyPairParams;
algorithm: types.Algorithm;
wKey: IImportKeyParams;
wrappedKey?: types.BufferSource;
}
export interface ITestImportAction extends IImportKeyParams, ITestAction {
}
export interface ITestDigestAction extends ITestAction {
algorithm: types.AlgorithmIdentifier;
data: types.BufferSource;
hash: types.BufferSource;
}
export interface ITestActions {
generateKey?: ITestGenerateKeyAction[];
encrypt?: ITestEncryptAction[];
wrapKey?: ITestWrapKeyAction[];
sign?: ITestSignAction[];
import?: ITestImportAction[];
deriveBits?: ITestDeriveBitsAction[];
deriveKey?: ITestDeriveKeyAction[];
digest?: ITestDigestAction[];
}
export interface ITestParams extends ITestMochaFunction {
name: string;
actions: ITestActions;
}
async function getKeys(crypto: Crypto, key: IImportKeyParams | IImportKeyPairParams) {
const keys = {} as types.CryptoKeyPair;
if ("privateKey" in key) {
keys.privateKey = await crypto.subtle.importKey(
key.privateKey.format,
key.privateKey.data,
key.privateKey.algorithm,
key.privateKey.extractable,
key.privateKey.keyUsages);
keys.publicKey = await crypto.subtle.importKey(
key.publicKey.format,
key.publicKey.data,
key.publicKey.algorithm,
key.publicKey.extractable,
key.publicKey.keyUsages);
} else {
keys.privateKey = keys.publicKey = await crypto.subtle.importKey(
key.format,
key.data,
key.algorithm,
key.extractable,
key.keyUsages);
}
return keys;
}
function wrapSkipOnly(item: Mocha.TestFunction, params: ITestMochaFunction): Mocha.PendingTestFunction;
function wrapSkipOnly(item: Mocha.SuiteFunction, params: ITestMochaFunction): Mocha.PendingSuiteFunction;
function wrapSkipOnly(item: Mocha.TestFunction | Mocha.SuiteFunction, params: ITestMochaFunction) {
return params.skip
? item.skip
: params.only
? item.only
: item;
}
async function wrapTest(promise: () => Promise<void>, action: ITestAction, index: number) {
wrapSkipOnly(it, action)(action.name || `#${index + 1}`, async () => {
if (action.error) {
if (typeof (action.error) === "boolean") {
await assert.rejects(promise());
} else {
await assert.rejects(promise(), action.error);
}
} else {
await promise();
}
});
}
export function testCrypto(crypto: Crypto, params: ITestParams[]) {
params.forEach((param) => {
wrapSkipOnly(context, param)(param.name, () => {
//#region Generate key
if (param.actions.generateKey) {
context("Generate Key", () => {
param.actions.generateKey!.forEach((action, index) => {
wrapTest(async () => {
const algorithm = Object.assign({}, action.algorithm);
algorithm.name = algorithm.name.toLowerCase();
const key = await crypto.subtle.generateKey(
algorithm,
action.extractable,
action.keyUsages,
);
assert.equal(!!key, true);
if (!key.privateKey) {
assert.equal(key.algorithm.name, action.algorithm.name, "Algorithm name MUST be equal to incoming algorithm and in the same case");
assert.equal(key.extractable, action.extractable);
assert.deepEqual([...key.usages].sort(), [...action.keyUsages].sort());
} else {
assert.equal(!!key.privateKey, true);
assert.equal(key.privateKey.algorithm.name, action.algorithm.name, "Algorithm name MUST be equal to incoming algorithm and in the same case");
assert.equal(key.privateKey.extractable, action.extractable);
assert.equal(!!key.publicKey, true);
assert.equal(key.publicKey.algorithm.name, action.algorithm.name, "Algorithm name MUST be equal to incoming algorithm and in the same case");
assert.equal(key.publicKey.extractable, true);
}
}, action, index);
});
});
}
//#endregion
//#region encrypt
if (param.actions.encrypt) {
context("Encrypt/Decrypt", () => {
param.actions.encrypt!.forEach((action, index) => {
wrapTest(async () => {
// import keys
const keys = await getKeys(crypto, action.key);
const encKey = keys.publicKey;
const decKey = keys.privateKey;
const algorithm = Object.assign({}, action.algorithm);
algorithm.name = algorithm.name.toLowerCase();
// encrypt
const enc = await crypto.subtle.encrypt(algorithm, encKey, action.data);
// decrypt
let dec = await crypto.subtle.decrypt(algorithm, decKey, enc);
assert.equal(pvtsutils.Convert.ToHex(dec), pvtsutils.Convert.ToHex(action.data));
dec = await crypto.subtle.decrypt(algorithm, decKey, action.encData);
assert.equal(pvtsutils.Convert.ToHex(dec), pvtsutils.Convert.ToHex(action.data));
}, action, index);
});
});
}
//#endregion
//#region Import/Export
if (param.actions.import) {
context("Import/Export", () => {
param.actions.import!.forEach((action, index) => {
wrapTest(async () => {
const importedKey = await crypto.subtle.importKey(
action.format,
action.data,
action.algorithm,
action.extractable,
action.keyUsages);
// Can't continue if key is not extractable.
if (!action.extractable) {
return;
}
const exportedData = await crypto.subtle.exportKey(
action.format,
importedKey);
if (action.format === "jwk") {
exportedData.key_ops.sort();
(action.data as Required<types.JsonWebKey>).key_ops.sort();
assert.deepEqual(exportedData, action.data);
} else {
assert.equal(pvtsutils.Convert.ToHex(exportedData as ArrayBuffer), pvtsutils.Convert.ToHex(action.data as ArrayBuffer));
}
}, action, index);
});
});
}
//#endregion
//#region Sign/Verify
if (param.actions.sign) {
context("Sign/Verify", () => {
param.actions.sign!.forEach((action, index) => {
wrapTest(async () => {
// import keys
const keys = await getKeys(crypto, action.key);
const verifyKey = keys.publicKey;
const signKey = keys.privateKey;
const algorithm = Object.assign({}, action.algorithm);
algorithm.name = algorithm.name.toLowerCase();
// sign
const signature = await crypto.subtle.sign(algorithm, signKey, action.data);
// verify
let ok = await crypto.subtle.verify(algorithm, verifyKey, signature, action.data);
assert.equal(true, ok, "Cannot verify signature from Action data");
ok = await crypto.subtle.verify(algorithm, verifyKey, action.signature, action.data);
if (!ok) {
assert.equal(pvtsutils.Convert.ToHex(signature), pvtsutils.Convert.ToHex(action.signature));
}
assert.equal(true, ok);
}, action, index);
});
});
}
//#endregion
//#region Derive bits
if (param.actions.deriveBits) {
context("Derive bits", () => {
param.actions.deriveBits!.forEach((action, index) => {
wrapTest(async () => {
// import keys
const keys = await getKeys(crypto, action.key);
const algorithm = Object.assign({}, action.algorithm, { public: keys.publicKey });
algorithm.name = algorithm.name.toLowerCase();
// derive bits
const derivedBits = await crypto.subtle.deriveBits(algorithm, keys.privateKey, action.length);
assert.equal(pvtsutils.Convert.ToHex(derivedBits), pvtsutils.Convert.ToHex(action.data));
}, action, index);
});
});
}
//#endregion
//#region Derive key
if (param.actions.deriveKey) {
context("Derive key", () => {
param.actions.deriveKey!.forEach((action, index) => {
wrapTest(async () => {
// import keys
const keys = await getKeys(crypto, action.key);
const algorithm = Object.assign({}, action.algorithm, { public: keys.publicKey });
algorithm.name = algorithm.name.toLowerCase();
// derive key
const derivedKey = await crypto.subtle.deriveKey(algorithm, keys.privateKey, action.derivedKeyType, true, action.keyUsages);
const keyData = await crypto.subtle.exportKey(action.format, derivedKey);
if (action.format === "jwk") {
assert.deepEqual(keyData, action.keyData);
} else {
assert.equal(pvtsutils.Convert.ToHex(keyData as ArrayBuffer), pvtsutils.Convert.ToHex(action.keyData as ArrayBuffer));
}
}, action, index);
});
});
}
//#endregion
//#region Digest
if (param.actions.digest) {
context("Digest", () => {
param.actions.digest!.forEach((action, index) => {
wrapTest(async () => {
const hash = await crypto.subtle.digest(action.algorithm, action.data);
assert.equal(pvtsutils.Convert.ToHex(hash), pvtsutils.Convert.ToHex(action.hash));
}, action, index);
});
});
}
//#endregion
//#region Wrap/Unwrap key
if (param.actions.wrapKey) {
context("Wrap/Unwrap key", () => {
param.actions.wrapKey!.forEach((action, index) => {
wrapTest(async () => {
const wKey = (await getKeys(crypto, action.wKey)).privateKey;
const key = await getKeys(crypto, action.key);
const wrappedKey = await crypto.subtle.wrapKey(action.wKey.format, wKey, key.publicKey, action.algorithm);
if (action.wrappedKey) {
assert.equal(pvtsutils.Convert.ToHex(wrappedKey), pvtsutils.Convert.ToHex(action.wrappedKey));
}
const unwrappedKey = await crypto.subtle.unwrapKey(
action.wKey.format,
wrappedKey,
key.privateKey,
action.algorithm,
action.wKey.algorithm,
action.wKey.extractable,
action.wKey.keyUsages);
assert.deepEqual(unwrappedKey.algorithm, wKey.algorithm);
}, action, index);
});
});
}
//#endregion
});
});
}

View File

@ -0,0 +1,2 @@
export * from "./init";
export * from "./helper";

View File

@ -0,0 +1,46 @@
import * as types from "@peculiar/webcrypto-types";
import { Crypto as NodeCrypto } from "@peculiar/webcrypto";
import { Crypto as WebCrypto, setCrypto } from "../../src";
export const nodeCrypto = new NodeCrypto();
const nativeGenerateKey = nodeCrypto.subtle.generateKey;
const nativeExportKey = nodeCrypto.subtle.exportKey;
// asmCrypto doesn't have key generation function and uses native generateKey with RSA-PSS
nodeCrypto.subtle.generateKey = async function (this: types.SubtleCrypto, ...args: any[]) {
if (args[0]?.name !== "RSA-PSS") {
throw new Error("Function is broken for test cases");
}
return nativeGenerateKey.apply(this, args as any);
} as any;
// asmCrypto doesn't have key generation function and uses native exportKey with RSA-PSS
nodeCrypto.subtle.exportKey = async function (this: types.SubtleCrypto, ...args: any[]) {
if (!(
(args[0] === "pkcs8"
|| args[0] === "spki")
&& args[1].algorithm.name === "RSA-PSS"
)) {
throw new Error("Function is broken for test cases");
}
return nativeExportKey.apply(this, args as any);
} as any;
// break crypto functions
[
"decrypt", "encrypt",
"wrapKey", "unwrapKey",
"sign", "verify",
"deriveBits", "deriveKey",
"importKey",
"digest",
].forEach((o) => {
(nodeCrypto.subtle as any)[o] = async () => {
throw new Error("Function is broken for test cases");
};
});
// set native crypto
setCrypto(nodeCrypto as types.Crypto);
export const webCrypto = new WebCrypto();

View File

@ -18,6 +18,9 @@
"@peculiar/webcrypto-pkcs11": [
"./packages/pkcs11/src"
],
"@peculiar/webcrypto-web": [
"./packages/web/src"
],
},
"skipLibCheck": true, // TODO @peculiar/x509 should use @peculiar/webcrypto-types. Remove this line when it's fixed
},

View File

@ -162,7 +162,7 @@
asn1js "^3.0.4"
tslib "^2.4.0"
"@peculiar/asn1-schema@^2.1.7", "@peculiar/asn1-schema@^2.1.8":
"@peculiar/asn1-schema@^2.1.6", "@peculiar/asn1-schema@^2.1.7", "@peculiar/asn1-schema@^2.1.8":
version "2.1.8"
resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.1.8.tgz#552300a1ed7991b22c9abf789a3920a3cb94c26b"
integrity sha512-u34H/bpqCdDuqrCVZvH0vpwFBT/dNEdNY+eE8u4IuC26yYnhDkXF4+Hliqca88Avbb7hyN2EF/eokyDdyS7G/A==
@ -224,6 +224,37 @@
estree-walker "^2.0.1"
picomatch "^2.2.2"
"@stablelib/binary@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@stablelib/binary/-/binary-1.0.1.tgz#c5900b94368baf00f811da5bdb1610963dfddf7f"
integrity sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==
dependencies:
"@stablelib/int" "^1.0.1"
"@stablelib/hash@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@stablelib/hash/-/hash-1.0.1.tgz#3c944403ff2239fad8ebb9015e33e98444058bc5"
integrity sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg==
"@stablelib/int@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@stablelib/int/-/int-1.0.1.tgz#75928cc25d59d73d75ae361f02128588c15fd008"
integrity sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==
"@stablelib/sha3@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@stablelib/sha3/-/sha3-1.0.1.tgz#c2d2955be3d184031200a2fa04f801eea4c8982b"
integrity sha512-82OHZcxWsJAS34L64VItIbqZdcdYgBJmeToYaou9lUA+iMjajdfOVZDDrditfV8C8yXUDrlS3BuMRWmKf9NQhQ==
dependencies:
"@stablelib/binary" "^1.0.1"
"@stablelib/hash" "^1.0.1"
"@stablelib/wipe" "^1.0.1"
"@stablelib/wipe@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@stablelib/wipe/-/wipe-1.0.1.tgz#d21401f1d59ade56a62e139462a97f104ed19a36"
integrity sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==
"@tsconfig/node10@^1.0.7":
version "1.0.8"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9"
@ -430,7 +461,12 @@ array-union@^2.1.0:
resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
asn1js@^3.0.4:
asmcrypto.js@^2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/asmcrypto.js/-/asmcrypto.js-2.3.2.tgz#b9f84bd0a1fb82f21f8c29cc284a707ad17bba2e"
integrity sha512-3FgFARf7RupsZETQ1nHnhLUUvpcttcCq1iZCaVAbJZbCZ5VNRrNyvpDyHTOb0KC3llFcsyOT/a99NZcCbeiEsA==
asn1js@^3.0.4, asn1js@^3.0.5:
version "3.0.5"
resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.5.tgz#5ea36820443dbefb51cc7f88a2ebb5b462114f38"
integrity sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==
@ -449,6 +485,11 @@ binary-extensions@^2.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
bn.js@^4.4.0:
version "4.12.0"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@ -464,6 +505,11 @@ braces@^3.0.2, braces@~3.0.2:
dependencies:
fill-range "^7.0.1"
brorand@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==
browser-stdout@1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
@ -592,6 +638,14 @@ deep-is@^0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
des.js@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843"
integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==
dependencies:
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
diff@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
@ -616,6 +670,18 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"
"elliptic@https://github.com/mahrud/elliptic":
version "6.5.0"
resolved "https://github.com/mahrud/elliptic#75637c76678e83c31682fd967c2fa9ff4761b3fc"
dependencies:
bn.js "^4.4.0"
brorand "^1.0.1"
hash.js "^1.0.0"
hmac-drbg "^1.0.0"
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.0"
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
@ -969,11 +1035,28 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
hash.js@^1.0.0, hash.js@^1.0.3:
version "1.1.7"
resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
dependencies:
inherits "^2.0.3"
minimalistic-assert "^1.0.1"
he@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
hmac-drbg@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=
dependencies:
hash.js "^1.0.3"
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
ignore@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
@ -1000,7 +1083,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
inherits@2:
inherits@2, inherits@^2.0.1, inherits@^2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@ -1173,6 +1256,16 @@ micromatch@^4.0.4:
braces "^3.0.2"
picomatch "^2.3.1"
minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
minimatch@4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4"