add web package
This commit is contained in:
parent
5a5412ecd3
commit
0bd5151604
|
@ -4,5 +4,5 @@ require:
|
|||
extension:
|
||||
- ts
|
||||
spec:
|
||||
- "packages/**/*.ts"
|
||||
- "packages/**/*.spec.ts"
|
||||
exit: true
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# `@peculiar/webcrypto-web`
|
||||
|
||||
> TODO: description
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
const webcryptoWeb = require('@peculiar/webcrypto-web');
|
||||
|
||||
// TODO: DEMONSTRATE API
|
||||
```
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export * from "./native";
|
||||
export * from "./crypto";
|
||||
export * from "./key";
|
|
@ -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);
|
||||
};
|
||||
}
|
|
@ -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 };
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import { Crypto, nativeCrypto } from ".";
|
||||
import "./init";
|
||||
|
||||
if (nativeCrypto) {
|
||||
Object.freeze(nativeCrypto.getRandomValues);
|
||||
}
|
||||
|
||||
export const crypto = new Crypto();
|
||||
export * from ".";
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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";
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export * from "./crypto";
|
||||
export * from "./des_cbc";
|
||||
export * from "./des_ede3_cbc";
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export * from "./ec_dh";
|
||||
export * from "./ec_dsa";
|
||||
export * from "./crypto";
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export * from "./eddsa";
|
||||
export * from "./ecdh_es";
|
||||
export * from "./private_key";
|
||||
export * from "./public_key";
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export * from "./hmac";
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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";
|
|
@ -0,0 +1 @@
|
|||
export * from "./pbkdf2";
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export * from "./crypto";
|
||||
export * from "./rsa_oaep";
|
||||
export * from "./rsa_pss";
|
||||
export * from "./rsa_ssa";
|
||||
export * from "./rsa_es";
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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";
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
import { Sha1Provider } from "./sha_1";
|
||||
|
||||
export class Sha256Provider extends Sha1Provider {
|
||||
public override name = "SHA-256";
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
import { Sha1Provider } from "./sha_1";
|
||||
|
||||
export class Sha512Provider extends Sha1Provider {
|
||||
public override name = "SHA-512";
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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 ".";
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
interface AlgorithmConverter {
|
||||
jwk2alg(alg: string): Algorithm;
|
||||
alg2jwk(alg: Algorithm): string;
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
|
||||
]);
|
||||
|
||||
});
|
|
@ -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"],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
});
|
|
@ -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);
|
||||
});
|
||||
|
||||
});
|
|
@ -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);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -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"],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
|
@ -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=="),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
|
@ -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"],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
});
|
|
@ -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"),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
});
|
|
@ -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
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
export * from "./init";
|
||||
export * from "./helper";
|
|
@ -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();
|
|
@ -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
|
||||
},
|
||||
|
|
99
yarn.lock
99
yarn.lock
|
@ -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"
|
||||
|
|
Reference in New Issue