From 74d0ef2b30c017a3a214a1bcbf036d67aede731d Mon Sep 17 00:00:00 2001 From: Neal Fennimore Date: Mon, 29 May 2023 15:07:26 -0400 Subject: [PATCH 01/20] Update to use node 18 --- .gitignore | 3 ++- package-lock.json | 2 +- package.json | 2 +- tsconfig.json | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 43a3c47..2e9e7c8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ coverage docs shell.nix .envrc -.direnv \ No newline at end of file +.direnv +scratchpad.js \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 6f85eda..fa7fd35 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "typescript": "^4.7.4" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/@ampproject/remapping": { diff --git a/package.json b/package.json index e640df0..944eb27 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "exports": "./lib/index.js", "type": "module", "engines": { - "node": ">=16" + "node": ">=18" }, "scripts": { "prepublishOnly": "npm run test && npm run build", diff --git a/tsconfig.json b/tsconfig.json index 6c1ddc2..5f43af7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "exclude": [ - "**/__tests__/*" + "**/__tests__/*", + "lib/**/*", ], "compilerOptions": { /* Visit https://aka.ms/tsconfig to read more about this file */ From 495939980634c89b0df310a4a228d6edfb0c03f4 Mon Sep 17 00:00:00 2001 From: Neal Fennimore Date: Mon, 29 May 2023 15:08:18 -0400 Subject: [PATCH 02/20] Add in intial proxy support --- global.d.ts | 7 ++++ src/ec/ecdsa.ts | 14 ++++++-- src/proxy.ts | 87 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 src/proxy.ts diff --git a/global.d.ts b/global.d.ts index 356a320..12815ec 100644 --- a/global.d.ts +++ b/global.d.ts @@ -1,2 +1,9 @@ declare var encode: Function; declare var decode: Function; + +interface ProxyConstructor { + new ( + target: TSource, + handler: ProxyHandler + ): TTarget; +} diff --git a/src/ec/ecdsa.ts b/src/ec/ecdsa.ts index 5fff681..45a39af 100644 --- a/src/ec/ecdsa.ts +++ b/src/ec/ecdsa.ts @@ -3,6 +3,7 @@ * @module */ import * as params from "../params.js"; +import * as proxy from "../proxy.js"; import * as WebCrypto from "../webcrypto.js"; import { Alg, @@ -25,12 +26,19 @@ export const generateKey = async ( }, extractable?: boolean, keyUsages?: KeyUsage[] -): Promise => - await EcShared.generateKey( +) => { + const keyPair = (await EcShared.generateKey( { ...algorithm, name: Alg.Variant.ECDSA }, extractable, keyUsages - ); + )) as EcdsaCryptoKeyPair; + + return proxy.proxifyKeyPair< + EcdsaCryptoKeyPair, + EcdsaPrivCryptoKey, + EcdsaPubCryptoKey + >(keyPair); +}; /** * Import an ECDSA public or private key diff --git a/src/proxy.ts b/src/proxy.ts new file mode 100644 index 0000000..3a6a141 --- /dev/null +++ b/src/proxy.ts @@ -0,0 +1,87 @@ +/** + * Code related to proxying CryptoKey and CryptoKeyPair + * @module + */ + +export interface ProxiedPubCryptoKey { + self: T; + verify: Function; +} + +export interface ProxiedPrivCryptoKey { + self: T; + sign: Function; +} + +export interface ProxiedCryptoKeyPair< + TKeyPair extends CryptoKeyPair, + TPrivKey extends CryptoKey, + TPubKey extends CryptoKey +> { + self: TKeyPair; + privateKey: ProxiedPrivCryptoKey; + publicKey: ProxiedPubCryptoKey; +} + +export function proxifyPubKey(pubKey: TPubKey) { + const handler = { + get(target: TPubKey, prop: string) { + switch (prop) { + case "self": + return target; + case "verify": + return () => console.log("verify called"); + } + + return Reflect.get(target, prop); + }, + }; + + return new Proxy>(pubKey, handler); +} + +export function proxifyPrivKey(privKey: TPrivKey) { + const handler = { + get(target: TPrivKey, prop: string) { + switch (prop) { + case "self": + return target; + case "sign": + return () => console.log("sign called"); + } + + return Reflect.get(target, prop); + }, + }; + + return new Proxy>( + privKey, + handler + ); +} + +export function proxifyKeyPair< + TKeyPair extends CryptoKeyPair, + TPrivKey extends CryptoKey, + TPubKey extends CryptoKey +>(keyPair: TKeyPair) { + return new Proxy< + TKeyPair, + ProxiedCryptoKeyPair + >(keyPair, { + get(target: TKeyPair, prop: string) { + switch (prop) { + case "self": + return target; + case "privateKey": + return proxifyPrivKey( + target.privateKey as TPrivKey + ); + case "publicKey": + return proxifyPubKey(target.publicKey as TPubKey); + } + + return Reflect.get(target, prop); + }, + }); +} From 5f138738d5d540ab94c7a255401407d225748be9 Mon Sep 17 00:00:00 2001 From: Neal Fennimore Date: Mon, 29 May 2023 16:33:25 -0400 Subject: [PATCH 03/20] Update ECDSA to be proxied --- .../__snapshots__/ecdsa.test.ts.snap | 33 +++- src/ec/__tests__/ecdsa.test.ts | 145 ++++++++++++------ src/ec/ecdsa.ts | 112 ++++++++++++-- src/ec/shared.ts | 31 ++++ src/proxy.ts | 112 +++++++------- 5 files changed, 322 insertions(+), 111 deletions(-) diff --git a/src/ec/__tests__/__snapshots__/ecdsa.test.ts.snap b/src/ec/__tests__/__snapshots__/ecdsa.test.ts.snap index e793d33..38b005b 100644 --- a/src/ec/__tests__/__snapshots__/ecdsa.test.ts.snap +++ b/src/ec/__tests__/__snapshots__/ecdsa.test.ts.snap @@ -1,6 +1,37 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ECDSA should generate key 1`] = ` +exports[`ECDSA Original should generate key 1`] = ` +Object { + "privateKey": CryptoKey { + Symbol(kKeyObject): PrivateKeyObject { + Symbol(kKeyType): "private", + }, + Symbol(kAlgorithm): Object { + "name": "ECDSA", + "namedCurve": "P-521", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "sign", + ], + }, + "publicKey": CryptoKey { + Symbol(kKeyObject): PublicKeyObject { + Symbol(kKeyType): "public", + }, + Symbol(kAlgorithm): Object { + "name": "ECDSA", + "namedCurve": "P-521", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "verify", + ], + }, +} +`; + +exports[`ECDSA Proxied should generate key 1`] = ` Object { "privateKey": CryptoKey { Symbol(kKeyObject): PrivateKeyObject { diff --git a/src/ec/__tests__/ecdsa.test.ts b/src/ec/__tests__/ecdsa.test.ts index 04f7345..0c8d0ea 100644 --- a/src/ec/__tests__/ecdsa.test.ts +++ b/src/ec/__tests__/ecdsa.test.ts @@ -1,54 +1,111 @@ import * as EC from "../index.js"; -import { EcdsaCryptoKeyPair } from "../shared.js"; +import { EcdsaCryptoKeyPair, EcdsaProxiedCryptoKeyPair } from "../shared.js"; const { ECDSA } = EC; describe("ECDSA", () => { - let keyPair: EcdsaCryptoKeyPair; - beforeEach(async () => { - keyPair = await ECDSA.generateKey(); - }); - it("should generate key", async () => { - expect(keyPair).toMatchSnapshot(); - }); - it("should import and export key", async () => { - let jwk = await ECDSA.exportKey("jwk", keyPair.publicKey); - const importedPubKey = await ECDSA.importKey( - "jwk", - jwk, - { namedCurve: "P-521" }, - true, - ["verify"] - ); - - expect(await ECDSA.exportKey("jwk", importedPubKey)).toEqual(jwk); - - jwk = await ECDSA.exportKey("jwk", keyPair.privateKey); - const importedPrivKey = await ECDSA.importKey( - "jwk", - jwk, - { namedCurve: "P-521" }, - true, - ["sign"] - ); - - expect(await ECDSA.exportKey("jwk", importedPrivKey)).toEqual(jwk); + describe("Original", () => { + let proxiedKeyPair: EcdsaProxiedCryptoKeyPair; + let keyPair: EcdsaCryptoKeyPair; + beforeEach(async () => { + proxiedKeyPair = await ECDSA.generateKey(); + keyPair = proxiedKeyPair.self; + }); + + it("should generate key", async () => { + expect(keyPair).toMatchSnapshot(); + }); + it("should import and export key", async () => { + let jwk = await ECDSA.exportKey("jwk", keyPair.publicKey); + const importedPubKey = await ECDSA.importKey( + "jwk", + jwk, + { namedCurve: "P-521" }, + true, + ["verify"] + ); + + expect(await ECDSA.exportKey("jwk", importedPubKey.self)).toEqual( + jwk + ); + + jwk = await ECDSA.exportKey("jwk", keyPair.privateKey); + const importedPrivKey = await ECDSA.importKey( + "jwk", + jwk, + { namedCurve: "P-521" }, + true, + ["sign"] + ); + + expect(await ECDSA.exportKey("jwk", importedPrivKey.self)).toEqual( + jwk + ); + }); + it("should sign and verify", async () => { + const text = encode("a message"); + const signature = await ECDSA.sign( + { hash: "SHA-512" }, + keyPair.privateKey, + text + ); + + expect( + await ECDSA.verify( + { hash: "SHA-512" }, + keyPair.publicKey, + signature, + text + ) + ).toBe(true); + }); }); - it("should sign and verify", async () => { - const text = encode("a message"); - const signature = await ECDSA.sign( - { hash: "SHA-512" }, - keyPair.privateKey, - text - ); - - expect( - await ECDSA.verify( + + describe("Proxied", () => { + let keyPair: EcdsaProxiedCryptoKeyPair; + beforeEach(async () => { + keyPair = await ECDSA.generateKey(); + }); + it("should generate key", async () => { + expect(keyPair.self).toMatchSnapshot(); + }); + it("should import and export key", async () => { + let jwk = await keyPair.publicKey.exportKey("jwk"); + const importedPubKey = await ECDSA.importKey( + "jwk", + jwk, + { namedCurve: "P-521" }, + true, + ["verify"] + ); + + expect(await importedPubKey.exportKey("jwk")).toEqual(jwk); + + jwk = await keyPair.privateKey.exportKey("jwk"); + const importedPrivKey = await ECDSA.importKey( + "jwk", + jwk, + { namedCurve: "P-521" }, + true, + ["sign"] + ); + + expect(await importedPrivKey.exportKey("jwk")).toEqual(jwk); + }); + it("should sign and verify", async () => { + const text = encode("a message"); + const signature = await keyPair.privateKey.sign( { hash: "SHA-512" }, - keyPair.publicKey, - signature, text - ) - ).toBe(true); + ); + + expect( + await keyPair.publicKey.verify( + { hash: "SHA-512" }, + signature, + text + ) + ).toBe(true); + }); }); }); diff --git a/src/ec/ecdsa.ts b/src/ec/ecdsa.ts index 45a39af..3b28134 100644 --- a/src/ec/ecdsa.ts +++ b/src/ec/ecdsa.ts @@ -7,18 +7,70 @@ import * as proxy from "../proxy.js"; import * as WebCrypto from "../webcrypto.js"; import { Alg, + EcShared, EcdsaCryptoKeyPair, EcdsaPrivCryptoKey, + EcdsaProxiedCryptoKeyPair, + EcdsaProxiedPrivCryptoKey, + EcdsaProxiedPubCryptoKey, EcdsaPubCryptoKey, - EcShared, } from "./shared.js"; +const handlers: proxy.ProxyKeyPairHandlers< + EcdsaPrivCryptoKey, + EcdsaPubCryptoKey +> = { + privHandler: { + get(target: EcdsaPrivCryptoKey, prop: string) { + switch (prop) { + case "self": + return target; + case "sign": + return ( + algorithm: Omit, + data: BufferSource + ) => sign(algorithm, target, data); + case "exportKey": + return (format: KeyFormat) => exportKey(format, target); + } + + return Reflect.get(target, prop); + }, + }, + pubHandler: { + get(target: EcdsaPubCryptoKey, prop: string) { + switch (prop) { + case "self": + return target; + case "verify": + return ( + algorithm: Omit, + signature: BufferSource, + data: BufferSource + ) => verify(algorithm, target, signature, data); + case "exportKey": + return (format: KeyFormat) => exportKey(format, target); + } + + return Reflect.get(target, prop); + }, + }, +}; + /** * Generate a new ECDSA keypair * @example * ```ts * const keyPair = await ECDSA.generateKey(); * ``` + * @example + * ```ts + * const keyPair = await ECDSA.generateKey({ namedCurve: "P-256" }, false); + * ``` + * @example + * ```ts + * const keyPair = await ECDSA.generateKey({ namedCurve: "P-256" }, true, ['sign', 'verify']); + * ``` */ export const generateKey = async ( algorithm: Omit = { @@ -26,7 +78,7 @@ export const generateKey = async ( }, extractable?: boolean, keyUsages?: KeyUsage[] -) => { +): Promise => { const keyPair = (await EcShared.generateKey( { ...algorithm, name: Alg.Variant.ECDSA }, extractable, @@ -36,15 +88,21 @@ export const generateKey = async ( return proxy.proxifyKeyPair< EcdsaCryptoKeyPair, EcdsaPrivCryptoKey, - EcdsaPubCryptoKey - >(keyPair); + EcdsaProxiedPrivCryptoKey, + EcdsaPubCryptoKey, + EcdsaProxiedPubCryptoKey + >(handlers)(keyPair); }; /** * Import an ECDSA public or private key * @example * ```ts - * const key = await ECDSA.importKey("jwk", pubKey, { namedCurve: "P-521" }, true, ['verify']); + * const pubKey = await ECDSA.importKey("jwk", pubKeyJwk, { namedCurve: "P-521" }, true, ['verify']); + * ``` + * @example + * ```ts + * const privKey = await ECDSA.importKey("jwk", privKeyJwk, { namedCurve: "P-521" }, true, ['sign']); * ``` */ export const importKey = async ( @@ -55,8 +113,8 @@ export const importKey = async ( }, extractable?: boolean, keyUsages?: KeyUsage[] -): Promise => - await EcShared.importKey( +): Promise => { + const importedKey = await EcShared.importKey( format, key, { ...algorithm, name: Alg.Variant.ECDSA }, @@ -64,11 +122,35 @@ export const importKey = async ( keyUsages ); + if (importedKey.type === "private") { + return proxy.proxifyPrivKey< + EcdsaPrivCryptoKey, + EcdsaProxiedPrivCryptoKey + >(handlers.privHandler)(importedKey as EcdsaPrivCryptoKey); + } else { + return proxy.proxifyPubKey( + handlers.pubHandler + )(importedKey as EcdsaPubCryptoKey); + } +}; + /** * Export an ECDSA public or private key * @example * ```ts - * const pubKeyJwk = await ECDSA.importKey("jwk", keyPair.publicKey); + * const pubKeyJwk = await ECDSA.exportKey("jwk", keyPair.publicKey.self); + * ``` + * @example + * ```ts + * const privKeyJwk = await ECDSA.exportKey("jwk", keyPair.privateKey.self); + * ``` + * @example + * ```ts + * const pubKeyJwk = await keyPair.publicKey.exportKey("jwk"); + * ``` + * @example + * ```ts + * const privKeyJwk = await keyPair.privateKey.exportKey("jwk"); * ``` */ export const exportKey = async ( @@ -81,7 +163,12 @@ export const exportKey = async ( * @example * ```ts * const message = new TextEncoder().encode("a message"); - * const signature = await ECDSA.sign({hash: "SHA-512"}, keyPair.privateKey, message); + * const signature = await ECDSA.sign({hash: "SHA-512"}, keyPair.privateKey.self, message); + * ``` + * @example + * ```ts + * const message = new TextEncoder().encode("a message"); + * const signature = await keyPair.privateKey.sign({hash: "SHA-512"}, message); * ``` */ export async function sign( @@ -104,7 +191,12 @@ export async function sign( * @example * ```ts * const message = new TextEncoder().encode("a message"); - * const isVerified = await ECDSA.verify({hash: "SHA-512"}, keyPair.publicKey, signature, message); + * const isVerified = await ECDSA.verify({hash: "SHA-512"}, keyPair.publicKey.self, signature, message); + * ``` + * @example + * ```ts + * const message = new TextEncoder().encode("a message"); + * const isVerified = await keyPair.publicKey.verify({hash: "SHA-512"}, signature, message); * ``` */ export async function verify( diff --git a/src/ec/shared.ts b/src/ec/shared.ts index 63eaa14..3fce08a 100644 --- a/src/ec/shared.ts +++ b/src/ec/shared.ts @@ -4,6 +4,7 @@ */ import { getKeyUsagePairsByAlg } from "../key_usages.js"; import * as params from "../params.js"; +import * as proxy from "../proxy.js"; import * as WebCrypto from "../webcrypto.js"; export interface EcdhPubCryptoKey extends CryptoKey { @@ -28,6 +29,36 @@ export interface EcdsaCryptoKeyPair extends CryptoKeyPair { publicKey: EcdsaPubCryptoKey; privateKey: EcdsaPrivCryptoKey; } + +export interface EcdsaProxiedPubCryptoKey + extends proxy.ProxiedPubCryptoKey { + verify: ( + algorithm: Omit, + signature: BufferSource, + data: BufferSource + ) => Promise; + + exportKey: (format: KeyFormat) => Promise; +} +export interface EcdsaProxiedPrivCryptoKey + extends proxy.ProxiedPrivCryptoKey { + sign: ( + algorithm: Omit, + data: BufferSource + ) => Promise; + + exportKey: (format: KeyFormat) => Promise; +} + +export interface EcdsaProxiedCryptoKeyPair + extends proxy.ProxiedCryptoKeyPair< + EcdsaCryptoKeyPair, + EcdsaPrivCryptoKey, + EcdsaProxiedPrivCryptoKey, + EcdsaPubCryptoKey, + EcdsaProxiedPubCryptoKey + > {} + export type EcCryptoKeys = | EcdhPubCryptoKey | EcdhPrivCryptoKey diff --git a/src/proxy.ts b/src/proxy.ts index 3a6a141..9344ae7 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -5,83 +5,83 @@ export interface ProxiedPubCryptoKey { self: T; - verify: Function; } export interface ProxiedPrivCryptoKey { self: T; - sign: Function; } export interface ProxiedCryptoKeyPair< TKeyPair extends CryptoKeyPair, TPrivKey extends CryptoKey, - TPubKey extends CryptoKey + TProxPrivKey extends ProxiedPrivCryptoKey, + TPubKey extends CryptoKey, + TProxPubKey extends ProxiedPubCryptoKey > { self: TKeyPair; - privateKey: ProxiedPrivCryptoKey; - publicKey: ProxiedPubCryptoKey; + privateKey: TProxPrivKey; + publicKey: TProxPubKey; } - -export function proxifyPubKey(pubKey: TPubKey) { - const handler = { - get(target: TPubKey, prop: string) { - switch (prop) { - case "self": - return target; - case "verify": - return () => console.log("verify called"); - } - - return Reflect.get(target, prop); - }, +export function proxifyPubKey< + TPubKey extends CryptoKey, + TProxPubKey extends ProxiedPubCryptoKey +>(handler: ProxyHandler) { + return function _proxifyPubKey(privKey: TPubKey) { + return new Proxy(privKey, handler); }; - - return new Proxy>(pubKey, handler); } -export function proxifyPrivKey(privKey: TPrivKey) { - const handler = { - get(target: TPrivKey, prop: string) { - switch (prop) { - case "self": - return target; - case "sign": - return () => console.log("sign called"); - } - - return Reflect.get(target, prop); - }, +export function proxifyPrivKey< + TPrivKey extends CryptoKey, + TProxPrivKey extends ProxiedPrivCryptoKey +>(handler: ProxyHandler) { + return function _proxifyPrivKey(privKey: TPrivKey) { + return new Proxy(privKey, handler); }; +} - return new Proxy>( - privKey, - handler - ); +export interface ProxyKeyPairHandlers< + TPrivKey extends CryptoKey, + TPubKey extends CryptoKey +> { + privHandler: ProxyHandler; + pubHandler: ProxyHandler; } export function proxifyKeyPair< TKeyPair extends CryptoKeyPair, TPrivKey extends CryptoKey, - TPubKey extends CryptoKey ->(keyPair: TKeyPair) { - return new Proxy< - TKeyPair, - ProxiedCryptoKeyPair - >(keyPair, { - get(target: TKeyPair, prop: string) { - switch (prop) { - case "self": - return target; - case "privateKey": - return proxifyPrivKey( - target.privateKey as TPrivKey - ); - case "publicKey": - return proxifyPubKey(target.publicKey as TPubKey); - } + TProxPrivKey extends ProxiedPrivCryptoKey, + TPubKey extends CryptoKey, + TProxPubKey extends ProxiedPubCryptoKey +>({ privHandler, pubHandler }: ProxyKeyPairHandlers) { + return function _proxifyKeyPair(keyPair: TKeyPair) { + return new Proxy< + TKeyPair, + ProxiedCryptoKeyPair< + TKeyPair, + TPrivKey, + TProxPrivKey, + TPubKey, + TProxPubKey + > + >(keyPair, { + get(target: TKeyPair, prop: string) { + switch (prop) { + case "self": + return target; + case "privateKey": + return proxifyPrivKey( + privHandler + )(target.privateKey as TPrivKey); + case "publicKey": + return proxifyPubKey(pubHandler)( + target.publicKey as TPubKey + ); + } - return Reflect.get(target, prop); - }, - }); + return Reflect.get(target, prop); + }, + }); + }; } From 8aaa8487edcdef4ee645cf305de85b7a2fc9e4b2 Mon Sep 17 00:00:00 2001 From: Neal Fennimore Date: Mon, 29 May 2023 16:43:14 -0400 Subject: [PATCH 04/20] Add in node version 20 to tests --- .github/workflows/test.yml | 2 +- .prettierignore | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 .prettierignore diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a89280f..6a470cf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: - node-version: [16.x, 18.x] + node-version: [16.x, 18.x, 20.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..ad30bfe --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +*.yml \ No newline at end of file From 347e19b15ec7532ed086a4302cfcdefc45417b7e Mon Sep 17 00:00:00 2001 From: Neal Fennimore Date: Mon, 29 May 2023 16:44:39 -0400 Subject: [PATCH 05/20] Update snapshot --- src/hmac/__tests__/__snapshots__/hmac.test.ts.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hmac/__tests__/__snapshots__/hmac.test.ts.snap b/src/hmac/__tests__/__snapshots__/hmac.test.ts.snap index 74ade7a..e193c41 100644 --- a/src/hmac/__tests__/__snapshots__/hmac.test.ts.snap +++ b/src/hmac/__tests__/__snapshots__/hmac.test.ts.snap @@ -9,7 +9,7 @@ CryptoKey { "hash": Object { "name": "SHA-512", }, - "length": 512, + "length": 1024, "name": "HMAC", }, Symbol(kExtractable): true, From 0b3a1c7099850c1d52e0181bebce84489e9f6949 Mon Sep 17 00:00:00 2001 From: Neal Fennimore Date: Mon, 29 May 2023 17:07:18 -0400 Subject: [PATCH 06/20] Update ECDH to be proxied --- .../__tests__/__snapshots__/ecdh.test.ts.snap | 54 +++++- src/ec/__tests__/ecdh.test.ts | 167 ++++++++++++------ src/ec/ecdh.ts | 151 ++++++++++++++-- src/ec/shared.ts | 32 ++++ 4 files changed, 338 insertions(+), 66 deletions(-) diff --git a/src/ec/__tests__/__snapshots__/ecdh.test.ts.snap b/src/ec/__tests__/__snapshots__/ecdh.test.ts.snap index 612925d..de3aee5 100644 --- a/src/ec/__tests__/__snapshots__/ecdh.test.ts.snap +++ b/src/ec/__tests__/__snapshots__/ecdh.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ECDH should derive keys 1`] = ` +exports[`ECDH Non-proxied should derive keys 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -20,7 +20,57 @@ CryptoKey { } `; -exports[`ECDH should generate key 1`] = ` +exports[`ECDH Non-proxied should generate key 1`] = ` +Object { + "privateKey": CryptoKey { + Symbol(kKeyObject): PrivateKeyObject { + Symbol(kKeyType): "private", + }, + Symbol(kAlgorithm): Object { + "name": "ECDH", + "namedCurve": "P-521", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "deriveKey", + "deriveBits", + ], + }, + "publicKey": CryptoKey { + Symbol(kKeyObject): PublicKeyObject { + Symbol(kKeyType): "public", + }, + Symbol(kAlgorithm): Object { + "name": "ECDH", + "namedCurve": "P-521", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [], + }, +} +`; + +exports[`ECDH Proxied should derive keys 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "hash": Object { + "name": "SHA-512", + }, + "length": 512, + "name": "HMAC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "sign", + "verify", + ], +} +`; + +exports[`ECDH Proxied should generate key 1`] = ` Object { "privateKey": CryptoKey { Symbol(kKeyObject): PrivateKeyObject { diff --git a/src/ec/__tests__/ecdh.test.ts b/src/ec/__tests__/ecdh.test.ts index 519806f..31c8ed9 100644 --- a/src/ec/__tests__/ecdh.test.ts +++ b/src/ec/__tests__/ecdh.test.ts @@ -2,67 +2,132 @@ import * as Authentication from "../../hmac/index.js"; import * as params from "../../params.js"; import * as SHA from "../../sha/index.js"; import * as EC from "../index.js"; -import { EcdhCryptoKeyPair } from "../shared.js"; +import { EcdhCryptoKeyPair, EcdhProxiedCryptoKeyPair } from "../shared.js"; const { ECDH } = EC; describe("ECDH", () => { - let keyPair: EcdhCryptoKeyPair; - beforeEach(async () => { - keyPair = await ECDH.generateKey(); - }); - it("should generate key", async () => { - expect(keyPair).toMatchSnapshot(); - }); - it("should import and export key", async () => { - let jwk = await ECDH.exportKey("jwk", keyPair.publicKey); - const importedPubKey = await ECDH.importKey( - "jwk", - jwk, - { namedCurve: "P-521" }, - true, - [] - ); + describe("Non-proxied", () => { + let proxiedKeyPair: EcdhProxiedCryptoKeyPair; + let keyPair: EcdhCryptoKeyPair; + beforeEach(async () => { + proxiedKeyPair = await ECDH.generateKey(); + keyPair = proxiedKeyPair.self; + }); + it("should generate key", async () => { + expect(keyPair).toMatchSnapshot(); + }); + it("should import and export key", async () => { + let jwk = await ECDH.exportKey("jwk", keyPair.publicKey); + const importedPubKey = await ECDH.importKey( + "jwk", + jwk, + { namedCurve: "P-521" }, + true, + [] + ); - expect(await ECDH.exportKey("jwk", importedPubKey)).toEqual(jwk); + expect(await ECDH.exportKey("jwk", importedPubKey.self)).toEqual( + jwk + ); - jwk = await ECDH.exportKey("jwk", keyPair.privateKey); - const importedPrivKey = await ECDH.importKey("jwk", jwk, { - namedCurve: "P-521", - }); + jwk = await ECDH.exportKey("jwk", keyPair.privateKey); + const importedPrivKey = await ECDH.importKey("jwk", jwk, { + namedCurve: "P-521", + }); - expect(await ECDH.exportKey("jwk", importedPrivKey)).toEqual(jwk); - }); - it("should derive bits", async () => { - const otherKeyPair = await ECDH.generateKey(); + expect(await ECDH.exportKey("jwk", importedPrivKey.self)).toEqual( + jwk + ); + }); + it("should derive bits", async () => { + const otherKeyPair = await ECDH.generateKey(); - const bits = await ECDH.deriveBits( - { public: otherKeyPair.publicKey }, - keyPair.privateKey, - 128 - ); - expect(bits.byteLength).toEqual(16); + const bits = await ECDH.deriveBits( + { public: otherKeyPair.publicKey.self }, + keyPair.privateKey, + 128 + ); + expect(bits.byteLength).toEqual(16); - await expect( - ECDH.deriveBits( - { public: otherKeyPair.publicKey }, + await expect( + ECDH.deriveBits( + { public: otherKeyPair.publicKey.self }, + keyPair.privateKey, + 127 + ) + ).rejects.toThrowError(RangeError); + }); + it("should derive keys", async () => { + const otherKeyPair = await ECDH.generateKey(); + const hmacParams: params.EnforcedHmacKeyGenParams = { + name: Authentication.Alg.Code.HMAC, + hash: SHA.Alg.Variant.SHA_512, + length: 512, + }; + let key = await ECDH.deriveKey( + { public: otherKeyPair.publicKey.self }, keyPair.privateKey, - 127 - ) - ).rejects.toThrowError(RangeError); + hmacParams + ); + expect(key).toMatchSnapshot(); + }); }); - it("should derive keys", async () => { - const otherKeyPair = await ECDH.generateKey(); - const hmacParams: params.EnforcedHmacKeyGenParams = { - name: Authentication.Alg.Code.HMAC, - hash: SHA.Alg.Variant.SHA_512, - length: 512, - }; - let key = await ECDH.deriveKey( - { public: otherKeyPair.publicKey }, - keyPair.privateKey, - hmacParams - ); - expect(key).toMatchSnapshot(); + describe("Proxied", () => { + let keyPair: EcdhProxiedCryptoKeyPair; + beforeEach(async () => { + keyPair = await ECDH.generateKey(); + }); + it("should generate key", async () => { + expect(keyPair).toMatchSnapshot(); + }); + it("should import and export key", async () => { + let jwk = await keyPair.publicKey.exportKey("jwk"); + const importedPubKey = await ECDH.importKey( + "jwk", + jwk, + { namedCurve: "P-521" }, + true, + [] + ); + + expect(await importedPubKey.exportKey("jwk")).toEqual(jwk); + + jwk = await keyPair.privateKey.exportKey("jwk"); + const importedPrivKey = await ECDH.importKey("jwk", jwk, { + namedCurve: "P-521", + }); + + expect(await importedPrivKey.exportKey("jwk")).toEqual(jwk); + }); + it("should derive bits", async () => { + const otherKeyPair = await ECDH.generateKey(); + + const bits = await keyPair.privateKey.deriveBits( + { public: otherKeyPair.publicKey.self }, + 128 + ); + expect(bits.byteLength).toEqual(16); + + await expect( + keyPair.privateKey.deriveBits( + { public: otherKeyPair.publicKey.self }, + 127 + ) + ).rejects.toThrowError(RangeError); + }); + it("should derive keys", async () => { + const otherKeyPair = await ECDH.generateKey(); + const hmacParams: params.EnforcedHmacKeyGenParams = { + name: Authentication.Alg.Code.HMAC, + hash: SHA.Alg.Variant.SHA_512, + length: 512, + }; + let key = await keyPair.privateKey.deriveKey( + { public: otherKeyPair.publicKey.self }, + hmacParams + ); + expect(key).toMatchSnapshot(); + }); }); }); diff --git a/src/ec/ecdh.ts b/src/ec/ecdh.ts index 479c40f..f6a7c77 100644 --- a/src/ec/ecdh.ts +++ b/src/ec/ecdh.ts @@ -6,21 +6,89 @@ import { AesCryptoKeys } from "../aes/shared.js"; import { HmacCryptoKey } from "../hmac/index.js"; import { getKeyUsagePairsByAlg } from "../key_usages.js"; import * as params from "../params.js"; +import * as proxy from "../proxy.js"; import * as WebCrypto from "../webcrypto.js"; import { Alg, + EcShared, EcdhCryptoKeyPair, EcdhPrivCryptoKey, + EcdhProxiedCryptoKeyPair, + EcdhProxiedPrivCryptoKey, + EcdhProxiedPubCryptoKey, EcdhPubCryptoKey, - EcShared, } from "./shared.js"; +const handlers: proxy.ProxyKeyPairHandlers< + EcdhPrivCryptoKey, + EcdhPubCryptoKey +> = { + privHandler: { + get(target: EcdhPrivCryptoKey, prop: string) { + switch (prop) { + case "self": + return target; + case "deriveKey": + return ( + algorithm: Omit< + params.EnforcedEcdhKeyDeriveParams, + "name" + >, + derivedKeyType: + | params.EnforcedAesKeyGenParams + | params.EnforcedHmacKeyGenParams, + extractable: boolean, + keyUsages?: KeyUsage[] + ) => + deriveKey( + algorithm, + target, + derivedKeyType, + extractable, + keyUsages + ); + case "deriveBits": + return ( + algorithm: Omit< + params.EnforcedEcdhKeyDeriveParams, + "name" + >, + length: number + ) => deriveBits(algorithm, target, length); + case "exportKey": + return (format: KeyFormat) => exportKey(format, target); + } + + return Reflect.get(target, prop); + }, + }, + pubHandler: { + get(target: EcdhPubCryptoKey, prop: string) { + switch (prop) { + case "self": + return target; + case "exportKey": + return (format: KeyFormat) => exportKey(format, target); + } + + return Reflect.get(target, prop); + }, + }, +}; + /** * Generate a new ECDH keypair * @example * ```ts * const keyPair = await ECDH.generateKey(); * ``` + * @example + * ```ts + * const keyPair = await ECDH.generateKey({ namedCurve: "P-256" }, false); + * ``` + * @example + * ```ts + * const keyPair = await ECDH.generateKey({ namedCurve: "P-256" }, true, ['deriveKey', 'deriveBits']); */ export const generateKey = async ( algorithm: Omit = { @@ -28,18 +96,30 @@ export const generateKey = async ( }, extractable?: boolean, keyUsages?: KeyUsage[] -): Promise => - await EcShared.generateKey( +): Promise => { + const keyPair = (await EcShared.generateKey( { ...algorithm, name: Alg.Variant.ECDH }, extractable, keyUsages - ); + )) as EcdhCryptoKeyPair; + return proxy.proxifyKeyPair< + EcdhCryptoKeyPair, + EcdhPrivCryptoKey, + EcdhProxiedPrivCryptoKey, + EcdhPubCryptoKey, + EcdhProxiedPubCryptoKey + >(handlers)(keyPair); +}; /** * Import an ECDH public or private key * @example * ```ts - * const key = await ECDH.importKey("jwk", pubKey, { namedCurve: "P-521" }, true, ['deriveKey', 'deriveBits']); + * const pubKey = await ECDH.importKey("jwk", pubKeyJwk, { namedCurve: "P-521" }, true, []); + * ``` + * @example + * ```ts + * const privKey = await ECDH.importKey("jwk", privKeyJwk, { namedCurve: "P-521" }, true, ['deriveBits', 'deriveKey']); * ``` */ export const importKey = async ( @@ -50,20 +130,42 @@ export const importKey = async ( }, extractable?: boolean, keyUsages?: KeyUsage[] -): Promise => - await EcShared.importKey( +): Promise => { + const importedKey = await EcShared.importKey( format, key, { ...algorithm, name: Alg.Variant.ECDH }, extractable, keyUsages ); - + if (importedKey.type === "private") { + return proxy.proxifyPrivKey< + EcdhPrivCryptoKey, + EcdhProxiedPrivCryptoKey + >(handlers.privHandler)(importedKey as EcdhPrivCryptoKey); + } else { + return proxy.proxifyPubKey( + handlers.pubHandler + )(importedKey as EcdhPubCryptoKey); + } +}; /** * Export an ECDH public or private key * @example * ```ts - * const pubKeyJwk = await ECDH.importKey("jwk", keyPair.publicKey); + * const pubKeyJwk = await ECDH.exportKey("jwk", keyPair.publicKey.self); + * ``` + * @example + * ```ts + * const privKeyJwk = await ECDH.exportKey("jwk", keyPair.privateKey.self); + * ``` + * @example + * ```ts + * const pubKeyJwk = await keyPair.publicKey.exportKey("jwk"); + * ``` + * @example + * ```ts + * const privKeyJwk = await keyPair.privateKey.exportKey("jwk"); * ``` */ export const exportKey = async ( @@ -83,8 +185,22 @@ export const exportKey = async ( * length: 512, * }; * let key = await ECDH.deriveKey( - * { public: otherKeyPair.publicKey }, - * keyPair.privateKey, + * { public: otherKeyPair.publicKey.self }, + * keyPair.privateKey.self, + * hmacParams + * ); + * ``` + * @example + * ```ts + * const keyPair = await ECDH.generateKey(); + * const otherKeyPair = await ECDH.generateKey(); + * const hmacParams: params.EnforcedHmacKeyGenParams = { + * name: Authentication.Alg.Code.HMAC, + * hash: SHA.Alg.Variant.SHA_512, + * length: 512, + * }; + * let key = await keyPair.privateKey.deriveKey( + * { public: otherKeyPair.publicKey.self }, * hmacParams * ); * ``` @@ -120,8 +236,17 @@ export async function deriveKey( * const keyPair = await ECDH.generateKey(); * const otherKeyPair = await ECDH.generateKey(); * const bits = await ECDH.deriveBits( - * { public: otherKeyPair.publicKey }, - * keyPair.privateKey, + * { public: otherKeyPair.publicKey.self }, + * keyPair.privateKey.self, + * 128 + * ); + * ``` + * @example + * ```ts + * const keyPair = await ECDH.generateKey(); + * const otherKeyPair = await ECDH.generateKey(); + * const bits = await keyPair.privateKey.deriveBits( + * { public: otherKeyPair.publicKey.self }, * 128 * ); * ``` diff --git a/src/ec/shared.ts b/src/ec/shared.ts index 3fce08a..bb04aee 100644 --- a/src/ec/shared.ts +++ b/src/ec/shared.ts @@ -2,6 +2,8 @@ * Shared code for EC * @module */ +import { AesCryptoKeys } from "../aes/shared.js"; +import { HmacCryptoKey } from "../hmac/index.js"; import { getKeyUsagePairsByAlg } from "../key_usages.js"; import * as params from "../params.js"; import * as proxy from "../proxy.js"; @@ -58,6 +60,36 @@ export interface EcdsaProxiedCryptoKeyPair EcdsaPubCryptoKey, EcdsaProxiedPubCryptoKey > {} +export interface EcdhProxiedPubCryptoKey + extends proxy.ProxiedPubCryptoKey { + exportKey: (format: KeyFormat) => Promise; +} +export interface EcdhProxiedPrivCryptoKey + extends proxy.ProxiedPrivCryptoKey { + deriveKey: ( + algorithm: Omit, + derivedKeyType: + | params.EnforcedAesKeyGenParams + | params.EnforcedHmacKeyGenParams, + extractable?: boolean, + keyUsages?: KeyUsage[] + ) => Promise; + deriveBits: ( + algorithm: Omit, + length: number + ) => Promise; + + exportKey: (format: KeyFormat) => Promise; +} + +export interface EcdhProxiedCryptoKeyPair + extends proxy.ProxiedCryptoKeyPair< + EcdhCryptoKeyPair, + EcdhPrivCryptoKey, + EcdhProxiedPrivCryptoKey, + EcdhPubCryptoKey, + EcdhProxiedPubCryptoKey + > {} export type EcCryptoKeys = | EcdhPubCryptoKey From 5fb5628e8deccf0f20a62256048ba553efc3d5da Mon Sep 17 00:00:00 2001 From: Neal Fennimore Date: Mon, 29 May 2023 17:14:38 -0400 Subject: [PATCH 07/20] Remove fail-fast --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6a470cf..2d09bf5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,8 +9,8 @@ on: jobs: build: runs-on: ubuntu-latest - strategy: + fail-fast: false matrix: node-version: [16.x, 18.x, 20.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ From 3e172a3ace8b8819392f6a57fe92a7be5ef2d53b Mon Sep 17 00:00:00 2001 From: Neal Fennimore Date: Mon, 29 May 2023 17:49:57 -0400 Subject: [PATCH 08/20] Update proxy signatures --- src/proxy.ts | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/proxy.ts b/src/proxy.ts index 9344ae7..a373f0a 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -3,13 +3,14 @@ * @module */ -export interface ProxiedPubCryptoKey { +export interface ProxiedCryptoKey { self: T; } +export interface ProxiedPubCryptoKey + extends ProxiedCryptoKey {} -export interface ProxiedPrivCryptoKey { - self: T; -} +export interface ProxiedPrivCryptoKey + extends ProxiedCryptoKey {} export interface ProxiedCryptoKeyPair< TKeyPair extends CryptoKeyPair, @@ -22,23 +23,17 @@ export interface ProxiedCryptoKeyPair< privateKey: TProxPrivKey; publicKey: TProxPubKey; } -export function proxifyPubKey< - TPubKey extends CryptoKey, - TProxPubKey extends ProxiedPubCryptoKey ->(handler: ProxyHandler) { - return function _proxifyPubKey(privKey: TPubKey) { - return new Proxy(privKey, handler); +export function proxifyKey< + TKey extends CryptoKey, + TProxKey extends ProxiedCryptoKey +>(handler: ProxyHandler) { + return function _proxifyKey(key: TKey) { + return new Proxy(key, handler); }; } -export function proxifyPrivKey< - TPrivKey extends CryptoKey, - TProxPrivKey extends ProxiedPrivCryptoKey ->(handler: ProxyHandler) { - return function _proxifyPrivKey(privKey: TPrivKey) { - return new Proxy(privKey, handler); - }; -} +export const proxifyPubKey = proxifyKey; +export const proxifyPrivKey = proxifyKey; export interface ProxyKeyPairHandlers< TPrivKey extends CryptoKey, From fa59a31faa8585a6e720f4a335100e034c4022a2 Mon Sep 17 00:00:00 2001 From: Neal Fennimore Date: Mon, 29 May 2023 17:50:22 -0400 Subject: [PATCH 09/20] Update HMAC to use proxy --- .../__tests__/__snapshots__/hmac.test.ts.snap | 40 +++++++++++ src/hmac/__tests__/hmac.test.ts | 65 +++++++++++++----- src/hmac/index.ts | 67 +++++++++++++++++-- 3 files changed, 150 insertions(+), 22 deletions(-) diff --git a/src/hmac/__tests__/__snapshots__/hmac.test.ts.snap b/src/hmac/__tests__/__snapshots__/hmac.test.ts.snap index e193c41..2ab7e0f 100644 --- a/src/hmac/__tests__/__snapshots__/hmac.test.ts.snap +++ b/src/hmac/__tests__/__snapshots__/hmac.test.ts.snap @@ -1,5 +1,45 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`HMAC Original should generate key 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "hash": Object { + "name": "SHA-512", + }, + "length": 1024, + "name": "HMAC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "sign", + "verify", + ], +} +`; + +exports[`HMAC Proxied should generate key 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "hash": Object { + "name": "SHA-512", + }, + "length": 1024, + "name": "HMAC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "sign", + "verify", + ], +} +`; + exports[`HMAC should generate key 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { diff --git a/src/hmac/__tests__/hmac.test.ts b/src/hmac/__tests__/hmac.test.ts index aae6613..de2b966 100644 --- a/src/hmac/__tests__/hmac.test.ts +++ b/src/hmac/__tests__/hmac.test.ts @@ -1,25 +1,58 @@ import * as HMAC from "../index.js"; describe("HMAC", () => { - let key: HMAC.HmacCryptoKey; - beforeEach(async () => { - key = await HMAC.generateKey(); - }); - it("should generate key", async () => { - expect(key).toMatchSnapshot(); - }); - it("should import and export key", async () => { - let jwk = await HMAC.exportKey("jwk", key); - const importedPubKey = await HMAC.importKey("jwk", jwk, { - hash: "SHA-512", + describe("Original", () => { + let proxiedKey: HMAC.HmacProxiedCryptoKey; + let key: HMAC.HmacCryptoKey; + beforeEach(async () => { + // @ts-ignore + proxiedKey = await HMAC.generateKey(); + key = proxiedKey.self; + }); + it("should generate key", async () => { + expect(key).toMatchSnapshot(); }); + it("should import and export key", async () => { + let jwk = await HMAC.exportKey("jwk", key); + const importedPubKey = await HMAC.importKey("jwk", jwk, { + hash: "SHA-512", + }); - expect(await HMAC.exportKey("jwk", importedPubKey)).toEqual(jwk); + expect(await HMAC.exportKey("jwk", importedPubKey.self)).toEqual( + jwk + ); + }); + it("should sign and verify", async () => { + const text = encode("a message"); + const signature = await HMAC.sign(key, text); + + expect(await HMAC.verify(key, signature, text)).toBe(true); + }); }); - it("should sign and verify", async () => { - const text = encode("a message"); - const signature = await HMAC.sign(key, text); + describe("Proxied", () => { + let key: HMAC.HmacProxiedCryptoKey; + beforeEach(async () => { + // @ts-ignore + key = await HMAC.generateKey(); + }); + it("should generate key", async () => { + expect(key).toMatchSnapshot(); + }); + it("should import and export key", async () => { + let jwk = await key.exportKey("jwk"); + const importedPubKey = await HMAC.importKey("jwk", jwk, { + hash: "SHA-512", + }); - expect(await HMAC.verify(key, signature, text)).toBe(true); + expect(await HMAC.exportKey("jwk", importedPubKey.self)).toEqual( + jwk + ); + }); + it("should sign and verify", async () => { + const text = encode("a message"); + const signature = await key.sign(text); + + expect(await key.verify(signature, text)).toBe(true); + }); }); }); diff --git a/src/hmac/index.ts b/src/hmac/index.ts index 72f6a3d..d2ed231 100644 --- a/src/hmac/index.ts +++ b/src/hmac/index.ts @@ -4,11 +4,37 @@ */ import { getKeyUsagePairsByAlg } from "../key_usages.js"; import * as params from "../params.js"; +import * as proxy from "../proxy.js"; import { Alg as SHA } from "../sha/shared.js"; import * as WebCrypto from "../webcrypto.js"; + export interface HmacCryptoKey extends CryptoKey { _hmacKeyBrand: any; } +export interface HmacProxiedCryptoKey + extends proxy.ProxiedCryptoKey { + sign: (data: BufferSource) => Promise; + verify: (signature: BufferSource, data: BufferSource) => Promise; + exportKey: (format: KeyFormat) => Promise; +} + +const handler: ProxyHandler = { + get(target: HmacCryptoKey, prop: string) { + switch (prop) { + case "self": + return target; + case "sign": + return (data: BufferSource) => sign(target, data); + case "verify": + return (signature: BufferSource, data: BufferSource) => + verify(target, signature, data); + case "exportKey": + return (format: KeyFormat) => exportKey(format, target); + } + + return Reflect.get(target, prop); + }, +}; export namespace Alg { export enum Code { @@ -30,8 +56,11 @@ export const generateKey = async ( }, extractable: boolean = true, keyUsages?: KeyUsage[] -) => - await WebCrypto.generateKey( +): Promise> => { + const key = await WebCrypto.generateKey< + HmacCryptoKey, + params.EnforcedHmacKeyGenParams + >( { ...algorithm, name: Alg.Code.HMAC, @@ -39,6 +68,11 @@ export const generateKey = async ( extractable, keyUsages ?? getKeyUsagePairsByAlg(Alg.Code.HMAC) ); + return proxy.proxifyKey< + HmacCryptoKey, + proxy.ProxiedCryptoKey + >(handler)(key); +}; /** * Import an HMAC key from the specified format @@ -53,8 +87,11 @@ export const importKey = async ( algorithm: Omit, extractable: boolean = true, keyUsages?: KeyUsage[] -) => - await WebCrypto.importKey( +): Promise> => { + const importedKey = await WebCrypto.importKey< + HmacCryptoKey, + params.EnforcedHmacImportParams + >( format as any, key as any, { ...algorithm, name: Alg.Code.HMAC }, @@ -62,11 +99,21 @@ export const importKey = async ( keyUsages ?? getKeyUsagePairsByAlg(Alg.Code.HMAC) ); + return proxy.proxifyKey< + HmacCryptoKey, + proxy.ProxiedCryptoKey + >(handler)(importedKey); +}; + /** * Export an HMAC key into the specified format * @example * ```ts - * const jwk = await HMAC.exportKey("jwk", key); + * const jwk = await HMAC.exportKey("jwk", key.self); + * ``` + * @example + * ```ts + * const jwk = await key.exportKey("jwk"); * ``` */ export async function exportKey( @@ -81,7 +128,11 @@ export async function exportKey( * @example * ```ts * const message = new TextEncoder().encode("a message"); - * const signature = await HMAC.sign(key, message); + * const signature = await HMAC.sign(key.self, message); + * ``` + * ```ts + * const message = new TextEncoder().encode("a message"); + * const signature = await key.sign(message); * ``` */ export async function sign( @@ -103,6 +154,10 @@ export async function sign( * ```ts * const isVerified = await HMAC.verify(key, signature, message); * ``` + * @example + * ```ts + * const isVerified = await key.verify(signature, message); + * ``` */ export async function verify( key: HmacCryptoKey, From 8162e5465a27063264b7ec993af71d7641db6293 Mon Sep 17 00:00:00 2001 From: Neal Fennimore Date: Mon, 29 May 2023 19:15:09 -0400 Subject: [PATCH 10/20] Update AES to be proxied --- src/aes/__tests__/aes_cbc.test.ts | 197 +++++++++++++++++++--------- src/aes/__tests__/aes_ctr.test.ts | 207 +++++++++++++++++++++--------- src/aes/__tests__/aes_gcm.test.ts | 198 +++++++++++++++++++--------- src/aes/__tests__/aes_kw.test.ts | 77 +++++++---- src/aes/aes_cbc.ts | 159 +++++++++++++++++++++-- src/aes/aes_ctr.ts | 140 ++++++++++++++++++-- src/aes/aes_gcm.ts | 138 ++++++++++++++++++-- src/aes/aes_kw.ts | 83 +++++++++++- src/aes/shared.ts | 130 ++++++++++++++++++- 9 files changed, 1084 insertions(+), 245 deletions(-) diff --git a/src/aes/__tests__/aes_cbc.test.ts b/src/aes/__tests__/aes_cbc.test.ts index 75545de..a8a35da 100644 --- a/src/aes/__tests__/aes_cbc.test.ts +++ b/src/aes/__tests__/aes_cbc.test.ts @@ -1,76 +1,151 @@ import * as Random from "../../random.js"; +import { AesCbcProxiedCryptoKey } from "../aes_cbc.js"; import * as AES from "../index.js"; describe("AES_CBC", () => { - let iv: Uint8Array, key: AES.AesCbcCryptoKey; - const text = "brown fox fox fox fox fox fox fox fox fox"; - beforeEach(async () => { - iv = await Random.IV.generate(); - key = await AES.AES_CBC.generateKey(); - }); - it("should encrypt and decrypt", async () => { - const ciphertextBytes = await AES.AES_CBC.encrypt( - { iv }, - key, - new TextEncoder().encode(text) - ); - const plaintextBytes = await AES.AES_CBC.decrypt( - { iv }, - key, - ciphertextBytes - ); - expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); - }); - it("should import and export keys", async () => { - const ciphertextBytes = await AES.AES_CBC.encrypt( - { iv }, - key, - new TextEncoder().encode(text) - ); + describe("Original", () => { + let iv: Uint8Array, + proxiedKey: AesCbcProxiedCryptoKey, + key: AES.AesCbcCryptoKey; + const text = "brown fox fox fox fox fox fox fox fox fox"; + beforeEach(async () => { + iv = await Random.IV.generate(); + proxiedKey = await AES.AES_CBC.generateKey(); + key = proxiedKey.self; + }); + it("should encrypt and decrypt", async () => { + const ciphertextBytes = await AES.AES_CBC.encrypt( + { iv }, + key, + new TextEncoder().encode(text) + ); + const plaintextBytes = await AES.AES_CBC.decrypt( + { iv }, + key, + ciphertextBytes + ); + expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); + }); + it("should import and export keys", async () => { + const ciphertextBytes = await AES.AES_CBC.encrypt( + { iv }, + key, + new TextEncoder().encode(text) + ); + + const jwk = await AES.AES_CBC.exportKey("jwk", key); + const importedKey = await AES.AES_CBC.importKey("jwk", jwk, { + length: 256, + }); - const jwk = await AES.AES_CBC.exportKey("jwk", key); - const importedKey = await AES.AES_CBC.importKey("jwk", jwk, { - length: 256, + const plaintextBytes = await AES.AES_CBC.decrypt( + { iv }, + importedKey.self, + ciphertextBytes + ); + expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); }); + it("should wrap and unwrap keys", async () => { + const kek = await AES.AES_CBC.generateKey({ length: 256 }, true, [ + "wrapKey", + "unwrapKey", + ]); + const dek = await AES.AES_CBC.generateKey({ + length: 256, + }); + + const ciphertextBytes = await AES.AES_CBC.encrypt( + { iv }, + dek.self, + new TextEncoder().encode(text) + ); - const plaintextBytes = await AES.AES_CBC.decrypt( - { iv }, - importedKey, - ciphertextBytes - ); - expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); + const wrappedKey = await AES.AES_CBC.wrapKey( + "raw", + dek.self, + kek.self, + { + iv, + } + ); + const unwrappedkey = (await AES.AES_CBC.unwrapKey( + "raw", + wrappedKey, + { name: AES.Alg.Mode.AES_CBC }, + kek.self, + { iv } + )) as AES.AesCbcCryptoKey; + + const plaintextBytes = await AES.AES_CBC.decrypt( + { iv }, + unwrappedkey, + ciphertextBytes + ); + expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); + }); }); - it("should wrap and unwrap keys", async () => { - const kek = await AES.AES_CBC.generateKey({ length: 256 }, true, [ - "wrapKey", - "unwrapKey", - ]); - const dek: AES.AesCbcCryptoKey = await AES.AES_CBC.generateKey({ - length: 256, + describe("Proxied", () => { + let iv: Uint8Array, key: AesCbcProxiedCryptoKey; + const text = "brown fox fox fox fox fox fox fox fox fox"; + beforeEach(async () => { + iv = await Random.IV.generate(); + key = await AES.AES_CBC.generateKey(); + }); + it("should encrypt and decrypt", async () => { + const ciphertextBytes = await key.encrypt( + { iv }, + new TextEncoder().encode(text) + ); + const plaintextBytes = await key.decrypt({ iv }, ciphertextBytes); + expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); }); + it("should import and export keys", async () => { + const ciphertextBytes = await key.encrypt( + { iv }, + new TextEncoder().encode(text) + ); - const ciphertextBytes = await AES.AES_CBC.encrypt( - { iv }, - dek, - new TextEncoder().encode(text) - ); + const jwk = await key.exportKey("jwk"); + const importedKey = await AES.AES_CBC.importKey("jwk", jwk, { + length: 256, + }); - const wrappedKey = await AES.AES_CBC.wrapKey("raw", dek, kek, { - iv, + const plaintextBytes = await importedKey.decrypt( + { iv }, + ciphertextBytes + ); + expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); }); - const unwrappedkey = (await AES.AES_CBC.unwrapKey( - "raw", - wrappedKey, - { name: AES.Alg.Mode.AES_CBC }, - kek, - { iv } - )) as AES.AesCbcCryptoKey; + it("should wrap and unwrap keys", async () => { + const kek = await AES.AES_CBC.generateKey({ length: 256 }, true, [ + "wrapKey", + "unwrapKey", + ]); + const dek = await AES.AES_CBC.generateKey({ + length: 256, + }); - const plaintextBytes = await AES.AES_CBC.decrypt( - { iv }, - unwrappedkey, - ciphertextBytes - ); - expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); + const ciphertextBytes = await dek.encrypt( + { iv }, + new TextEncoder().encode(text) + ); + + const wrappedKey = await kek.wrapKey("raw", dek.self, { + iv, + }); + const unwrappedkey = (await kek.unwrapKey( + "raw", + wrappedKey, + { name: AES.Alg.Mode.AES_CBC }, + { iv } + )) as AES.AesCbcCryptoKey; + + const plaintextBytes = await AES.AES_CBC.decrypt( + { iv }, + unwrappedkey, + ciphertextBytes + ); + expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); + }); }); }); diff --git a/src/aes/__tests__/aes_ctr.test.ts b/src/aes/__tests__/aes_ctr.test.ts index 6e6c08c..1381480 100644 --- a/src/aes/__tests__/aes_ctr.test.ts +++ b/src/aes/__tests__/aes_ctr.test.ts @@ -1,78 +1,159 @@ import * as Random from "../../random.js"; +import { AesCtrProxiedCryptoKey } from "../aes_ctr.js"; import * as AES from "../index.js"; describe("AES_CTR", () => { - let iv: Uint8Array, counter: Uint8Array, key: AES.AesCtrCryptoKey; - const text = "brown fox fox fox fox fox fox fox fox fox"; - beforeEach(async () => { - iv = await Random.IV.generate(); - counter = await AES.AES_CTR.generateCounter(); - key = await AES.AES_CTR.generateKey(); - }); - it("should encrypt and decrypt", async () => { - const ciphertextBytes = await AES.AES_CTR.encrypt( - { counter, length: 8 }, - key, - new TextEncoder().encode(text) - ); - const plaintextBytes = await AES.AES_CTR.decrypt( - { counter, length: 8 }, - key, - ciphertextBytes - ); - expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); - }); - it("should import and export keys", async () => { - const ciphertextBytes = await AES.AES_CTR.encrypt( - { counter, length: 8 }, - key, - new TextEncoder().encode(text) - ); + describe("Original", () => { + let iv: Uint8Array, + counter: Uint8Array, + proxiedKey: AesCtrProxiedCryptoKey, + key: AES.AesCtrCryptoKey; + const text = "brown fox fox fox fox fox fox fox fox fox"; + beforeEach(async () => { + iv = await Random.IV.generate(); + counter = await AES.AES_CTR.generateCounter(); + proxiedKey = await AES.AES_CTR.generateKey(); + key = proxiedKey.self; + }); + it("should encrypt and decrypt", async () => { + const ciphertextBytes = await AES.AES_CTR.encrypt( + { counter, length: 8 }, + key, + new TextEncoder().encode(text) + ); + const plaintextBytes = await AES.AES_CTR.decrypt( + { counter, length: 8 }, + key, + ciphertextBytes + ); + expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); + }); + it("should import and export keys", async () => { + const ciphertextBytes = await AES.AES_CTR.encrypt( + { counter, length: 8 }, + key, + new TextEncoder().encode(text) + ); + + const jwk = await AES.AES_CTR.exportKey("jwk", key); + const importedKey = await AES.AES_CTR.importKey("jwk", jwk, { + length: 256, + }); - const jwk = await AES.AES_CTR.exportKey("jwk", key); - const importedKey = await AES.AES_CTR.importKey("jwk", jwk, { - length: 256, + const plaintextBytes = await AES.AES_CTR.decrypt( + { counter, length: 8 }, + importedKey.self, + ciphertextBytes + ); + expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); }); + it("should wrap and unwrap keys", async () => { + const kek = await AES.AES_CTR.generateKey({ length: 256 }, true, [ + "wrapKey", + "unwrapKey", + ]); + const dek = await AES.AES_CTR.generateKey({ + length: 256, + }); + + const ciphertextBytes = await AES.AES_CTR.encrypt( + { counter, length: 8 }, + dek.self, + new TextEncoder().encode(text) + ); - const plaintextBytes = await AES.AES_CTR.decrypt( - { counter, length: 8 }, - importedKey, - ciphertextBytes - ); - expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); + const wrappedKey = await AES.AES_CTR.wrapKey( + "raw", + dek.self, + kek.self, + { + counter, + length: 8, + } + ); + const unwrappedKey = (await AES.AES_CTR.unwrapKey( + "raw", + wrappedKey, + { name: AES.Alg.Mode.AES_CTR }, + kek.self, + { counter, length: 8 } + )) as AES.AesCtrCryptoKey; + + const plaintextBytes = await AES.AES_CTR.decrypt( + { counter, length: 8 }, + unwrappedKey, + ciphertextBytes + ); + expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); + }); }); - it("should wrap and unwrap keys", async () => { - const kek = await AES.AES_CTR.generateKey({ length: 256 }, true, [ - "wrapKey", - "unwrapKey", - ]); - const dek: AES.AesCtrCryptoKey = await AES.AES_CTR.generateKey({ - length: 256, + describe("Proxied", () => { + let iv: Uint8Array, counter: Uint8Array, key: AesCtrProxiedCryptoKey; + const text = "brown fox fox fox fox fox fox fox fox fox"; + beforeEach(async () => { + iv = await Random.IV.generate(); + counter = await AES.AES_CTR.generateCounter(); + key = await AES.AES_CTR.generateKey(); + }); + it("should encrypt and decrypt", async () => { + const ciphertextBytes = await key.encrypt( + { counter, length: 8 }, + new TextEncoder().encode(text) + ); + const plaintextBytes = await key.decrypt( + { counter, length: 8 }, + ciphertextBytes + ); + expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); }); + it("should import and export keys", async () => { + const ciphertextBytes = await key.encrypt( + { counter, length: 8 }, + new TextEncoder().encode(text) + ); - const ciphertextBytes = await AES.AES_CTR.encrypt( - { counter, length: 8 }, - dek, - new TextEncoder().encode(text) - ); + const jwk = await key.exportKey("jwk"); + const importedKey = await AES.AES_CTR.importKey("jwk", jwk, { + length: 256, + }); - const wrappedKey = await AES.AES_CTR.wrapKey("raw", dek, kek, { - counter, - length: 8, + const plaintextBytes = await importedKey.decrypt( + { counter, length: 8 }, + ciphertextBytes + ); + expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); }); - const unwrappedkey = (await AES.AES_CTR.unwrapKey( - "raw", - wrappedKey, - { name: AES.Alg.Mode.AES_CTR }, - kek, - { counter, length: 8 } - )) as AES.AesCtrCryptoKey; + it("should wrap and unwrap keys", async () => { + const kek = await AES.AES_CTR.generateKey({ length: 256 }, true, [ + "wrapKey", + "unwrapKey", + ]); + const dek = await AES.AES_CTR.generateKey({ + length: 256, + }); - const plaintextBytes = await AES.AES_CTR.decrypt( - { counter, length: 8 }, - unwrappedkey, - ciphertextBytes - ); - expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); + const ciphertextBytes = await dek.encrypt( + { counter, length: 8 }, + new TextEncoder().encode(text) + ); + + const wrappedKey = await kek.wrapKey("raw", dek.self, { + counter, + length: 8, + }); + const unwrappedKey = (await kek.unwrapKey( + "raw", + wrappedKey, + { name: AES.Alg.Mode.AES_CTR }, + { counter, length: 8 } + )) as AES.AesCtrCryptoKey; + + const plaintextBytes = await AES.AES_CTR.decrypt( + { counter, length: 8 }, + unwrappedKey, + ciphertextBytes + ); + expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); + }); }); }); diff --git a/src/aes/__tests__/aes_gcm.test.ts b/src/aes/__tests__/aes_gcm.test.ts index 275483b..ffd2456 100644 --- a/src/aes/__tests__/aes_gcm.test.ts +++ b/src/aes/__tests__/aes_gcm.test.ts @@ -1,76 +1,152 @@ import * as Random from "../../random.js"; +import { AesGcmProxiedCryptoKey } from "../aes_gcm.js"; import * as AES from "../index.js"; describe("AES_GCM", () => { - let iv: Uint8Array, key: AES.AesGcmCryptoKey; - const text = "brown fox fox fox fox fox fox fox fox fox"; - beforeEach(async () => { - iv = await Random.IV.generate(); - key = await AES.AES_GCM.generateKey({ length: 256 }); - }); - it("should encrypt and decrypt", async () => { - const ciphertextBytes = await AES.AES_GCM.encrypt( - { iv }, - key, - new TextEncoder().encode(text) - ); - const plaintextBytes = await AES.AES_GCM.decrypt( - { iv }, - key, - ciphertextBytes - ); - expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); - }); - it("should import and export keys", async () => { - const ciphertextBytes = await AES.AES_GCM.encrypt( - { iv }, - key, - new TextEncoder().encode(text) - ); + describe("Original", () => { + let iv: Uint8Array, + proxiedKey: AesGcmProxiedCryptoKey, + key: AES.AesGcmCryptoKey; + const text = "brown fox fox fox fox fox fox fox fox fox"; + beforeEach(async () => { + iv = await Random.IV.generate(); + proxiedKey = await AES.AES_GCM.generateKey({ length: 256 }); + key = proxiedKey.self; + }); + it("should encrypt and decrypt", async () => { + const ciphertextBytes = await AES.AES_GCM.encrypt( + { iv }, + key, + new TextEncoder().encode(text) + ); + const plaintextBytes = await AES.AES_GCM.decrypt( + { iv }, + key, + ciphertextBytes + ); + expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); + }); + it("should import and export keys", async () => { + const ciphertextBytes = await AES.AES_GCM.encrypt( + { iv }, + key, + new TextEncoder().encode(text) + ); + + const jwk = await AES.AES_GCM.exportKey("jwk", key); + const importedKey = await AES.AES_GCM.importKey("jwk", jwk, { + length: 256, + }); - const jwk = await AES.AES_GCM.exportKey("jwk", key); - const importedKey = await AES.AES_GCM.importKey("jwk", jwk, { - length: 256, + const plaintextBytes = await AES.AES_GCM.decrypt( + { iv }, + importedKey.self, + ciphertextBytes + ); + expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); }); + it("should wrap and unwrap keys", async () => { + const kek = await AES.AES_GCM.generateKey({ length: 256 }, true, [ + "wrapKey", + "unwrapKey", + ]); + const dek = await AES.AES_GCM.generateKey({ + length: 256, + }); + + const ciphertextBytes = await AES.AES_GCM.encrypt( + { iv }, + dek.self, + new TextEncoder().encode(text) + ); - const plaintextBytes = await AES.AES_GCM.decrypt( - { iv }, - importedKey, - ciphertextBytes - ); - expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); + const wrappedKey = await AES.AES_GCM.wrapKey( + "raw", + dek.self, + kek.self, + { + iv, + } + ); + const unwrappedkey = (await AES.AES_GCM.unwrapKey( + "raw", + wrappedKey, + { name: AES.Alg.Mode.AES_GCM }, + kek.self, + { iv } + )) as AES.AesGcmCryptoKey; + + const plaintextBytes = await AES.AES_GCM.decrypt( + { iv }, + unwrappedkey, + ciphertextBytes + ); + expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); + }); }); - it("should wrap and unwrap keys", async () => { - const kek = await AES.AES_GCM.generateKey({ length: 256 }, true, [ - "wrapKey", - "unwrapKey", - ]); - const dek: AES.AesGcmCryptoKey = await AES.AES_GCM.generateKey({ - length: 256, + describe("Proxied", () => { + let iv: Uint8Array, key: AesGcmProxiedCryptoKey; + const text = "brown fox fox fox fox fox fox fox fox fox"; + beforeEach(async () => { + iv = await Random.IV.generate(); + key = await AES.AES_GCM.generateKey({ length: 256 }); + }); + it("should encrypt and decrypt", async () => { + const ciphertextBytes = await key.encrypt( + { iv }, + new TextEncoder().encode(text) + ); + const plaintextBytes = await key.decrypt({ iv }, ciphertextBytes); + expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); }); + it("should import and export keys", async () => { + const ciphertextBytes = await key.encrypt( + { iv }, + new TextEncoder().encode(text) + ); - const ciphertextBytes = await AES.AES_GCM.encrypt( - { iv }, - dek, - new TextEncoder().encode(text) - ); + const jwk = await key.exportKey("jwk"); + const importedKey = await AES.AES_GCM.importKey("jwk", jwk, { + length: 256, + }); - const wrappedKey = await AES.AES_GCM.wrapKey("raw", dek, kek, { - iv, + const plaintextBytes = await importedKey.decrypt( + { iv }, + ciphertextBytes + ); + expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); }); - const unwrappedkey = (await AES.AES_GCM.unwrapKey( - "raw", - wrappedKey, - { name: AES.Alg.Mode.AES_GCM }, - kek, - { iv } - )) as AES.AesGcmCryptoKey; + it("should wrap and unwrap keys", async () => { + const kek = await AES.AES_GCM.generateKey({ length: 256 }, true, [ + "wrapKey", + "unwrapKey", + ]); + const dek = await AES.AES_GCM.generateKey({ + length: 256, + }); - const plaintextBytes = await AES.AES_GCM.decrypt( - { iv }, - unwrappedkey, - ciphertextBytes - ); - expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); + const ciphertextBytes = await dek.encrypt( + { iv }, + new TextEncoder().encode(text) + ); + + const wrappedKey = await kek.wrapKey("raw", dek.self, { + iv, + }); + const unwrappedKey = (await AES.AES_GCM.unwrapKey( + "raw", + wrappedKey, + { name: AES.Alg.Mode.AES_GCM }, + kek.self, + { iv } + )) as AES.AesGcmCryptoKey; + + const plaintextBytes = await AES.AES_GCM.decrypt( + { iv }, + unwrappedKey, + ciphertextBytes + ); + expect(new TextDecoder().decode(plaintextBytes)).toEqual(text); + }); }); }); diff --git a/src/aes/__tests__/aes_kw.test.ts b/src/aes/__tests__/aes_kw.test.ts index 2c4b114..6f3429b 100644 --- a/src/aes/__tests__/aes_kw.test.ts +++ b/src/aes/__tests__/aes_kw.test.ts @@ -1,32 +1,65 @@ +import { AesKwProxiedCryptoKey } from "../aes_kw.js"; import * as AES from "../index.js"; describe("AES_KW", () => { - let key: AES.AesKwCryptoKey; - beforeEach(async () => { - key = await AES.AES_KW.generateKey(); - }); - it("should import and export keys", async () => { - const jwk = await AES.AES_KW.exportKey("jwk", key); - const importedKek = await AES.AES_KW.importKey("jwk", jwk); - const exportedKek = await AES.AES_KW.exportKey("jwk", importedKek); + describe("Original", () => { + let proxiedKey: AesKwProxiedCryptoKey; + let key: AES.AesKwCryptoKey; + beforeEach(async () => { + proxiedKey = await AES.AES_KW.generateKey(); + key = proxiedKey.self; + }); + it("should import and export keys", async () => { + const jwk = await AES.AES_KW.exportKey("jwk", key); + const importedKek = await AES.AES_KW.importKey("jwk", jwk); + const exportedKek = await AES.AES_KW.exportKey( + "jwk", + importedKek.self + ); + + expect(jwk).toEqual(exportedKek); + }); + it("should wrap and unwrap keys", async () => { + const dek = await AES.AES_CBC.generateKey(); - expect(jwk).toEqual(exportedKek); + const wrappedDek = await AES.AES_KW.wrapKey("raw", dek.self, key); + const unwrappedDek = (await AES.AES_KW.unwrapKey( + "raw", + wrappedDek, + { + name: AES.Alg.Mode.AES_CBC, + }, + key + )) as AES.AesCbcCryptoKey; + + expect(await AES.AES_CBC.exportKey("jwk", dek.self)).toEqual( + await AES.AES_CBC.exportKey("jwk", unwrappedDek) + ); + }); }); - it("should import and export keys", async () => { - const dek = await AES.AES_CBC.generateKey(); + describe("Proxied", () => { + let key: AesKwProxiedCryptoKey; + beforeEach(async () => { + key = await AES.AES_KW.generateKey(); + }); + it("should import and export keys", async () => { + const jwk = await key.exportKey("jwk"); + const importedKek = await AES.AES_KW.importKey("jwk", jwk); + const exportedKek = await importedKek.exportKey("jwk"); + + expect(jwk).toEqual(exportedKek); + }); + it("should wrap and unwrap keys", async () => { + const dek = await AES.AES_CBC.generateKey(); - const wrappedDek = await AES.AES_KW.wrapKey("raw", dek, key); - const unwrappedDek = (await AES.AES_KW.unwrapKey( - "raw", - wrappedDek, - { + const wrappedDek = await key.wrapKey("raw", dek.self); + const unwrappedDek = (await key.unwrapKey("raw", wrappedDek, { name: AES.Alg.Mode.AES_CBC, - }, - key - )) as AES.AesCbcCryptoKey; + })) as AES.AesCbcCryptoKey; - expect(await AES.AES_CBC.exportKey("jwk", dek)).toEqual( - await AES.AES_CBC.exportKey("jwk", unwrappedDek) - ); + expect(await AES.AES_CBC.exportKey("jwk", dek.self)).toEqual( + await AES.AES_CBC.exportKey("jwk", unwrappedDek) + ); + }); }); }); diff --git a/src/aes/aes_cbc.ts b/src/aes/aes_cbc.ts index dad5e4f..05256a5 100644 --- a/src/aes/aes_cbc.ts +++ b/src/aes/aes_cbc.ts @@ -4,7 +4,94 @@ */ import * as params from "../params.js"; +import * as proxy from "../proxy.js"; import { AesCbcCryptoKey, AesShared, Alg } from "./shared.js"; + +export interface AesCbcProxiedCryptoKey + extends proxy.ProxiedCryptoKey { + encrypt( + algorithm: Omit, + data: BufferSource + ): Promise; + + decrypt( + algorithm: Omit, + data: BufferSource + ): Promise; + + wrapKey( + format: KeyFormat, + key: CryptoKey, + wrapAlgorithm: Omit + ): Promise; + + unwrapKey( + format: KeyFormat, + wrappedKey: BufferSource, + wrappedKeyAlgorithm: params.EnforcedImportParams, + unwrappingKeyAlgorithm: Omit, + extractable?: boolean, + keyUsages?: KeyUsage[] + ): Promise; + + exportKey: (format: KeyFormat) => Promise; +} + +const handler: ProxyHandler = { + get(target: AesCbcCryptoKey, prop: string) { + switch (prop) { + case "self": + return target; + + case "encrypt": + return ( + algorithm: Omit, + data: BufferSource + ) => encrypt(algorithm, target, data); + + case "decrypt": + return ( + algorithm: Omit, + data: BufferSource + ) => decrypt(algorithm, target, data); + + case "wrapKey": + return ( + format: KeyFormat, + key: CryptoKey, + wrapAlgorithm: Omit + ) => wrapKey(format, key, target, wrapAlgorithm); + + case "unwrapKey": + return ( + format: KeyFormat, + wrappedKey: BufferSource, + wrappedKeyAlgorithm: params.EnforcedImportParams, + unwrappingKeyAlgorithm: Omit< + params.EnforcedAesCbcParams, + "name" + >, + extractable?: boolean, + keyUsages?: KeyUsage[] + ) => + unwrapKey( + format, + wrappedKey, + wrappedKeyAlgorithm, + target, + unwrappingKeyAlgorithm, + extractable, + keyUsages + ); + + case "exportKey": + return (format: KeyFormat) => exportKey(format, target); + } + + return Reflect.get(target, prop); + }, +}; + /** * Generate a new AES_CBC key * @example @@ -18,8 +105,8 @@ export async function generateKey( }, extractable: boolean = true, keyUsages?: KeyUsage[] -): Promise { - return await AesShared.generateKey( +): Promise { + const key = await AesShared.generateKey( { ...algorithm, name: Alg.Mode.AES_CBC, @@ -27,6 +114,9 @@ export async function generateKey( extractable, keyUsages ); + return proxy.proxifyKey(handler)( + key + ); } /** @@ -44,8 +134,8 @@ export async function importKey( algorithm: Omit, extractable?: boolean, keyUsages?: KeyUsage[] -): Promise { - return await AesShared.importKey( +): Promise { + const importedKey = (await AesShared.importKey( format as any, key as any, { @@ -54,6 +144,9 @@ export async function importKey( }, extractable, keyUsages + )) as AesCbcCryptoKey; + return proxy.proxifyKey(handler)( + importedKey ); } @@ -62,7 +155,12 @@ export async function importKey( * @example * ```ts * const key = await AES_CBC.generateKey(); - * const jwk = await AES_CBC.exportKey("jwk", key); + * const jwk = await AES_CBC.exportKey("jwk", key.self); + * ``` + * @example + * ```ts + * const key = await AES_CBC.generateKey(); + * const jwk = await key.exportKey("jwk"); * ``` */ export const exportKey = async (format: KeyFormat, key: AesCbcCryptoKey) => @@ -76,7 +174,16 @@ export const exportKey = async (format: KeyFormat, key: AesCbcCryptoKey) => * const iv = await IV.generate(); * const ciphertextBytes = await AES_CBC.encrypt( * { iv }, - * key, + * key.self, + * new TextEncoder().encode('message') + * ); + * ``` + * @example + * ```ts + * const key = await AES_CBC.generateKey(); + * const iv = await IV.generate(); + * const ciphertextBytes = await key.encrypt( + * { iv }, * new TextEncoder().encode('message') * ); * ``` @@ -102,7 +209,14 @@ export async function encrypt( * ```ts * const plaintextBytes = await AES_CBC.decrypt( * { iv }, - * key, + * key.self, + * ciphertextBytes + * ); + * ``` + * @example + * ```ts + * const plaintextBytes = await key.decrypt( + * { iv }, * ciphertextBytes * ); * ``` @@ -138,6 +252,19 @@ export async function decrypt( * iv, * }); * ``` + * ```ts + * const kek = await AES_CBC.generateKey({ length: 256 }, true, [ + * "wrapKey", + * "unwrapKey", + * ]); + * const dek: AesCbcCryptoKey = await AES_CBC.generateKey({ + * length: 256, + * }); + * const iv = await IV.generate(); + * const wrappedKey = await kek.wrapKey("raw", dek.self, { + * iv, + * }); + * ``` */ export async function wrapKey( format: KeyFormat, @@ -155,14 +282,26 @@ export async function wrapKey( * Unwrap a wrapped key using the key encryption key * @example * ```ts - * const wrappedKey = await AES_CBC.wrapKey("raw", dek, kek, { + * const wrappedKey = await AES_CBC.wrapKey("raw", dek.self, kek.self, { + * iv, + * }); + * const unwrappedKey = await AES_CBC.unwrapKey( + * "raw", + * wrappedKey, + * { name: Alg.Mode.AES_CBC }, + * kek.self, + * { iv } + * ); + * ``` + * @example + * ```ts + * const wrappedKey = await AES_CBC.wrapKey("raw", dek.self, kek.self, { * iv, * }); - * const unwrappedkey = await AES_CBC.unwrapKey( + * const unwrappedKey = await kek.unwrapKey( * "raw", * wrappedKey, * { name: Alg.Mode.AES_CBC }, - * kek, * { iv } * ); * ``` diff --git a/src/aes/aes_ctr.ts b/src/aes/aes_ctr.ts index 6acfbc8..6dc7d26 100644 --- a/src/aes/aes_ctr.ts +++ b/src/aes/aes_ctr.ts @@ -4,9 +4,95 @@ */ import * as params from "../params.js"; +import * as proxy from "../proxy.js"; import { getValues } from "../random.js"; import { AesCtrCryptoKey, AesShared, Alg } from "./shared.js"; +export interface AesCtrProxiedCryptoKey + extends proxy.ProxiedCryptoKey { + encrypt( + algorithm: Omit, + data: BufferSource + ): Promise; + + decrypt( + algorithm: Omit, + data: BufferSource + ): Promise; + + wrapKey( + format: KeyFormat, + key: CryptoKey, + wrapAlgorithm: Omit + ): Promise; + + unwrapKey( + format: KeyFormat, + wrappedKey: BufferSource, + wrappedKeyAlgorithm: params.EnforcedImportParams, + unwrappingKeyAlgorithm: Omit, + extractable?: boolean, + keyUsages?: KeyUsage[] + ): Promise; + + exportKey: (format: KeyFormat) => Promise; +} + +const handler: ProxyHandler = { + get(target: AesCtrCryptoKey, prop: string) { + switch (prop) { + case "self": + return target; + + case "encrypt": + return ( + algorithm: Omit, + data: BufferSource + ) => encrypt(algorithm, target, data); + + case "decrypt": + return ( + algorithm: Omit, + data: BufferSource + ) => decrypt(algorithm, target, data); + + case "wrapKey": + return ( + format: KeyFormat, + key: CryptoKey, + wrapAlgorithm: Omit + ) => wrapKey(format, key, target, wrapAlgorithm); + + case "unwrapKey": + return ( + format: KeyFormat, + wrappedKey: BufferSource, + wrappedKeyAlgorithm: params.EnforcedImportParams, + unwrappingKeyAlgorithm: Omit< + params.EnforcedAesCtrParams, + "name" + >, + extractable?: boolean, + keyUsages?: KeyUsage[] + ) => + unwrapKey( + format, + wrappedKey, + wrappedKeyAlgorithm, + target, + unwrappingKeyAlgorithm, + extractable, + keyUsages + ); + + case "exportKey": + return (format: KeyFormat) => exportKey(format, target); + } + + return Reflect.get(target, prop); + }, +}; + /** * Generates a counter, with the given length, starting from the count of 1. The nonce is randomized. * @see https://developer.mozilla.org/en-US/docs/Web/API/AesCtrParams @@ -38,8 +124,8 @@ export async function generateKey( }, extractable: boolean = true, keyUsages?: KeyUsage[] -): Promise { - return await AesShared.generateKey( +): Promise { + const key = await AesShared.generateKey( { ...algorithm, name: Alg.Mode.AES_CTR, @@ -47,6 +133,9 @@ export async function generateKey( extractable, keyUsages ); + return proxy.proxifyKey(handler)( + key + ); } /** @@ -62,8 +151,8 @@ export async function importKey( algorithm: Omit, extractable?: boolean, keyUsages?: KeyUsage[] -): Promise { - return await AesShared.importKey( +): Promise { + const importedKey = (await AesShared.importKey( format as any, key as any, { @@ -72,6 +161,9 @@ export async function importKey( }, extractable, keyUsages + )) as AesCtrCryptoKey; + return proxy.proxifyKey(handler)( + importedKey ); } @@ -79,7 +171,13 @@ export async function importKey( * Export an AES_CTR key into the specified format * @example * ```ts - * const jwk = await AES_CTR.exportKey("jwk", key); + * const key = await AES_CTR.generateKey(); + * const jwk = await AES_CTR.exportKey("jwk", key.self); + * ``` + * @example + * ```ts + * const key = await AES_CTR.generateKey(); + * const jwk = await key.exportKey("jwk"); * ``` */ export const exportKey = async (format: KeyFormat, key: AesCtrCryptoKey) => @@ -93,7 +191,15 @@ export const exportKey = async (format: KeyFormat, key: AesCtrCryptoKey) => * const message = new TextEncoder().encode("a message"); * const length = 8; * const counter = await AES_CTR.generateCounter(length); - * const data = await AES_CTR.encrypt({length, counter}, key, message); + * const data = await AES_CTR.encrypt({length, counter}, key.self, message); + * ``` + * @example + * ```ts + * const key = await AES_CTR.generateKey(); + * const message = new TextEncoder().encode("a message"); + * const length = 8; + * const counter = await AES_CTR.generateCounter(length); + * const data = await key.encrypt({length, counter}, message); * ``` */ export async function encrypt( @@ -115,7 +221,11 @@ export async function encrypt( * Decrypt with an AES_CTR key * @example * ```ts - * const data = await AES_CTR.decrypt({length, counter}, key, data); + * const data = await AES_CTR.decrypt({length, counter}, key.self, data); + * ``` + * @example + * ```ts + * const data = await key.decrypt({length, counter}, data); * ``` */ export async function decrypt( @@ -141,7 +251,15 @@ export async function decrypt( * const dek = await AES_CTR.generateKey(); * const length = 8; * const counter = await AES_CTR.generateCounter(length); - * const wrappedKey = await AES_CTR.wrapKey("raw", dek, kek, {length, counter}); + * const wrappedKey = await AES_CTR.wrapKey("raw", dek.self, kek.self, {length, counter}); + * ``` + * @example + * ```ts + * const kek = await AES_CTR.generateKey({length: 256}, true, ['wrapKey', 'unwrapKey']); + * const dek = await AES_CTR.generateKey(); + * const length = 8; + * const counter = await AES_CTR.generateCounter(length); + * const wrappedKey = await kek.wrapKey("raw", dek.self, {length, counter}); * ``` */ export async function wrapKey( @@ -160,7 +278,11 @@ export async function wrapKey( * Unwrap a wrapped key using the key encryption key * @example * ```ts - * const dek = await AES_CTR.unwrapKey("raw", wrappedKey, {name: "AES_CTR"}, kek, {length, counter}); + * const dek = await AES_CTR.unwrapKey("raw", wrappedKey, {name: "AES_CTR"}, kek.self, {length, counter}); + * ``` + * @example + * ```ts + * const dek = await kek.unwrapKey("raw", wrappedKey, {name: "AES_CTR"}, {length, counter}); * ``` */ export async function unwrapKey( diff --git a/src/aes/aes_gcm.ts b/src/aes/aes_gcm.ts index d07e269..b68627c 100644 --- a/src/aes/aes_gcm.ts +++ b/src/aes/aes_gcm.ts @@ -3,8 +3,94 @@ * @module */ import * as params from "../params.js"; +import * as proxy from "../proxy.js"; import { AesGcmCryptoKey, AesShared, Alg } from "./shared.js"; +export interface AesGcmProxiedCryptoKey + extends proxy.ProxiedCryptoKey { + encrypt( + algorithm: Omit, + data: BufferSource + ): Promise; + + decrypt( + algorithm: Omit, + data: BufferSource + ): Promise; + + wrapKey( + format: KeyFormat, + key: CryptoKey, + wrapAlgorithm: Omit + ): Promise; + + unwrapKey( + format: KeyFormat, + wrappedKey: BufferSource, + wrappedKeyAlgorithm: params.EnforcedImportParams, + unwrappingKeyAlgorithm: Omit, + extractable?: boolean, + keyUsages?: KeyUsage[] + ): Promise; + + exportKey: (format: KeyFormat) => Promise; +} + +const handler: ProxyHandler = { + get(target: AesGcmCryptoKey, prop: string) { + switch (prop) { + case "self": + return target; + + case "encrypt": + return ( + algorithm: Omit, + data: BufferSource + ) => encrypt(algorithm, target, data); + + case "decrypt": + return ( + algorithm: Omit, + data: BufferSource + ) => decrypt(algorithm, target, data); + + case "wrapKey": + return ( + format: KeyFormat, + key: CryptoKey, + wrapAlgorithm: Omit + ) => wrapKey(format, key, target, wrapAlgorithm); + + case "unwrapKey": + return ( + format: KeyFormat, + wrappedKey: BufferSource, + wrappedKeyAlgorithm: params.EnforcedImportParams, + unwrappingKeyAlgorithm: Omit< + params.EnforcedAesGcmParams, + "name" + >, + extractable?: boolean, + keyUsages?: KeyUsage[] + ) => + unwrapKey( + format, + wrappedKey, + wrappedKeyAlgorithm, + target, + unwrappingKeyAlgorithm, + extractable, + keyUsages + ); + + case "exportKey": + return (format: KeyFormat) => exportKey(format, target); + } + + return Reflect.get(target, prop); + }, +}; + /** * Generate a new AES_GCM key * @example @@ -18,8 +104,8 @@ export async function generateKey( }, extractable: boolean = true, keyUsages?: KeyUsage[] -): Promise { - return await AesShared.generateKey( +): Promise { + const key = await AesShared.generateKey( { ...algorithm, name: Alg.Mode.AES_GCM, @@ -27,6 +113,9 @@ export async function generateKey( extractable, keyUsages ); + return proxy.proxifyKey(handler)( + key + ); } /** @@ -42,8 +131,8 @@ export async function importKey( algorithm: Omit, extractable?: boolean, keyUsages?: KeyUsage[] -): Promise { - return await AesShared.importKey( +): Promise { + const importedKey = (await AesShared.importKey( format as any, key as any, { @@ -52,6 +141,9 @@ export async function importKey( }, extractable, keyUsages + )) as AesGcmCryptoKey; + return proxy.proxifyKey(handler)( + importedKey ); } @@ -59,7 +151,13 @@ export async function importKey( * Export an AES_GCM key into the specified format * @example * ```ts - * const jwk = await AES_GCM.exportKey("jwk", key); + * const key = await AES_GCM.generateKey(); + * const jwk = await AES_GCM.exportKey("jwk", key.self); + * ``` + * @example + * ```ts + * const key = await AES_GCM.generateKey(); + * const jwk = await key.exportKey("jwk"); * ``` */ export const exportKey = async (format: KeyFormat, key: AesGcmCryptoKey) => @@ -72,7 +170,14 @@ export const exportKey = async (format: KeyFormat, key: AesGcmCryptoKey) => * const iv = await Random.IV.generate(); * const key = await AES_GCM.generateKey(); * const message = new TextEncoder().encode("a message"); - * const data = await AES_GCM.encrypt({iv}, key, message); + * const data = await AES_GCM.encrypt({iv}, key.self, message); + * ``` + * @example + * ```ts + * const iv = await Random.IV.generate(); + * const key = await AES_GCM.generateKey(); + * const message = new TextEncoder().encode("a message"); + * const data = await key.encrypt({iv}, message); * ``` */ export async function encrypt( @@ -94,7 +199,13 @@ export async function encrypt( * Decrypt with an AES_GCM key * @example * ```ts - * const data = await AES_GCM.decrypt({iv}, key, data); + * const key = await AES_GCM.generateKey(); + * const data = await AES_GCM.decrypt({iv}, key.self, data); + * ``` + * @example + * ```ts + * const key = await AES_GCM.generateKey(); + * const data = await key.decrypt({iv}, data); * ``` */ export async function decrypt( @@ -119,7 +230,14 @@ export async function decrypt( * const iv = await Random.IV.generate(); * const kek = await AES_GCM.generateKey({length: 256}, true, ['wrapKey', 'unwrapKey']); * const dek = await AES_GCM.generateKey(); - * const wrappedKey = await AES_GCM.wrapKey("raw", dek, kek, {iv}); + * const wrappedKey = await AES_GCM.wrapKey("raw", dek.self, kek.self, {iv}); + * ``` + * @example + * ```ts + * const iv = await Random.IV.generate(); + * const kek = await AES_GCM.generateKey({length: 256}, true, ['wrapKey', 'unwrapKey']); + * const dek = await AES_GCM.generateKey(); + * const wrappedKey = await kek.wrapKey("raw", dek.self, {iv}); * ``` */ export async function wrapKey( @@ -140,6 +258,10 @@ export async function wrapKey( * ```ts * const dek = await AES_GCM.unwrapKey("raw", wrappedKey, {name: "AES_GCM"}, kek, {iv}); * ``` + * @example + * ```ts + * const dek = await kek.unwrapKey("raw", wrappedKey, {name: "AES_GCM"}, {iv}); + * ``` */ export async function unwrapKey( format: KeyFormat, diff --git a/src/aes/aes_kw.ts b/src/aes/aes_kw.ts index 7423871..bcb6363 100644 --- a/src/aes/aes_kw.ts +++ b/src/aes/aes_kw.ts @@ -3,8 +3,57 @@ * @module */ import * as params from "../params.js"; +import * as proxy from "../proxy.js"; import { AesKwCryptoKey, AesShared, Alg } from "./shared.js"; +export interface AesKwProxiedCryptoKey + extends proxy.ProxiedCryptoKey { + wrapKey(format: KeyFormat, key: CryptoKey): Promise; + + unwrapKey( + format: KeyFormat, + wrappedKey: BufferSource, + wrappedKeyAlgorithm: params.EnforcedImportParams, + extractable?: boolean, + keyUsages?: KeyUsage[] + ): Promise; + + exportKey: (format: KeyFormat) => Promise; +} + +const handler: ProxyHandler = { + get(target: AesKwCryptoKey, prop: string) { + switch (prop) { + case "self": + return target; + + case "wrapKey": + return (format: KeyFormat, key: CryptoKey) => + wrapKey(format, key, target); + case "unwrapKey": + return ( + format: KeyFormat, + wrappedKey: BufferSource, + wrappedKeyAlgorithm: params.EnforcedImportParams, + extractable?: boolean, + keyUsages?: KeyUsage[] + ) => + unwrapKey( + format, + wrappedKey, + wrappedKeyAlgorithm, + target, + extractable, + keyUsages + ); + case "exportKey": + return (format: KeyFormat) => exportKey(format, target); + } + + return Reflect.get(target, prop); + }, +}; + /** * Generate a new AES_KW key * @example @@ -18,14 +67,17 @@ export async function generateKey( }, extractable: boolean = true, keyUsages?: KeyUsage[] -): Promise { - return await AesShared.generateKey( +): Promise { + const key = (await AesShared.generateKey( { ...algorithm, name: Alg.Mode.AES_KW, }, extractable, keyUsages + )) as AesKwCryptoKey; + return proxy.proxifyKey(handler)( + key ); } @@ -41,8 +93,8 @@ export async function importKey( key: BufferSource | JsonWebKey, extractable?: boolean, keyUsages?: KeyUsage[] -): Promise { - return await AesShared.importKey( +): Promise { + const importedKey = (await AesShared.importKey( format as any, key as any, { @@ -50,6 +102,9 @@ export async function importKey( }, extractable, keyUsages + )) as AesKwCryptoKey; + return proxy.proxifyKey(handler)( + importedKey ); } @@ -57,7 +112,11 @@ export async function importKey( * Export an AES_KW key into the specified format * @example * ```ts - * const jwk = await AES_KW.exportKey("jwk", key); + * const jwk = await AES_KW.exportKey("jwk", key.self); + * ``` + * @example + * ```ts + * const jwk = await key.exportKey("jwk"); * ``` */ export const exportKey = async (format: KeyFormat, key: AesKwCryptoKey) => @@ -69,7 +128,13 @@ export const exportKey = async (format: KeyFormat, key: AesKwCryptoKey) => * ```ts * const kek = await AES_KW.generateKey({length: 256}, true, ['wrapKey', 'unwrapKey']); * const dek = await AES_GCM.generateKey(); - * const wrappedKey = await AES_GCM.wrapKey("raw", dek, kek); + * const wrappedKey = await AES_KW.wrapKey("raw", dek.self, kek.self); + * ``` + * @example + * ```ts + * const kek = await AES_KW.generateKey({length: 256}, true, ['wrapKey', 'unwrapKey']); + * const dek = await AES_GCM.generateKey(); + * const wrappedKey = await kek.wrapKey("raw", dek.self); * ``` */ export async function wrapKey( @@ -86,7 +151,11 @@ export async function wrapKey( * Unwrap a wrapped key using the key encryption key * @example * ```ts - * const dek = await AES_GCM.unwrapKey("raw", wrappedKey, {name: "AES_GCM"}, kek); + * const dek = await AES_KW.unwrapKey("raw", wrappedKey, {name: "AES_GCM"}, kek.self); + * ``` + * @example + * ```ts + * const dek = await kek.unwrapKey("raw", wrappedKey, {name: "AES_GCM"}); * ``` */ export async function unwrapKey( diff --git a/src/aes/shared.ts b/src/aes/shared.ts index f49323d..01fda9b 100644 --- a/src/aes/shared.ts +++ b/src/aes/shared.ts @@ -3,8 +3,9 @@ * @module */ -import { getKeyUsagePairsByAlg } from "../key_usages.js"; +import * as usages from "../key_usages.js"; import * as params from "../params.js"; +import * as proxy from "../proxy.js"; import * as WebCrypto from "../webcrypto.js"; export interface AesGcmCryptoKey extends CryptoKey { @@ -40,6 +41,127 @@ export namespace Alg { export type Modes = `${Mode}`; } +export interface AesProxiedCryptoKey + extends proxy.ProxiedCryptoKey { + encrypt( + algorithm: Exclude< + params.EnforcedAesParams, + params.EnforcedAesKwParams + >, + data: BufferSource + ): Promise; + + decrypt( + algorithm: Exclude< + params.EnforcedAesParams, + params.EnforcedAesKwParams + >, + data: BufferSource + ): Promise; + + wrapKey( + format: KeyFormat, + key: CryptoKey, + wrapAlgorithm: params.EnforcedAesParams + ): Promise; + + unwrapKey( + format: KeyFormat, + wrappedKey: BufferSource, + wrappedKeyAlgorithm: params.EnforcedImportParams, + unwrappingKeyAlgorithm: params.EnforcedAesParams, + extractable: boolean, + keyUsages?: KeyUsage[] + ): Promise; + + exportKey: (format: KeyFormat) => Promise; +} + +export const handler: ProxyHandler = { + get(target: AesCryptoKeys, prop: string) { + switch (prop) { + case "self": + return target; + + case "encrypt": + if ( + !target.usages.every((u) => + usages.EncryptionKeyUsagePair.includes(u) + ) + ) { + throw new Error("encrypt is not supported"); + } + return ( + algorithm: Exclude< + params.EnforcedAesParams, + params.EnforcedAesKwParams + >, + data: BufferSource + ) => AesShared.encrypt(algorithm, target, data); + + case "decrypt": + if ( + !target.usages.every((u) => + usages.EncryptionKeyUsagePair.includes(u) + ) + ) { + throw new Error("decrypt is not supported"); + } + return ( + algorithm: Exclude< + params.EnforcedAesParams, + params.EnforcedAesKwParams + >, + data: BufferSource + ) => AesShared.decrypt(algorithm, target, data); + + case "wrapKey": + if ( + !target.usages.every((u) => + usages.WrappingKeyUsagePair.includes(u) + ) + ) { + throw new Error("wrapKey is not supported"); + } + return ( + format: KeyFormat, + key: CryptoKey, + wrapAlgorithm: params.EnforcedAesParams + ) => AesShared.wrapKey(format, key, target, wrapAlgorithm); + case "unwrapKey": + if ( + !target.usages.every((u) => + usages.WrappingKeyUsagePair.includes(u) + ) + ) { + throw new Error("unwrapKey is not supported"); + } + return ( + format: KeyFormat, + wrappedKey: BufferSource, + wrappedKeyAlgorithm: params.EnforcedImportParams, + unwrappingKeyAlgorithm: params.EnforcedAesParams, + extractable?: boolean, + keyUsages?: KeyUsage[] + ) => + AesShared.unwrapKey( + format, + wrappedKey, + wrappedKeyAlgorithm, + target, + unwrappingKeyAlgorithm, + extractable, + keyUsages + ); + case "exportKey": + return (format: KeyFormat) => + AesShared.exportKey(format, target); + } + + return Reflect.get(target, prop); + }, +}; + export namespace AesShared { export async function generateKey( algorithm: params.EnforcedAesKeyGenParams, @@ -49,7 +171,7 @@ export namespace AesShared { return await WebCrypto.generateKey( algorithm, extractable, - keyUsages ?? getKeyUsagePairsByAlg(algorithm.name) + keyUsages ?? usages.getKeyUsagePairsByAlg(algorithm.name) ); } @@ -65,7 +187,7 @@ export namespace AesShared { key as any, algorithm, extractable, - keyUsages ?? getKeyUsagePairsByAlg(algorithm.name) + keyUsages ?? usages.getKeyUsagePairsByAlg(algorithm.name) ); } @@ -127,7 +249,7 @@ export namespace AesShared { unwrappingKeyAlgorithm, wrappedKeyAlgorithm, extractable, - keyUsages ?? getKeyUsagePairsByAlg(wrappedKeyAlgorithm.name) + keyUsages ?? usages.getKeyUsagePairsByAlg(wrappedKeyAlgorithm.name) ); } } From 8084b28d0e2717719cae0a947a8c5507e1a2a455 Mon Sep 17 00:00:00 2001 From: Neal Fennimore Date: Mon, 29 May 2023 19:57:52 -0400 Subject: [PATCH 11/20] Update KDF to be proxied --- .../__tests__/__snapshots__/hkdf.test.ts.snap | 1264 +++++++++++++++++ .../__snapshots__/pbkdf2.test.ts.snap | 1264 +++++++++++++++++ src/kdf/__tests__/hkdf.test.ts | 184 ++- src/kdf/__tests__/pbkdf2.test.ts | 176 ++- src/kdf/hkdf.ts | 86 +- src/kdf/pbkdf.ts | 68 +- 6 files changed, 2924 insertions(+), 118 deletions(-) diff --git a/src/kdf/__tests__/__snapshots__/hkdf.test.ts.snap b/src/kdf/__tests__/__snapshots__/hkdf.test.ts.snap index 6c68cf1..13ba695 100644 --- a/src/kdf/__tests__/__snapshots__/hkdf.test.ts.snap +++ b/src/kdf/__tests__/__snapshots__/hkdf.test.ts.snap @@ -1,5 +1,1269 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`HKDF Original should derive keys: AES_CBC_128_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_CBC_128_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_CBC_128_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_CBC_192_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_CBC_192_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_CBC_192_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_CBC_256_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_CBC_256_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_CBC_256_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_CTR_128_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_CTR_128_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_CTR_128_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_CTR_192_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_CTR_192_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_CTR_192_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_CTR_256_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_CTR_256_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_CTR_256_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_GCM_128_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_GCM_128_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_GCM_128_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_GCM_192_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_GCM_192_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_GCM_192_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_GCM_256_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_GCM_256_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_GCM_256_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_KW_128_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_KW_128_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_KW_128_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_KW_192_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_KW_192_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_KW_192_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_KW_256_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_KW_256_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`HKDF Original should derive keys: AES_KW_256_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`HKDF Original should derive keys: HMAC_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "hash": Object { + "name": "SHA-512", + }, + "length": 512, + "name": "HMAC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "sign", + "verify", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_CBC_128_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_CBC_128_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_CBC_128_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_CBC_192_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_CBC_192_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_CBC_192_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_CBC_256_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_CBC_256_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_CBC_256_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_CTR_128_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_CTR_128_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_CTR_128_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_CTR_192_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_CTR_192_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_CTR_192_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_CTR_256_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_CTR_256_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_CTR_256_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_GCM_128_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_GCM_128_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_GCM_128_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_GCM_192_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_GCM_192_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_GCM_192_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_GCM_256_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_GCM_256_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_GCM_256_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_KW_128_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_KW_128_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_KW_128_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_KW_192_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_KW_192_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_KW_192_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_KW_256_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_KW_256_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`HKDF Proxied should derive keys: AES_KW_256_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`HKDF Proxied should derive keys: HMAC_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "hash": Object { + "name": "SHA-512", + }, + "length": 512, + "name": "HMAC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "sign", + "verify", + ], +} +`; + exports[`HKDF should derive keys: AES_CBC_128_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { diff --git a/src/kdf/__tests__/__snapshots__/pbkdf2.test.ts.snap b/src/kdf/__tests__/__snapshots__/pbkdf2.test.ts.snap index 4c830b7..ac1fb30 100644 --- a/src/kdf/__tests__/__snapshots__/pbkdf2.test.ts.snap +++ b/src/kdf/__tests__/__snapshots__/pbkdf2.test.ts.snap @@ -1,5 +1,1269 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`PBKDF2 Original should derive keys: AES_CBC_128_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_CBC_128_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_CBC_128_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_CBC_192_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_CBC_192_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_CBC_192_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_CBC_256_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_CBC_256_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_CBC_256_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_CTR_128_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_CTR_128_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_CTR_128_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_CTR_192_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_CTR_192_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_CTR_192_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_CTR_256_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_CTR_256_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_CTR_256_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_GCM_128_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_GCM_128_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_GCM_128_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_GCM_192_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_GCM_192_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_GCM_192_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_GCM_256_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_GCM_256_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_GCM_256_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_KW_128_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_KW_128_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_KW_128_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_KW_192_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_KW_192_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_KW_192_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_KW_256_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_KW_256_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: AES_KW_256_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`PBKDF2 Original should derive keys: HMAC_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "hash": Object { + "name": "SHA-512", + }, + "length": 512, + "name": "HMAC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "sign", + "verify", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_CBC_128_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_CBC_128_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_CBC_128_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_CBC_192_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_CBC_192_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_CBC_192_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_CBC_256_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_CBC_256_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_CBC_256_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CBC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_CTR_128_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_CTR_128_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_CTR_128_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_CTR_192_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_CTR_192_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_CTR_192_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_CTR_256_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_CTR_256_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_CTR_256_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-CTR", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_GCM_128_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_GCM_128_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_GCM_128_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_GCM_192_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_GCM_192_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_GCM_192_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_GCM_256_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_GCM_256_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_GCM_256_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-GCM", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + "decrypt", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_KW_128_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_KW_128_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_KW_128_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 128, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_KW_192_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_KW_192_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_KW_192_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 192, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_KW_256_SHA_256 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_KW_256_SHA_384 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: AES_KW_256_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "length": 256, + "name": "AES-KW", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "wrapKey", + "unwrapKey", + ], +} +`; + +exports[`PBKDF2 Proxied should derive keys: HMAC_SHA_512 1`] = ` +CryptoKey { + Symbol(kKeyObject): SecretKeyObject { + Symbol(kKeyType): "secret", + }, + Symbol(kAlgorithm): Object { + "hash": Object { + "name": "SHA-512", + }, + "length": 512, + "name": "HMAC", + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "sign", + "verify", + ], +} +`; + exports[`PBKDF2 should derive keys: AES_CBC_128_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { diff --git a/src/kdf/__tests__/hkdf.test.ts b/src/kdf/__tests__/hkdf.test.ts index f35d4e6..5f2c4a4 100644 --- a/src/kdf/__tests__/hkdf.test.ts +++ b/src/kdf/__tests__/hkdf.test.ts @@ -1,76 +1,148 @@ import { Alg as AES } from "../../aes/shared.js"; import { Alg as Authentication } from "../../hmac/index.js"; -import { Alg as SHA } from "../../sha/shared.js"; import * as params from "../../params.js"; import * as Random from "../../random.js"; +import { Alg as SHA } from "../../sha/shared.js"; +import { HkdfProxiedKeyMaterial } from "../hkdf.js"; import * as KDF from "../index.js"; import type { HkdfKeyMaterial } from "../shared.js"; const { HKDF } = KDF; describe("HKDF", () => { - let keyMaterial: HkdfKeyMaterial, salt: Uint8Array, info: Uint8Array; - beforeEach(async () => { - keyMaterial = await HKDF.generateKeyMaterial( - "raw", - new TextEncoder().encode("password") - ); + describe("Original", () => { + let proxiedKeyMaterial: HkdfProxiedKeyMaterial, + keyMaterial: HkdfKeyMaterial, + salt: Uint8Array, + info: Uint8Array; + beforeEach(async () => { + proxiedKeyMaterial = await HKDF.generateKeyMaterial( + "raw", + new TextEncoder().encode("password") + ); + keyMaterial = proxiedKeyMaterial.self; - salt = await Random.Salt.generate(); - info = await Random.Salt.generate(); - }); - it("should derive bits", async () => { - const bits = await HKDF.deriveBits( - { salt, info, hash: SHA.Variant.SHA_512 }, - keyMaterial, - 512 - ); - expect(bits.byteLength).toEqual(64); + salt = await Random.Salt.generate(); + info = await Random.Salt.generate(); + }); + it("should derive bits", async () => { + const bits = await HKDF.deriveBits( + { salt, info, hash: SHA.Variant.SHA_512 }, + keyMaterial, + 512 + ); + expect(bits.byteLength).toEqual(64); + + await expect( + HKDF.deriveBits( + { salt, info, hash: SHA.Variant.SHA_512 }, + keyMaterial, + 511 + ) + ).rejects.toThrowError(RangeError); + }); + it("should derive keys", async () => { + for (const [aesKey, aesVal] of Object.entries(AES.Mode)) { + for (const [shaKey, shaVal] of Object.entries(SHA.Variant)) { + if (shaVal === "SHA-1") { + continue; + } + for (const aesLength of [128, 192, 256]) { + const aesParams: params.EnforcedAesKeyGenParams = { + name: aesVal, + length: aesLength as any, + }; + try { + let key = await HKDF.deriveKey( + { salt, info, hash: shaVal }, + keyMaterial, + aesParams + ); + expect(key).toMatchSnapshot( + `${aesKey}_${aesLength}_${shaKey}` + ); + } catch (e) { + console.log(`${aesKey}_${aesLength}_${shaKey}`); + } + } + } + } - await expect( - HKDF.deriveBits( + const hmacParams: params.EnforcedHmacKeyGenParams = { + name: Authentication.Code.HMAC, + hash: SHA.Variant.SHA_512, + length: 512, + }; + let key = await HKDF.deriveKey( { salt, info, hash: SHA.Variant.SHA_512 }, keyMaterial, - 511 - ) - ).rejects.toThrowError(RangeError); + hmacParams + ); + expect(key).toMatchSnapshot("HMAC_SHA_512"); + }); }); - it("should derive keys", async () => { - for (const [aesKey, aesVal] of Object.entries(AES.Mode)) { - for (const [shaKey, shaVal] of Object.entries(SHA.Variant)) { - if (shaVal === "SHA-1") { - continue; - } - for (const aesLength of [128, 192, 256]) { - const aesParams: params.EnforcedAesKeyGenParams = { - name: aesVal, - length: aesLength as any, - }; - try { - let key = await HKDF.deriveKey( - { salt, info, hash: shaVal }, - keyMaterial, - aesParams - ); - expect(key).toMatchSnapshot( - `${aesKey}_${aesLength}_${shaKey}` - ); - } catch (e) { - console.log(`${aesKey}_${aesLength}_${shaKey}`); + describe("Proxied", () => { + let keyMaterial: HkdfProxiedKeyMaterial, + salt: Uint8Array, + info: Uint8Array; + beforeEach(async () => { + keyMaterial = await HKDF.generateKeyMaterial( + "raw", + new TextEncoder().encode("password") + ); + + salt = await Random.Salt.generate(); + info = await Random.Salt.generate(); + }); + it("should derive bits", async () => { + const bits = await keyMaterial.deriveBits( + { salt, info, hash: SHA.Variant.SHA_512 }, + 512 + ); + expect(bits.byteLength).toEqual(64); + + await expect( + keyMaterial.deriveBits( + { salt, info, hash: SHA.Variant.SHA_512 }, + 511 + ) + ).rejects.toThrowError(RangeError); + }); + it("should derive keys", async () => { + for (const [aesKey, aesVal] of Object.entries(AES.Mode)) { + for (const [shaKey, shaVal] of Object.entries(SHA.Variant)) { + if (shaVal === "SHA-1") { + continue; + } + for (const aesLength of [128, 192, 256]) { + const aesParams: params.EnforcedAesKeyGenParams = { + name: aesVal, + length: aesLength as any, + }; + try { + let key = await keyMaterial.deriveKey( + { salt, info, hash: shaVal }, + aesParams + ); + expect(key).toMatchSnapshot( + `${aesKey}_${aesLength}_${shaKey}` + ); + } catch (e) { + console.log(`${aesKey}_${aesLength}_${shaKey}`); + } } } } - } - const hmacParams: params.EnforcedHmacKeyGenParams = { - name: Authentication.Code.HMAC, - hash: SHA.Variant.SHA_512, - length: 512, - }; - let key = await HKDF.deriveKey( - { salt, info, hash: SHA.Variant.SHA_512 }, - keyMaterial, - hmacParams - ); - expect(key).toMatchSnapshot("HMAC_SHA_512"); + const hmacParams: params.EnforcedHmacKeyGenParams = { + name: Authentication.Code.HMAC, + hash: SHA.Variant.SHA_512, + length: 512, + }; + let key = await keyMaterial.deriveKey( + { salt, info, hash: SHA.Variant.SHA_512 }, + hmacParams + ); + expect(key).toMatchSnapshot("HMAC_SHA_512"); + }); }); }); diff --git a/src/kdf/__tests__/pbkdf2.test.ts b/src/kdf/__tests__/pbkdf2.test.ts index 6901c68..d0db79c 100644 --- a/src/kdf/__tests__/pbkdf2.test.ts +++ b/src/kdf/__tests__/pbkdf2.test.ts @@ -4,73 +4,139 @@ import * as params from "../../params.js"; import * as Random from "../../random.js"; import { Alg as SHA } from "../../sha/shared.js"; import * as KDF from "../index.js"; -import type { Pbkdf2KeyMaterial } from "../shared.js"; +import { Pbkdf2ProxiedKeyMaterial } from "../pbkdf.js"; +import { Pbkdf2KeyMaterial } from "../shared.js"; const { PBKDF2 } = KDF; describe("PBKDF2", () => { - let keyMaterial: Pbkdf2KeyMaterial, salt: Uint8Array; - beforeEach(async () => { - keyMaterial = await PBKDF2.generateKeyMaterial( - "raw", - new TextEncoder().encode("password") - ); + describe("Original", () => { + let proxiedKeyMaterial: Pbkdf2ProxiedKeyMaterial, + keyMaterial: Pbkdf2KeyMaterial, + salt: Uint8Array; + beforeEach(async () => { + proxiedKeyMaterial = await PBKDF2.generateKeyMaterial( + "raw", + new TextEncoder().encode("password") + ); + keyMaterial = proxiedKeyMaterial.self; - salt = await Random.Salt.generate(); - }); - it("should derive bits", async () => { - const bits = await PBKDF2.deriveBits( - { salt, hash: SHA.Variant.SHA_512 }, - keyMaterial, - 512 - ); - expect(bits.byteLength).toEqual(64); + salt = await Random.Salt.generate(); + }); + it("should derive bits", async () => { + const bits = await PBKDF2.deriveBits( + { salt, hash: SHA.Variant.SHA_512 }, + keyMaterial, + 512 + ); + expect(bits.byteLength).toEqual(64); + + await expect( + PBKDF2.deriveBits( + { salt, hash: SHA.Variant.SHA_512 }, + keyMaterial, + 511 + ) + ).rejects.toThrowError(RangeError); + }); + it("should derive keys", async () => { + for (const [aesKey, aesVal] of Object.entries(AES.Mode)) { + for (const [shaKey, shaVal] of Object.entries(SHA.Variant)) { + if (shaVal === "SHA-1") { + continue; + } + for (const aesLength of [128, 192, 256]) { + const aesParams: params.EnforcedAesKeyGenParams = { + name: aesVal, + length: aesLength as any, + }; + try { + let key = await PBKDF2.deriveKey( + { salt, hash: shaVal }, + keyMaterial, + aesParams + ); + expect(key).toMatchSnapshot( + `${aesKey}_${aesLength}_${shaKey}` + ); + } catch (e) { + console.log(`${aesKey}_${aesLength}_${shaKey}`); + } + } + } + } - await expect( - PBKDF2.deriveBits( + const hmacParams: params.EnforcedHmacKeyGenParams = { + name: Authentication.Code.HMAC, + hash: SHA.Variant.SHA_512, + length: 512, + }; + let key = await PBKDF2.deriveKey( { salt, hash: SHA.Variant.SHA_512 }, keyMaterial, - 511 - ) - ).rejects.toThrowError(RangeError); + hmacParams + ); + expect(key).toMatchSnapshot("HMAC_SHA_512"); + }); }); - it("should derive keys", async () => { - for (const [aesKey, aesVal] of Object.entries(AES.Mode)) { - for (const [shaKey, shaVal] of Object.entries(SHA.Variant)) { - if (shaVal === "SHA-1") { - continue; - } - for (const aesLength of [128, 192, 256]) { - const aesParams: params.EnforcedAesKeyGenParams = { - name: aesVal, - length: aesLength as any, - }; - try { - let key = await PBKDF2.deriveKey( - { salt, hash: shaVal }, - keyMaterial, - aesParams - ); - expect(key).toMatchSnapshot( - `${aesKey}_${aesLength}_${shaKey}` - ); - } catch (e) { - console.log(`${aesKey}_${aesLength}_${shaKey}`); + describe("Proxied", () => { + let keyMaterial: Pbkdf2ProxiedKeyMaterial, salt: Uint8Array; + beforeEach(async () => { + keyMaterial = await PBKDF2.generateKeyMaterial( + "raw", + new TextEncoder().encode("password") + ); + + salt = await Random.Salt.generate(); + }); + it("should derive bits", async () => { + const bits = await keyMaterial.deriveBits( + { salt, hash: SHA.Variant.SHA_512 }, + + 512 + ); + expect(bits.byteLength).toEqual(64); + + await expect( + keyMaterial.deriveBits({ salt, hash: SHA.Variant.SHA_512 }, 511) + ).rejects.toThrowError(RangeError); + }); + it("should derive keys", async () => { + for (const [aesKey, aesVal] of Object.entries(AES.Mode)) { + for (const [shaKey, shaVal] of Object.entries(SHA.Variant)) { + if (shaVal === "SHA-1") { + continue; + } + for (const aesLength of [128, 192, 256]) { + const aesParams: params.EnforcedAesKeyGenParams = { + name: aesVal, + length: aesLength as any, + }; + try { + let key = await keyMaterial.deriveKey( + { salt, hash: shaVal }, + aesParams + ); + expect(key).toMatchSnapshot( + `${aesKey}_${aesLength}_${shaKey}` + ); + } catch (e) { + console.log(`${aesKey}_${aesLength}_${shaKey}`); + } } } } - } - const hmacParams: params.EnforcedHmacKeyGenParams = { - name: Authentication.Code.HMAC, - hash: SHA.Variant.SHA_512, - length: 512, - }; - let key = await PBKDF2.deriveKey( - { salt, hash: SHA.Variant.SHA_512 }, - keyMaterial, - hmacParams - ); - expect(key).toMatchSnapshot("HMAC_SHA_512"); + const hmacParams: params.EnforcedHmacKeyGenParams = { + name: Authentication.Code.HMAC, + hash: SHA.Variant.SHA_512, + length: 512, + }; + let key = await keyMaterial.deriveKey( + { salt, hash: SHA.Variant.SHA_512 }, + hmacParams + ); + expect(key).toMatchSnapshot("HMAC_SHA_512"); + }); }); }); diff --git a/src/kdf/hkdf.ts b/src/kdf/hkdf.ts index 9936eb8..9b5e81d 100644 --- a/src/kdf/hkdf.ts +++ b/src/kdf/hkdf.ts @@ -2,9 +2,62 @@ * Code related to HKDF * @module */ +import type { AesCryptoKeys } from "../aes/index.js"; +import { HmacCryptoKey } from "../hmac/index.js"; import * as params from "../params.js"; +import * as proxy from "../proxy.js"; import { Alg, HkdfKeyMaterial, KdfShared } from "./shared.js"; +export interface HkdfProxiedKeyMaterial + extends proxy.ProxiedCryptoKey { + deriveKey( + algorithm: Omit, + derivedKeyType: + | params.EnforcedAesKeyGenParams + | params.EnforcedHmacKeyGenParams, + extractable?: boolean, + keyUsages?: KeyUsage[] + ): Promise; + + deriveBits( + algorithm: Omit, + length: number + ): Promise; + exportKey: (format: KeyFormat) => Promise; +} + +const handler: ProxyHandler = { + get(target: HkdfKeyMaterial, prop: string) { + switch (prop) { + case "self": + return target; + case "deriveKey": + return ( + algorithm: Omit, + derivedKeyType: + | params.EnforcedAesKeyGenParams + | params.EnforcedHmacKeyGenParams, + extractable?: boolean, + keyUsages?: KeyUsage[] + ) => + deriveKey( + algorithm, + target, + derivedKeyType, + extractable, + keyUsages + ); + case "deriveBits": + return ( + algorithm: Omit, + length: number + ) => deriveBits(algorithm, target, length); + } + + return Reflect.get(target, prop); + }, +}; + /** * Generate key material for deriving * @example @@ -12,18 +65,23 @@ import { Alg, HkdfKeyMaterial, KdfShared } from "./shared.js"; * const keyMaterial = await HKDF.generateKeyMaterial("raw", new TextEncoder().encode("lots_of_entropy")); * ``` */ -export const generateKeyMaterial = ( +export const generateKeyMaterial = async ( format: KeyFormat, key: BufferSource, extractable?: boolean -) => - KdfShared.generateKeyMaterial( +): Promise => { + const keyMaterial = await KdfShared.generateKeyMaterial( format, key, Alg.Variant.HKDF, extractable ); + return proxy.proxifyKey(handler)( + keyMaterial + ); +}; + /** * Derive a shared key from HKDF key material * @example @@ -40,6 +98,19 @@ export const generateKeyMaterial = ( * keyMaterial, * hmacParams * ); + * @example + * ```ts + * const hmacParams: params.EnforcedHmacKeyGenParams = { + * name: Authentication.Alg.Code.HMAC, + * hash: SHA.Alg.Variant.SHA_512, + * length: 512, + * }; + * const salt = await Random.Salt.generate(); + * const info = await Random.getValues(6); + * let key = await keyMaterial.deriveKey( + * { salt, info, hash: "SHA-512" }, + * hmacParams + * ); * ``` */ export const deriveKey = ( @@ -74,6 +145,15 @@ export const deriveKey = ( * 128 * ); * ``` + * @example + * ```ts + * const salt = await Random.Salt.generate(); + * const info = await Random.getValues(6); + * const bits = await keyMaterial.deriveBits( + * { salt, info, hash: "SHA-512" }, + * 128 + * ); + * ``` */ export const deriveBits = ( algorithm: Omit, diff --git a/src/kdf/pbkdf.ts b/src/kdf/pbkdf.ts index ff6bf1e..082b425 100644 --- a/src/kdf/pbkdf.ts +++ b/src/kdf/pbkdf.ts @@ -2,10 +2,65 @@ * Code related to PBKDF2 * @module */ +import type { AesCryptoKeys } from "../aes/index.js"; +import { HmacCryptoKey } from "../hmac/index.js"; import * as params from "../params.js"; +import * as proxy from "../proxy.js"; import { Alg as SHA } from "../sha/shared.js"; import { Alg, KdfShared, Pbkdf2KeyMaterial } from "./shared.js"; +export interface Pbkdf2ProxiedKeyMaterial + extends proxy.ProxiedCryptoKey { + deriveKey( + algorithm: Omit, + derivedKeyType: + | params.EnforcedAesKeyGenParams + | params.EnforcedHmacKeyGenParams, + extractable?: boolean, + keyUsages?: KeyUsage[] + ): Promise; + deriveBits( + algorithm: Omit, + length: number + ): Promise; + exportKey: (format: KeyFormat) => Promise; +} + +const handler: ProxyHandler = { + get(target: Pbkdf2KeyMaterial, prop: string) { + switch (prop) { + case "self": + return target; + case "deriveKey": + return ( + algorithm: Omit< + params.EnforcedPbkdf2Params, + "name" | "iterations" + >, + derivedKeyType: + | params.EnforcedAesKeyGenParams + | params.EnforcedHmacKeyGenParams, + extractable?: boolean, + keyUsages?: KeyUsage[] + ) => + deriveKey( + algorithm, + target, + derivedKeyType, + extractable, + keyUsages + ); + case "deriveBits": + return ( + algorithm: Omit, + length: number + ) => deriveBits(algorithm, target, length); + } + + return Reflect.get(target, prop); + }, +}; + const hashIterations: Record = { "SHA-256": 310_000, "SHA-384": 310_000, @@ -19,18 +74,23 @@ const hashIterations: Record = { * const keyMaterial = await PBKDF2.generateKeyMaterial("raw", new TextEncoder().encode("could_be_a_little_entropy")); * ``` */ -export const generateKeyMaterial = ( +export const generateKeyMaterial = async ( format: KeyFormat, key: BufferSource, extractable?: boolean -) => - KdfShared.generateKeyMaterial( +): Promise => { + const keyMaterial = await KdfShared.generateKeyMaterial( format, key, Alg.Variant.PBKDF2, extractable ); + return proxy.proxifyKey( + handler + )(keyMaterial); +}; + /** * Derive a shared key from PBKDF2 key material * @example @@ -41,7 +101,7 @@ export const generateKeyMaterial = ( * length: 512, * }; * let key = await PBKDF2.deriveKey( - * { hash: "SHA-512" }, + * { hash: "SHA512" }, * keyMaterial, * hmacParams * ); From e3aae02907f7c4741d39dcfc3c274f0aa67c0aa9 Mon Sep 17 00:00:00 2001 From: Neal Fennimore Date: Mon, 29 May 2023 20:35:21 -0400 Subject: [PATCH 12/20] Update RSA OAEP to be proxied --- .../__snapshots__/rsa_oaep.test.ts.snap | 49 +++- src/rsa/__tests__/rsa_oaep.test.ts | 210 ++++++++++++------ src/rsa/rsa_oaep.ts | 146 +++++++++++- src/rsa/shared.ts | 46 +++- 4 files changed, 370 insertions(+), 81 deletions(-) diff --git a/src/rsa/__tests__/__snapshots__/rsa_oaep.test.ts.snap b/src/rsa/__tests__/__snapshots__/rsa_oaep.test.ts.snap index 05313c9..ca8cc0a 100644 --- a/src/rsa/__tests__/__snapshots__/rsa_oaep.test.ts.snap +++ b/src/rsa/__tests__/__snapshots__/rsa_oaep.test.ts.snap @@ -1,6 +1,53 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`RSA_OAEP should generate key 1`] = ` +exports[`RSA_OAEP Original should generate key 1`] = ` +Object { + "privateKey": CryptoKey { + Symbol(kKeyObject): PrivateKeyObject { + Symbol(kKeyType): "private", + }, + Symbol(kAlgorithm): Object { + "hash": Object { + "name": "SHA-512", + }, + "modulusLength": 4096, + "name": "RSA-OAEP", + "publicExponent": Uint8Array [ + 1, + 0, + 1, + ], + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "decrypt", + ], + }, + "publicKey": CryptoKey { + Symbol(kKeyObject): PublicKeyObject { + Symbol(kKeyType): "public", + }, + Symbol(kAlgorithm): Object { + "hash": Object { + "name": "SHA-512", + }, + "modulusLength": 4096, + "name": "RSA-OAEP", + "publicExponent": Uint8Array [ + 1, + 0, + 1, + ], + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "encrypt", + ], + }, +} +`; + +exports[`RSA_OAEP Proxied should generate key 1`] = ` Object { "privateKey": CryptoKey { Symbol(kKeyObject): PrivateKeyObject { diff --git a/src/rsa/__tests__/rsa_oaep.test.ts b/src/rsa/__tests__/rsa_oaep.test.ts index ff7e1b9..6b3f8dc 100644 --- a/src/rsa/__tests__/rsa_oaep.test.ts +++ b/src/rsa/__tests__/rsa_oaep.test.ts @@ -1,81 +1,155 @@ import * as AES from "../../aes/index.js"; import * as Random from "../../random.js"; import * as RSA from "../index.js"; +import { RsaOaepProxiedPubCryptoKey } from "../shared.js"; const { RSA_OAEP } = RSA; describe("RSA_OAEP", () => { - let label: BufferSource; - beforeEach(async () => { - label = await Random.getValues(8); - }); - it("should generate key", async () => { - const key = await RSA_OAEP.generateKey(); - expect(key).toMatchSnapshot(); - }); + describe("Original", () => { + let label: BufferSource; + beforeEach(async () => { + label = await Random.getValues(8); + }); + it("should generate key", async () => { + const key = await RSA_OAEP.generateKey(); + expect(key).toMatchSnapshot(); + }); - it("should import and export key", async () => { - const keyPair = await RSA_OAEP.generateKey(); - const jwk = await RSA_OAEP.exportKey("jwk", keyPair.publicKey); - const pubKey = (await RSA_OAEP.importKey( - "jwk", - jwk, - { hash: "SHA-512" }, - false, - ["encrypt"] - )) as RSA.RsaOaepPubCryptoKey; - const text = encode("a message"); - const ciphertext = await RSA_OAEP.encrypt(undefined, pubKey, text); - const plaintext = await RSA_OAEP.decrypt( - undefined, - keyPair.privateKey, - ciphertext - ); - expect(decode(plaintext)).toEqual(decode(text)); - }); - it("should encrypt and decrypt", async () => { - const keyPair = await RSA_OAEP.generateKey(); - const text = encode("a message"); - const ciphertext = await RSA_OAEP.encrypt( - { label }, - keyPair.publicKey, - text - ); - const plaintext = await RSA_OAEP.decrypt( - { label }, - keyPair.privateKey, - ciphertext - ); - expect(decode(plaintext)).toEqual(decode(text)); + it("should import and export key", async () => { + const keyPair = await RSA_OAEP.generateKey(); + const jwk = await RSA_OAEP.exportKey("jwk", keyPair.publicKey.self); + const pubKey = (await RSA_OAEP.importKey( + "jwk", + jwk, + { hash: "SHA-512" }, + false, + ["encrypt"] + )) as RsaOaepProxiedPubCryptoKey; + const text = encode("a message"); + const ciphertext = await RSA_OAEP.encrypt( + undefined, + pubKey.self, + text + ); + const plaintext = await RSA_OAEP.decrypt( + undefined, + keyPair.privateKey.self, + ciphertext + ); + expect(decode(plaintext)).toEqual(decode(text)); + }); + it("should encrypt and decrypt", async () => { + const keyPair = await RSA_OAEP.generateKey(); + const text = encode("a message"); + const ciphertext = await RSA_OAEP.encrypt( + { label }, + keyPair.publicKey.self, + text + ); + const plaintext = await RSA_OAEP.decrypt( + { label }, + keyPair.privateKey.self, + ciphertext + ); + expect(decode(plaintext)).toEqual(decode(text)); + }); + it("should wrap and unwrap key", async () => { + const aesKey = await AES.AES_CBC.generateKey(); + const keyPair = await RSA_OAEP.generateKey( + { + hash: "SHA-512", + modulusLength: 4096, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + }, + true, + ["wrapKey", "unwrapKey"] + ); + + const wrappedAesKey = await RSA_OAEP.wrapKey( + "raw", + aesKey.self, + keyPair.publicKey.self, + {} + ); + const unwrappedAesKey = (await RSA_OAEP.unwrapKey( + "raw", + wrappedAesKey, + { name: AES.Alg.Mode.AES_CBC }, + keyPair.privateKey.self, + {} + )) as AES.AesCbcCryptoKey; + + expect(await AES.AES_CBC.exportKey("jwk", aesKey.self)).toEqual( + await AES.AES_CBC.exportKey("jwk", unwrappedAesKey) + ); + }); }); - it("should wrap and unwrap key", async () => { - const aesKey = await AES.AES_CBC.generateKey(); - const keyPair = await RSA_OAEP.generateKey( - { - hash: "SHA-512", - modulusLength: 4096, - publicExponent: new Uint8Array([0x01, 0x00, 0x01]), - }, - true, - ["wrapKey", "unwrapKey"] - ); + describe("Proxied", () => { + let label: BufferSource; + beforeEach(async () => { + label = await Random.getValues(8); + }); + it("should generate key", async () => { + const key = await RSA_OAEP.generateKey(); + expect(key).toMatchSnapshot(); + }); + + it("should import and export key", async () => { + const keyPair = await RSA_OAEP.generateKey(); + const jwk = await keyPair.publicKey.exportKey("jwk"); + const pubKey = (await RSA_OAEP.importKey( + "jwk", + jwk, + { hash: "SHA-512" }, + false, + ["encrypt"] + )) as RsaOaepProxiedPubCryptoKey; + const text = encode("a message"); + const ciphertext = await pubKey.encrypt({ label }, text); + const plaintext = await keyPair.privateKey.decrypt( + { label }, + ciphertext + ); + expect(decode(plaintext)).toEqual(decode(text)); + }); + it("should encrypt and decrypt", async () => { + const keyPair = await RSA_OAEP.generateKey(); + const text = encode("a message"); + const ciphertext = await keyPair.publicKey.encrypt({ label }, text); + const plaintext = await keyPair.privateKey.decrypt( + { label }, + ciphertext + ); + expect(decode(plaintext)).toEqual(decode(text)); + }); + it("should wrap and unwrap key", async () => { + const aesKey = await AES.AES_CBC.generateKey(); + const keyPair = await RSA_OAEP.generateKey( + { + hash: "SHA-512", + modulusLength: 4096, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + }, + true, + ["wrapKey", "unwrapKey"] + ); - const wrappedAesKey = await RSA_OAEP.wrapKey( - "raw", - aesKey, - keyPair.publicKey, - {} - ); - const unwrappedAesKey = (await RSA_OAEP.unwrapKey( - "raw", - wrappedAesKey, - { name: AES.Alg.Mode.AES_CBC }, - keyPair.privateKey, - {} - )) as AES.AesCbcCryptoKey; + const wrappedAesKey = await keyPair.publicKey.wrapKey( + "raw", + aesKey.self, + {} + ); + const unwrappedAesKey = (await keyPair.privateKey.unwrapKey( + "raw", + wrappedAesKey, + { name: AES.Alg.Mode.AES_CBC }, + {} + )) as AES.AesCbcCryptoKey; - expect(await AES.AES_CBC.exportKey("jwk", aesKey)).toEqual( - await AES.AES_CBC.exportKey("jwk", unwrappedAesKey) - ); + expect(await AES.AES_CBC.exportKey("jwk", aesKey.self)).toEqual( + await AES.AES_CBC.exportKey("jwk", unwrappedAesKey) + ); + }); }); }); diff --git a/src/rsa/rsa_oaep.ts b/src/rsa/rsa_oaep.ts index 459551f..cdeddf8 100644 --- a/src/rsa/rsa_oaep.ts +++ b/src/rsa/rsa_oaep.ts @@ -5,16 +5,90 @@ import { getKeyUsagePairsByAlg, KeyUsagePairs } from "../key_usages.js"; import * as params from "../params.js"; +import * as proxy from "../proxy.js"; import { Alg as SHA } from "../sha/shared.js"; import * as WebCrypto from "../webcrypto.js"; import { Alg, RsaOaepCryptoKeyPair, RsaOaepPrivCryptoKey, + RsaOaepProxiedCryptoKeyPair, + RsaOaepProxiedPrivCryptoKey, + RsaOaepProxiedPubCryptoKey, RsaOaepPubCryptoKey, RsaShared, } from "./shared.js"; +const handlers: proxy.ProxyKeyPairHandlers< + RsaOaepPrivCryptoKey, + RsaOaepPubCryptoKey +> = { + privHandler: { + get(target: RsaOaepPrivCryptoKey, prop: string) { + switch (prop) { + case "self": + return target; + case "decrypt": + return ( + algorithm: Omit, + data: BufferSource + ) => decrypt(algorithm, target, data); + case "unwrapKey": + return ( + format: KeyFormat, + wrappedKey: BufferSource, + wrappedKeyAlgorithm: params.EnforcedImportParams, + unwrappingKeyAlgorithm: Omit< + params.EnforcedRsaOaepParams, + "name" + >, + extractable?: boolean, + keyUsages?: KeyUsagePairs + ) => + unwrapKey( + format, + wrappedKey, + wrappedKeyAlgorithm, + target, + unwrappingKeyAlgorithm, + extractable, + keyUsages + ); + case "exportKey": + return (format: KeyFormat) => exportKey(format, target); + } + + return Reflect.get(target, prop); + }, + }, + pubHandler: { + get(target: RsaOaepPubCryptoKey, prop: string) { + switch (prop) { + case "self": + return target; + case "encrypt": + return ( + algorithm: Omit, + data: BufferSource + ) => encrypt(algorithm, target, data); + case "wrapKey": + return ( + format: KeyFormat, + key: CryptoKey, + wrapAlgorithm?: Omit< + params.EnforcedRsaOaepParams, + "name" + > + ) => wrapKey(format, key, target, wrapAlgorithm); + case "exportKey": + return (format: KeyFormat) => exportKey(format, target); + } + + return Reflect.get(target, prop); + }, + }, +}; + /** * Generate a new RSA_OAEP keypair * @example @@ -30,8 +104,8 @@ export const generateKey = async ( }, extractable?: boolean, keyUsages?: KeyUsage[] -) => - (await RsaShared.generateKey( +): Promise => { + const keyPair = (await RsaShared.generateKey( { ...algorithm, name: Alg.Variant.RSA_OAEP, @@ -40,6 +114,15 @@ export const generateKey = async ( keyUsages )) as RsaOaepCryptoKeyPair; + return proxy.proxifyKeyPair< + RsaOaepCryptoKeyPair, + RsaOaepPrivCryptoKey, + RsaOaepProxiedPrivCryptoKey, + RsaOaepPubCryptoKey, + RsaOaepProxiedPubCryptoKey + >(handlers)(keyPair); +}; + /** * Import an RSA_OAEP public or private key * @example @@ -53,8 +136,8 @@ export const importKey = async ( algorithm: Omit, extractable?: boolean, keyUsages?: KeyUsage[] -): Promise => - await RsaShared.importKey( +): Promise => { + const importedKey = await RsaShared.importKey( format, key, { ...algorithm, name: Alg.Variant.RSA_OAEP }, @@ -62,11 +145,29 @@ export const importKey = async ( keyUsages ); + if (importedKey.type === "private") { + return proxy.proxifyPrivKey< + RsaOaepPrivCryptoKey, + RsaOaepProxiedPrivCryptoKey + >(handlers.privHandler)(importedKey as RsaOaepPrivCryptoKey); + } else { + return proxy.proxifyPubKey< + RsaOaepPubCryptoKey, + RsaOaepProxiedPubCryptoKey + >(handlers.pubHandler)(importedKey as RsaOaepPubCryptoKey); + } +}; + /** * Export an RSA_OAEP public or private key * @example * ```ts - * const pubKeyJwk = await RSA_OAEP.importKey("jwk", keyPair.publicKey); + * const pubKeyJwk = await RSA_OAEP.exportKey("jwk", keyPair.publicKey.self); + * ``` + * @example + * ```ts + * const pubKeyJwk = await keyPair.publicKey.exportKey("jwk"); + * const privKeyJwk = await keyPair.privateKey.exportKey("jwk"); * ``` */ export const exportKey = async ( @@ -79,7 +180,12 @@ export const exportKey = async ( * @example * ```ts * const message = new TextEncoder().encode("a message"); - * const data = await RSA_OAEP.encrypt({label}, keyPair.publicKey, message); + * const data = await RSA_OAEP.encrypt({label}, keyPair.publicKey.self, message); + * ``` + * @example + * ```ts + * const message = new TextEncoder().encode("a message"); + * const data = await keyPair.publicKey.encrypt({label}, message); * ``` */ export async function encrypt( @@ -101,7 +207,11 @@ export async function encrypt( * Decrypt with an RSA_OAEP private key * @example * ```ts - * const data = await RSA_OAEP.decrypt({label}, keyPair.privateKey, data); + * const data = await RSA_OAEP.decrypt({label}, keyPair.privateKey.self, data); + * ``` + * @example + * ```ts + * const data = await keyPair.privateKey.decrypt({label}, data); * ``` */ @@ -126,7 +236,13 @@ export async function decrypt( * ```ts * const kek = await RSA_OAEP.generateKey(undefined, true, ['wrapKey', 'unwrapKey']); * const dek = await RSA_OAEP.generateKey(); - * const wrappedKey = await RSA_OAEP.wrapKey("raw", dek, kek, {iv}); + * const wrappedKey = await RSA_OAEP.wrapKey("raw", dek.self, kek.self, {iv}); + * ``` + * @example + * ```ts + * const kek = await RSA_OAEP.generateKey(undefined, true, ['wrapKey', 'unwrapKey']); + * const dek = await RSA_OAEP.generateKey(); + * const wrappedKey = await kek.wrapKey("raw", dek.self, {iv}); * ``` */ export async function wrapKey( @@ -151,12 +267,20 @@ export async function wrapKey( * Unwrap a wrapped key using the key encryption key * @example * ```ts - * const wrappedKey = await RSA_OAEP.wrapKey("raw", dek, kek); - * const unwrappedkey = await RSA_OAEP.unwrapKey( + * const wrappedKey = await RSA_OAEP.wrapKey("raw", dek.self, kek.self); + * const unwrappedKey = await RSA_OAEP.unwrapKey( + * "raw", + * wrappedKey, + * { name: Alg.Mode.RSA_OAEP }, + * kek.self, + * ); + * ``` + * ```ts + * const wrappedKey = await kek.wrapKey("raw", dek.self); + * const unwrappedKey = await kek.unwrapKey( * "raw", * wrappedKey, * { name: Alg.Mode.RSA_OAEP }, - * kek, * ); * ``` */ diff --git a/src/rsa/shared.ts b/src/rsa/shared.ts index da24679..37c5644 100644 --- a/src/rsa/shared.ts +++ b/src/rsa/shared.ts @@ -3,8 +3,9 @@ * @module */ -import { getKeyUsagePairsByAlg } from "../key_usages.js"; +import { getKeyUsagePairsByAlg, KeyUsagePairs } from "../key_usages.js"; import * as params from "../params.js"; +import * as proxy from "../proxy.js"; import * as WebCrypto from "../webcrypto.js"; export interface RsaOaepPubCryptoKey extends CryptoKey { @@ -129,3 +130,46 @@ export namespace RsaShared { return await WebCrypto.verify(algorithm, key, signature, data); } } + +export interface RsaOaepProxiedPubCryptoKey + extends proxy.ProxiedPubCryptoKey { + encrypt: ( + algorithm: Omit, + data: BufferSource + ) => Promise; + wrapKey: ( + format: KeyFormat, + key: CryptoKey, + wrapAlgorithm?: Omit + ) => Promise; + + exportKey: (format: KeyFormat) => Promise; +} +export interface RsaOaepProxiedPrivCryptoKey + extends proxy.ProxiedPrivCryptoKey { + decrypt: ( + algorithm: Omit, + data: BufferSource + ) => Promise; + + unwrapKey: ( + format: KeyFormat, + wrappedKey: BufferSource, + wrappedKeyAlgorithm: params.EnforcedImportParams, + // unwrappingKey: RsaOaepPrivCryptoKey, + unwrappingKeyAlgorithm: Omit, + extractable?: boolean, + keyUsages?: KeyUsagePairs + ) => Promise; + + exportKey: (format: KeyFormat) => Promise; +} + +export interface RsaOaepProxiedCryptoKeyPair + extends proxy.ProxiedCryptoKeyPair< + RsaOaepCryptoKeyPair, + RsaOaepPrivCryptoKey, + RsaOaepProxiedPrivCryptoKey, + RsaOaepPubCryptoKey, + RsaOaepProxiedPubCryptoKey + > {} From da52323eb18e08e08d2235ab6c47b74e856079b8 Mon Sep 17 00:00:00 2001 From: Neal Fennimore Date: Mon, 29 May 2023 20:44:37 -0400 Subject: [PATCH 13/20] Update RSA PSS to be proxied --- .../__snapshots__/rsa_pss.test.ts.snap | 94 ++++++++++++++ src/rsa/__tests__/rsa_pss.test.ts | 121 +++++++++++++----- src/rsa/rsa_pss.ts | 73 ++++++++++- src/rsa/shared.ts | 26 ++++ 4 files changed, 275 insertions(+), 39 deletions(-) diff --git a/src/rsa/__tests__/__snapshots__/rsa_pss.test.ts.snap b/src/rsa/__tests__/__snapshots__/rsa_pss.test.ts.snap index 4cecb25..a49360e 100644 --- a/src/rsa/__tests__/__snapshots__/rsa_pss.test.ts.snap +++ b/src/rsa/__tests__/__snapshots__/rsa_pss.test.ts.snap @@ -1,5 +1,99 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`RSA_PSS Original should generate key 1`] = ` +Object { + "privateKey": CryptoKey { + Symbol(kKeyObject): PrivateKeyObject { + Symbol(kKeyType): "private", + }, + Symbol(kAlgorithm): Object { + "hash": Object { + "name": "SHA-512", + }, + "modulusLength": 4096, + "name": "RSA-PSS", + "publicExponent": Uint8Array [ + 1, + 0, + 1, + ], + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "sign", + ], + }, + "publicKey": CryptoKey { + Symbol(kKeyObject): PublicKeyObject { + Symbol(kKeyType): "public", + }, + Symbol(kAlgorithm): Object { + "hash": Object { + "name": "SHA-512", + }, + "modulusLength": 4096, + "name": "RSA-PSS", + "publicExponent": Uint8Array [ + 1, + 0, + 1, + ], + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "verify", + ], + }, +} +`; + +exports[`RSA_PSS Proxied should generate key 1`] = ` +Object { + "privateKey": CryptoKey { + Symbol(kKeyObject): PrivateKeyObject { + Symbol(kKeyType): "private", + }, + Symbol(kAlgorithm): Object { + "hash": Object { + "name": "SHA-512", + }, + "modulusLength": 4096, + "name": "RSA-PSS", + "publicExponent": Uint8Array [ + 1, + 0, + 1, + ], + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "sign", + ], + }, + "publicKey": CryptoKey { + Symbol(kKeyObject): PublicKeyObject { + Symbol(kKeyType): "public", + }, + Symbol(kAlgorithm): Object { + "hash": Object { + "name": "SHA-512", + }, + "modulusLength": 4096, + "name": "RSA-PSS", + "publicExponent": Uint8Array [ + 1, + 0, + 1, + ], + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "verify", + ], + }, +} +`; + exports[`RSA_PSS should generate key 1`] = ` Object { "privateKey": CryptoKey { diff --git a/src/rsa/__tests__/rsa_pss.test.ts b/src/rsa/__tests__/rsa_pss.test.ts index 6cceb29..c6e3054 100644 --- a/src/rsa/__tests__/rsa_pss.test.ts +++ b/src/rsa/__tests__/rsa_pss.test.ts @@ -3,42 +3,93 @@ import * as RSA from "../index.js"; const { RSA_PSS } = RSA; describe("RSA_PSS", () => { - it("should generate key", async () => { - const key = await RSA_PSS.generateKey(); - expect(key).toMatchSnapshot(); - }); + describe("Original", () => { + it("should generate key", async () => { + const key = await RSA_PSS.generateKey(); + expect(key.self).toMatchSnapshot(); + }); + + it("should import and export key", async () => { + const keyPair = await RSA_PSS.generateKey(); + + let jwk = await RSA_PSS.exportKey("jwk", keyPair.publicKey.self); + const pubKey = await RSA_PSS.importKey( + "jwk", + jwk, + { hash: "SHA-512" }, + true, + ["verify"] + ); + + expect(jwk).toEqual(await RSA_PSS.exportKey("jwk", pubKey.self)); + + jwk = await RSA_PSS.exportKey("jwk", keyPair.privateKey.self); + const privKey = await RSA_PSS.importKey( + "jwk", + jwk, + { hash: "SHA-512" }, + true, + ["sign"] + ); - it("should import and export key", async () => { - const keyPair = await RSA_PSS.generateKey(); - - let jwk = await RSA_PSS.exportKey("jwk", keyPair.publicKey); - const pubKey = await RSA_PSS.importKey( - "jwk", - jwk, - { hash: "SHA-512" }, - true, - ["verify"] - ); - - expect(jwk).toEqual(await RSA_PSS.exportKey("jwk", pubKey)); - - jwk = await RSA_PSS.exportKey("jwk", keyPair.privateKey); - const privKey = await RSA_PSS.importKey( - "jwk", - jwk, - { hash: "SHA-512" }, - true, - ["sign"] - ); - - expect(jwk).toEqual(await RSA_PSS.exportKey("jwk", privKey)); + expect(jwk).toEqual(await RSA_PSS.exportKey("jwk", privKey.self)); + }); + it("should sign and verify", async () => { + const keyPair = await RSA_PSS.generateKey(); + const text = encode("a message"); + const signature = await RSA_PSS.sign( + 16, + keyPair.privateKey.self, + text + ); + expect( + await RSA_PSS.verify( + 16, + keyPair.publicKey.self, + signature, + text + ) + ).toEqual(true); + }); }); - it("should sign and verify", async () => { - const keyPair = await RSA_PSS.generateKey(); - const text = encode("a message"); - const signature = await RSA_PSS.sign(16, keyPair.privateKey, text); - expect( - await RSA_PSS.verify(16, keyPair.publicKey, signature, text) - ).toEqual(true); + describe("Proxied", () => { + it("should generate key", async () => { + const key = await RSA_PSS.generateKey(); + expect(key).toMatchSnapshot(); + }); + + it("should import and export key", async () => { + const keyPair = await RSA_PSS.generateKey(); + + let jwk = await keyPair.publicKey.exportKey("jwk"); + const pubKey = await RSA_PSS.importKey( + "jwk", + jwk, + { hash: "SHA-512" }, + true, + ["verify"] + ); + + expect(jwk).toEqual(await pubKey.exportKey("jwk")); + + jwk = await keyPair.privateKey.exportKey("jwk"); + const privKey = await RSA_PSS.importKey( + "jwk", + jwk, + { hash: "SHA-512" }, + true, + ["sign"] + ); + + expect(jwk).toEqual(await privKey.exportKey("jwk")); + }); + it("should sign and verify", async () => { + const keyPair = await RSA_PSS.generateKey(); + const text = encode("a message"); + const signature = await keyPair.privateKey.sign(16, text); + expect(await keyPair.publicKey.verify(16, signature, text)).toEqual( + true + ); + }); }); }); diff --git a/src/rsa/rsa_pss.ts b/src/rsa/rsa_pss.ts index b561426..6f465f3 100644 --- a/src/rsa/rsa_pss.ts +++ b/src/rsa/rsa_pss.ts @@ -4,15 +4,58 @@ */ import * as params from "../params.js"; +import * as proxy from "../proxy.js"; import { Alg as SHA } from "../sha/shared.js"; import { Alg, RsaPssCryptoKeyPair, RsaPssPrivCryptoKey, + RsaPssProxiedCryptoKeyPair, + RsaPssProxiedPrivCryptoKey, + RsaPssProxiedPubCryptoKey, RsaPssPubCryptoKey, RsaShared, } from "./shared.js"; +const handlers: proxy.ProxyKeyPairHandlers< + RsaPssPrivCryptoKey, + RsaPssPubCryptoKey +> = { + privHandler: { + get(target: RsaPssPrivCryptoKey, prop: string) { + switch (prop) { + case "self": + return target; + case "sign": + return (saltLength: number, data: BufferSource) => + sign(saltLength, target, data); + case "exportKey": + return (format: KeyFormat) => exportKey(format, target); + } + + return Reflect.get(target, prop); + }, + }, + pubHandler: { + get(target: RsaPssPubCryptoKey, prop: string) { + switch (prop) { + case "self": + return target; + case "verify": + return ( + saltLength: number, + signature: BufferSource, + data: BufferSource + ) => verify(saltLength, target, signature, data); + case "exportKey": + return (format: KeyFormat) => exportKey(format, target); + } + + return Reflect.get(target, prop); + }, + }, +}; + /** * Generate a new RSA_PSS keypair * @example @@ -28,8 +71,8 @@ export const generateKey = async ( }, extractable?: boolean, keyUsages?: KeyUsage[] -) => - (await RsaShared.generateKey( +): Promise => { + const keyPair = (await RsaShared.generateKey( { ...algorithm, name: Alg.Variant.RSA_PSS, @@ -38,6 +81,15 @@ export const generateKey = async ( keyUsages )) as RsaPssCryptoKeyPair; + return proxy.proxifyKeyPair< + RsaPssCryptoKeyPair, + RsaPssPrivCryptoKey, + RsaPssProxiedPrivCryptoKey, + RsaPssPubCryptoKey, + RsaPssProxiedPubCryptoKey + >(handlers)(keyPair); +}; + /** * Import an RSA_PSS public or private key * @example @@ -51,8 +103,8 @@ export const importKey = async ( algorithm: Omit, extractable?: boolean, keyUsages?: KeyUsage[] -): Promise => - await RsaShared.importKey( +): Promise => { + const importedKey = await RsaShared.importKey( format, key, { ...algorithm, name: Alg.Variant.RSA_PSS }, @@ -60,6 +112,19 @@ export const importKey = async ( keyUsages ); + if (importedKey.type === "private") { + return proxy.proxifyPrivKey< + RsaPssPrivCryptoKey, + RsaPssProxiedPrivCryptoKey + >(handlers.privHandler)(importedKey as RsaPssPrivCryptoKey); + } else { + return proxy.proxifyPubKey< + RsaPssPubCryptoKey, + RsaPssProxiedPubCryptoKey + >(handlers.pubHandler)(importedKey as RsaPssPubCryptoKey); + } +}; + /** * Export an RSA_PSS public or private key * @example diff --git a/src/rsa/shared.ts b/src/rsa/shared.ts index 37c5644..7b477ad 100644 --- a/src/rsa/shared.ts +++ b/src/rsa/shared.ts @@ -173,3 +173,29 @@ export interface RsaOaepProxiedCryptoKeyPair RsaOaepPubCryptoKey, RsaOaepProxiedPubCryptoKey > {} + +export interface RsaPssProxiedPubCryptoKey + extends proxy.ProxiedPubCryptoKey { + verify: ( + saltLength: number, + signature: BufferSource, + data: BufferSource + ) => Promise; + + exportKey: (format: KeyFormat) => Promise; +} +export interface RsaPssProxiedPrivCryptoKey + extends proxy.ProxiedPrivCryptoKey { + sign: (saltLength: number, data: BufferSource) => Promise; + + exportKey: (format: KeyFormat) => Promise; +} + +export interface RsaPssProxiedCryptoKeyPair + extends proxy.ProxiedCryptoKeyPair< + RsaPssCryptoKeyPair, + RsaPssPrivCryptoKey, + RsaPssProxiedPrivCryptoKey, + RsaPssPubCryptoKey, + RsaPssProxiedPubCryptoKey + > {} From 4f07863a3157fb3579174652ced3a1c0c048e983 Mon Sep 17 00:00:00 2001 From: Neal Fennimore Date: Mon, 29 May 2023 22:50:15 -0400 Subject: [PATCH 14/20] Update RSASSA PKCS1 v1.5 to be proxied --- .../rsassa_pkcs1_v1_5.test.ts.snap | 94 +++++++++++++ src/rsa/__tests__/rsassa_pkcs1_v1_5.test.ts | 132 +++++++++++++----- src/rsa/rsassa_pkcs1_v1_5.ts | 70 +++++++++- src/rsa/shared.ts | 22 +++ 4 files changed, 276 insertions(+), 42 deletions(-) diff --git a/src/rsa/__tests__/__snapshots__/rsassa_pkcs1_v1_5.test.ts.snap b/src/rsa/__tests__/__snapshots__/rsassa_pkcs1_v1_5.test.ts.snap index 7457d06..dda1455 100644 --- a/src/rsa/__tests__/__snapshots__/rsassa_pkcs1_v1_5.test.ts.snap +++ b/src/rsa/__tests__/__snapshots__/rsassa_pkcs1_v1_5.test.ts.snap @@ -1,5 +1,99 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`RSASSA_PKCS1_v1_5 Original should generate key 1`] = ` +Object { + "privateKey": CryptoKey { + Symbol(kKeyObject): PrivateKeyObject { + Symbol(kKeyType): "private", + }, + Symbol(kAlgorithm): Object { + "hash": Object { + "name": "SHA-512", + }, + "modulusLength": 4096, + "name": "RSASSA-PKCS1-v1_5", + "publicExponent": Uint8Array [ + 1, + 0, + 1, + ], + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "sign", + ], + }, + "publicKey": CryptoKey { + Symbol(kKeyObject): PublicKeyObject { + Symbol(kKeyType): "public", + }, + Symbol(kAlgorithm): Object { + "hash": Object { + "name": "SHA-512", + }, + "modulusLength": 4096, + "name": "RSASSA-PKCS1-v1_5", + "publicExponent": Uint8Array [ + 1, + 0, + 1, + ], + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "verify", + ], + }, +} +`; + +exports[`RSASSA_PKCS1_v1_5 Proxied should generate key 1`] = ` +Object { + "privateKey": CryptoKey { + Symbol(kKeyObject): PrivateKeyObject { + Symbol(kKeyType): "private", + }, + Symbol(kAlgorithm): Object { + "hash": Object { + "name": "SHA-512", + }, + "modulusLength": 4096, + "name": "RSASSA-PKCS1-v1_5", + "publicExponent": Uint8Array [ + 1, + 0, + 1, + ], + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "sign", + ], + }, + "publicKey": CryptoKey { + Symbol(kKeyObject): PublicKeyObject { + Symbol(kKeyType): "public", + }, + Symbol(kAlgorithm): Object { + "hash": Object { + "name": "SHA-512", + }, + "modulusLength": 4096, + "name": "RSASSA-PKCS1-v1_5", + "publicExponent": Uint8Array [ + 1, + 0, + 1, + ], + }, + Symbol(kExtractable): true, + Symbol(kKeyUsages): Array [ + "verify", + ], + }, +} +`; + exports[`RSASSA_PKCS1_v1_5 should generate key 1`] = ` Object { "privateKey": CryptoKey { diff --git a/src/rsa/__tests__/rsassa_pkcs1_v1_5.test.ts b/src/rsa/__tests__/rsassa_pkcs1_v1_5.test.ts index c0740ae..56bbbae 100644 --- a/src/rsa/__tests__/rsassa_pkcs1_v1_5.test.ts +++ b/src/rsa/__tests__/rsassa_pkcs1_v1_5.test.ts @@ -3,45 +3,101 @@ import * as RSA from "../index.js"; const { RSASSA_PKCS1_v1_5 } = RSA; describe("RSASSA_PKCS1_v1_5", () => { - it("should generate key", async () => { - const key = await RSASSA_PKCS1_v1_5.generateKey(); - expect(key).toMatchSnapshot(); - }); + describe("Original", () => { + it("should generate key", async () => { + const key = await RSASSA_PKCS1_v1_5.generateKey(); + expect(key.self).toMatchSnapshot(); + }); + + it("should import and export key", async () => { + const keyPair = await RSASSA_PKCS1_v1_5.generateKey(); + + let jwk = await RSASSA_PKCS1_v1_5.exportKey( + "jwk", + keyPair.publicKey.self + ); + const pubKey = await RSASSA_PKCS1_v1_5.importKey( + "jwk", + jwk, + { hash: "SHA-512" }, + true, + ["verify"] + ); + + expect(jwk).toEqual( + await RSASSA_PKCS1_v1_5.exportKey("jwk", pubKey.self) + ); + + jwk = await RSASSA_PKCS1_v1_5.exportKey( + "jwk", + keyPair.privateKey.self + ); + const privKey = await RSASSA_PKCS1_v1_5.importKey( + "jwk", + jwk, + { hash: "SHA-512" }, + true, + ["sign"] + ); - it("should import and export key", async () => { - const keyPair = await RSASSA_PKCS1_v1_5.generateKey(); - - let jwk = await RSASSA_PKCS1_v1_5.exportKey("jwk", keyPair.publicKey); - const pubKey = await RSASSA_PKCS1_v1_5.importKey( - "jwk", - jwk, - { hash: "SHA-512" }, - true, - ["verify"] - ); - - expect(jwk).toEqual(await RSASSA_PKCS1_v1_5.exportKey("jwk", pubKey)); - - jwk = await RSASSA_PKCS1_v1_5.exportKey("jwk", keyPair.privateKey); - const privKey = await RSASSA_PKCS1_v1_5.importKey( - "jwk", - jwk, - { hash: "SHA-512" }, - true, - ["sign"] - ); - - expect(jwk).toEqual(await RSASSA_PKCS1_v1_5.exportKey("jwk", privKey)); + expect(jwk).toEqual( + await RSASSA_PKCS1_v1_5.exportKey("jwk", privKey.self) + ); + }); + it("should sign and verify", async () => { + const keyPair = await RSASSA_PKCS1_v1_5.generateKey(); + const text = encode("a message"); + const signature = await RSASSA_PKCS1_v1_5.sign( + keyPair.privateKey.self, + text + ); + expect( + await RSASSA_PKCS1_v1_5.verify( + keyPair.publicKey.self, + signature, + text + ) + ).toEqual(true); + }); }); - it("should sign and verify", async () => { - const keyPair = await RSASSA_PKCS1_v1_5.generateKey(); - const text = encode("a message"); - const signature = await RSASSA_PKCS1_v1_5.sign( - keyPair.privateKey, - text - ); - expect( - await RSASSA_PKCS1_v1_5.verify(keyPair.publicKey, signature, text) - ).toEqual(true); + describe("Proxied", () => { + it("should generate key", async () => { + const key = await RSASSA_PKCS1_v1_5.generateKey(); + expect(key).toMatchSnapshot(); + }); + + it("should import and export key", async () => { + const keyPair = await RSASSA_PKCS1_v1_5.generateKey(); + + let jwk = await keyPair.publicKey.exportKey("jwk"); + const pubKey = await RSASSA_PKCS1_v1_5.importKey( + "jwk", + jwk, + { hash: "SHA-512" }, + true, + ["verify"] + ); + + expect(jwk).toEqual(await pubKey.exportKey("jwk")); + + jwk = await keyPair.privateKey.exportKey("jwk"); + const privKey = await RSASSA_PKCS1_v1_5.importKey( + "jwk", + jwk, + { hash: "SHA-512" }, + true, + ["sign"] + ); + + expect(jwk).toEqual(await privKey.exportKey("jwk")); + }); + it("should sign and verify", async () => { + const keyPair = await RSASSA_PKCS1_v1_5.generateKey(); + const text = encode("a message"); + const signature = await keyPair.privateKey.sign(text); + expect(await keyPair.publicKey.verify(signature, text)).toEqual( + true + ); + }); }); }); diff --git a/src/rsa/rsassa_pkcs1_v1_5.ts b/src/rsa/rsassa_pkcs1_v1_5.ts index e814527..016c35f 100644 --- a/src/rsa/rsassa_pkcs1_v1_5.ts +++ b/src/rsa/rsassa_pkcs1_v1_5.ts @@ -4,15 +4,54 @@ */ import * as params from "../params.js"; +import * as proxy from "../proxy.js"; import { Alg as SHA } from "../sha/shared.js"; import { Alg, RsaShared, RsassaPkcs1V15CryptoKeyPair, RsassaPkcs1V15PrivCryptoKey, + RsassaPkcs1V15ProxiedCryptoKeyPair, + RsassaPkcs1V15ProxiedPrivCryptoKey, + RsassaPkcs1V15ProxiedPubCryptoKey, RsassaPkcs1V15PubCryptoKey, } from "./shared.js"; +const handlers: proxy.ProxyKeyPairHandlers< + RsassaPkcs1V15PrivCryptoKey, + RsassaPkcs1V15PubCryptoKey +> = { + privHandler: { + get(target: RsassaPkcs1V15PrivCryptoKey, prop: string) { + switch (prop) { + case "self": + return target; + case "sign": + return (data: BufferSource) => sign(target, data); + case "exportKey": + return (format: KeyFormat) => exportKey(format, target); + } + + return Reflect.get(target, prop); + }, + }, + pubHandler: { + get(target: RsassaPkcs1V15PubCryptoKey, prop: string) { + switch (prop) { + case "self": + return target; + case "verify": + return (signature: BufferSource, data: BufferSource) => + verify(target, signature, data); + case "exportKey": + return (format: KeyFormat) => exportKey(format, target); + } + + return Reflect.get(target, prop); + }, + }, +}; + /** * Generate a new RSASSA_PKCS1_v1_5 keypair * @example @@ -28,8 +67,8 @@ export const generateKey = async ( }, extractable?: boolean, keyUsages?: KeyUsage[] -) => - (await RsaShared.generateKey( +): Promise => { + const keyPair = (await RsaShared.generateKey( { ...algorithm, name: Alg.Variant.RSASSA_PKCS1_v1_5, @@ -37,6 +76,14 @@ export const generateKey = async ( extractable, keyUsages )) as RsassaPkcs1V15CryptoKeyPair; + return proxy.proxifyKeyPair< + RsassaPkcs1V15CryptoKeyPair, + RsassaPkcs1V15PrivCryptoKey, + RsassaPkcs1V15ProxiedPrivCryptoKey, + RsassaPkcs1V15PubCryptoKey, + RsassaPkcs1V15ProxiedPubCryptoKey + >(handlers)(keyPair); +}; /** * Import an RSASSA_PKCS1_v1_5 public or private key @@ -51,8 +98,10 @@ export const importKey = async ( algorithm: Omit, extractable?: boolean, keyUsages?: KeyUsage[] -): Promise => - await RsaShared.importKey( +): Promise< + RsassaPkcs1V15ProxiedPubCryptoKey | RsassaPkcs1V15ProxiedPrivCryptoKey +> => { + const importedKey = await RsaShared.importKey( format, key, { ...algorithm, name: Alg.Variant.RSASSA_PKCS1_v1_5 }, @@ -60,6 +109,19 @@ export const importKey = async ( keyUsages ); + if (importedKey.type === "private") { + return proxy.proxifyPrivKey< + RsassaPkcs1V15PrivCryptoKey, + RsassaPkcs1V15ProxiedPrivCryptoKey + >(handlers.privHandler)(importedKey as RsassaPkcs1V15PrivCryptoKey); + } else { + return proxy.proxifyPubKey< + RsassaPkcs1V15PubCryptoKey, + RsassaPkcs1V15ProxiedPubCryptoKey + >(handlers.pubHandler)(importedKey as RsassaPkcs1V15PubCryptoKey); + } +}; + /** * Export an RSASSA_PKCS1_v1_5 public or private key * @example diff --git a/src/rsa/shared.ts b/src/rsa/shared.ts index 7b477ad..32f0255 100644 --- a/src/rsa/shared.ts +++ b/src/rsa/shared.ts @@ -199,3 +199,25 @@ export interface RsaPssProxiedCryptoKeyPair RsaPssPubCryptoKey, RsaPssProxiedPubCryptoKey > {} + +export interface RsassaPkcs1V15ProxiedPubCryptoKey + extends proxy.ProxiedPubCryptoKey { + verify: (signature: BufferSource, data: BufferSource) => Promise; + + exportKey: (format: KeyFormat) => Promise; +} +export interface RsassaPkcs1V15ProxiedPrivCryptoKey + extends proxy.ProxiedPrivCryptoKey { + sign: (data: BufferSource) => Promise; + + exportKey: (format: KeyFormat) => Promise; +} + +export interface RsassaPkcs1V15ProxiedCryptoKeyPair + extends proxy.ProxiedCryptoKeyPair< + RsassaPkcs1V15CryptoKeyPair, + RsassaPkcs1V15PrivCryptoKey, + RsassaPkcs1V15ProxiedPrivCryptoKey, + RsassaPkcs1V15PubCryptoKey, + RsassaPkcs1V15ProxiedPubCryptoKey + > {} From 2df4c3803f00a960032a5e7cc46976e20ad8a274 Mon Sep 17 00:00:00 2001 From: Neal Fennimore Date: Mon, 29 May 2023 22:53:34 -0400 Subject: [PATCH 15/20] Update examples --- src/rsa/rsa_pss.ts | 20 +++++++++++++++++--- src/rsa/rsassa_pkcs1_v1_5.ts | 20 +++++++++++++++++--- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/rsa/rsa_pss.ts b/src/rsa/rsa_pss.ts index 6f465f3..76ba726 100644 --- a/src/rsa/rsa_pss.ts +++ b/src/rsa/rsa_pss.ts @@ -129,7 +129,11 @@ export const importKey = async ( * Export an RSA_PSS public or private key * @example * ```ts - * const pubKeyJwk = await RSA_PSS.importKey("jwk", keyPair.publicKey); + * const pubKeyJwk = await RSA_PSS.importKey("jwk", keyPair.publicKey.self); + * ``` + * @example + * ```ts + * const pubKeyJwk = await keyPair.publicKey.importKey("jwk"); * ``` */ export const exportKey = async ( @@ -142,7 +146,12 @@ export const exportKey = async ( * @example * ```ts * const message = new TextEncoder().encode("a message"); - * const signature = await RSA_PSS.sign(128, keyPair.privateKey, message); + * const signature = await RSA_PSS.sign(128, keyPair.privateKey.self, message); + * ``` + * @example + * ```ts + * const message = new TextEncoder().encode("a message"); + * const signature = await keyPair.privateKey.sign(128, message); * ``` */ export const sign = async ( @@ -164,7 +173,12 @@ export const sign = async ( * @example * ```ts * const message = new TextEncoder().encode("a message"); - * const isVerified = await ECDSA.verify(128, keyPair.publicKey, signature, message); + * const isVerified = await ECDSA.verify(128, keyPair.publicKey.self, signature, message); + * ``` + * @example + * ```ts + * const message = new TextEncoder().encode("a message"); + * const isVerified = await keyPair.publicKey.verify(128, signature, message); * ``` */ export const verify = async ( diff --git a/src/rsa/rsassa_pkcs1_v1_5.ts b/src/rsa/rsassa_pkcs1_v1_5.ts index 016c35f..3285686 100644 --- a/src/rsa/rsassa_pkcs1_v1_5.ts +++ b/src/rsa/rsassa_pkcs1_v1_5.ts @@ -126,7 +126,11 @@ export const importKey = async ( * Export an RSASSA_PKCS1_v1_5 public or private key * @example * ```ts - * const pubKeyJwk = await RSASSA_PKCS1_v1_5.importKey("jwk", keyPair.publicKey); + * const pubKeyJwk = await RSASSA_PKCS1_v1_5.importKey("jwk", keyPair.publicKey.self); + * ``` + * @example + * ```ts + * const pubKeyJwk = await keyPair.publicKey.importKey("jwk"); * ``` */ export const exportKey = async ( @@ -139,7 +143,12 @@ export const exportKey = async ( * @example * ```ts * const message = new TextEncoder().encode("a message"); - * const signature = await RSASSA_PKCS1_v1_5.sign(keyPair.privateKey, message); + * const signature = await RSASSA_PKCS1_v1_5.sign(keyPair.privateKey.self, message); + * ``` + * @example + * ```ts + * const message = new TextEncoder().encode("a message"); + * const signature = await keyPair.privateKey.sign(message); * ``` */ export const sign = async ( @@ -159,7 +168,12 @@ export const sign = async ( * @example * ```ts * const message = new TextEncoder().encode("a message"); - * const isVerified = await RSASSA_PKCS1_v1_5.verify(keyPair.publicKey, signature, message); + * const isVerified = await RSASSA_PKCS1_v1_5.verify(keyPair.publicKey.self, signature, message); + * ``` + * @example + * ```ts + * const message = new TextEncoder().encode("a message"); + * const isVerified = await keyPair.publicKey.verify( signature, message); * ``` */ export const verify = async ( From 893c4594ce1e124ba6e2c3f773c5492176f2745c Mon Sep 17 00:00:00 2001 From: Neal Fennimore Date: Mon, 29 May 2023 22:53:43 -0400 Subject: [PATCH 16/20] Update snapshots --- .../__tests__/__snapshots__/hmac.test.ts.snap | 20 - .../__tests__/__snapshots__/hkdf.test.ts.snap | 632 ------------------ .../__snapshots__/pbkdf2.test.ts.snap | 632 ------------------ .../__snapshots__/rsa_pss.test.ts.snap | 47 -- .../rsassa_pkcs1_v1_5.test.ts.snap | 47 -- 5 files changed, 1378 deletions(-) diff --git a/src/hmac/__tests__/__snapshots__/hmac.test.ts.snap b/src/hmac/__tests__/__snapshots__/hmac.test.ts.snap index 2ab7e0f..2dd296c 100644 --- a/src/hmac/__tests__/__snapshots__/hmac.test.ts.snap +++ b/src/hmac/__tests__/__snapshots__/hmac.test.ts.snap @@ -39,23 +39,3 @@ CryptoKey { ], } `; - -exports[`HMAC should generate key 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "hash": Object { - "name": "SHA-512", - }, - "length": 1024, - "name": "HMAC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "sign", - "verify", - ], -} -`; diff --git a/src/kdf/__tests__/__snapshots__/hkdf.test.ts.snap b/src/kdf/__tests__/__snapshots__/hkdf.test.ts.snap index 13ba695..1778f25 100644 --- a/src/kdf/__tests__/__snapshots__/hkdf.test.ts.snap +++ b/src/kdf/__tests__/__snapshots__/hkdf.test.ts.snap @@ -1263,635 +1263,3 @@ CryptoKey { ], } `; - -exports[`HKDF should derive keys: AES_CBC_128_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-CBC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_CBC_128_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-CBC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_CBC_128_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-CBC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_CBC_192_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-CBC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_CBC_192_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-CBC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_CBC_192_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-CBC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_CBC_256_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-CBC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_CBC_256_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-CBC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_CBC_256_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-CBC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_CTR_128_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-CTR", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_CTR_128_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-CTR", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_CTR_128_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-CTR", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_CTR_192_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-CTR", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_CTR_192_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-CTR", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_CTR_192_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-CTR", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_CTR_256_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-CTR", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_CTR_256_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-CTR", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_CTR_256_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-CTR", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_GCM_128_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-GCM", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_GCM_128_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-GCM", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_GCM_128_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-GCM", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_GCM_192_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-GCM", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_GCM_192_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-GCM", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_GCM_192_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-GCM", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_GCM_256_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-GCM", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_GCM_256_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-GCM", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_GCM_256_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-GCM", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`HKDF should derive keys: AES_KW_128_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-KW", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "wrapKey", - "unwrapKey", - ], -} -`; - -exports[`HKDF should derive keys: AES_KW_128_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-KW", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "wrapKey", - "unwrapKey", - ], -} -`; - -exports[`HKDF should derive keys: AES_KW_128_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-KW", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "wrapKey", - "unwrapKey", - ], -} -`; - -exports[`HKDF should derive keys: AES_KW_192_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-KW", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "wrapKey", - "unwrapKey", - ], -} -`; - -exports[`HKDF should derive keys: AES_KW_192_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-KW", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "wrapKey", - "unwrapKey", - ], -} -`; - -exports[`HKDF should derive keys: AES_KW_192_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-KW", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "wrapKey", - "unwrapKey", - ], -} -`; - -exports[`HKDF should derive keys: AES_KW_256_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-KW", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "wrapKey", - "unwrapKey", - ], -} -`; - -exports[`HKDF should derive keys: AES_KW_256_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-KW", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "wrapKey", - "unwrapKey", - ], -} -`; - -exports[`HKDF should derive keys: AES_KW_256_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-KW", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "wrapKey", - "unwrapKey", - ], -} -`; - -exports[`HKDF should derive keys: HMAC_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "hash": Object { - "name": "SHA-512", - }, - "length": 512, - "name": "HMAC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "sign", - "verify", - ], -} -`; diff --git a/src/kdf/__tests__/__snapshots__/pbkdf2.test.ts.snap b/src/kdf/__tests__/__snapshots__/pbkdf2.test.ts.snap index ac1fb30..d33ffa7 100644 --- a/src/kdf/__tests__/__snapshots__/pbkdf2.test.ts.snap +++ b/src/kdf/__tests__/__snapshots__/pbkdf2.test.ts.snap @@ -1263,635 +1263,3 @@ CryptoKey { ], } `; - -exports[`PBKDF2 should derive keys: AES_CBC_128_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-CBC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_CBC_128_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-CBC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_CBC_128_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-CBC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_CBC_192_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-CBC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_CBC_192_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-CBC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_CBC_192_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-CBC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_CBC_256_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-CBC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_CBC_256_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-CBC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_CBC_256_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-CBC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_CTR_128_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-CTR", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_CTR_128_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-CTR", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_CTR_128_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-CTR", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_CTR_192_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-CTR", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_CTR_192_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-CTR", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_CTR_192_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-CTR", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_CTR_256_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-CTR", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_CTR_256_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-CTR", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_CTR_256_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-CTR", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_GCM_128_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-GCM", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_GCM_128_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-GCM", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_GCM_128_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-GCM", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_GCM_192_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-GCM", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_GCM_192_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-GCM", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_GCM_192_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-GCM", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_GCM_256_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-GCM", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_GCM_256_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-GCM", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_GCM_256_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-GCM", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "encrypt", - "decrypt", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_KW_128_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-KW", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "wrapKey", - "unwrapKey", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_KW_128_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-KW", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "wrapKey", - "unwrapKey", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_KW_128_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 128, - "name": "AES-KW", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "wrapKey", - "unwrapKey", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_KW_192_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-KW", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "wrapKey", - "unwrapKey", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_KW_192_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-KW", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "wrapKey", - "unwrapKey", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_KW_192_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 192, - "name": "AES-KW", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "wrapKey", - "unwrapKey", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_KW_256_SHA_256 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-KW", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "wrapKey", - "unwrapKey", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_KW_256_SHA_384 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-KW", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "wrapKey", - "unwrapKey", - ], -} -`; - -exports[`PBKDF2 should derive keys: AES_KW_256_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "length": 256, - "name": "AES-KW", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "wrapKey", - "unwrapKey", - ], -} -`; - -exports[`PBKDF2 should derive keys: HMAC_SHA_512 1`] = ` -CryptoKey { - Symbol(kKeyObject): SecretKeyObject { - Symbol(kKeyType): "secret", - }, - Symbol(kAlgorithm): Object { - "hash": Object { - "name": "SHA-512", - }, - "length": 512, - "name": "HMAC", - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "sign", - "verify", - ], -} -`; diff --git a/src/rsa/__tests__/__snapshots__/rsa_pss.test.ts.snap b/src/rsa/__tests__/__snapshots__/rsa_pss.test.ts.snap index a49360e..e7766bd 100644 --- a/src/rsa/__tests__/__snapshots__/rsa_pss.test.ts.snap +++ b/src/rsa/__tests__/__snapshots__/rsa_pss.test.ts.snap @@ -93,50 +93,3 @@ Object { }, } `; - -exports[`RSA_PSS should generate key 1`] = ` -Object { - "privateKey": CryptoKey { - Symbol(kKeyObject): PrivateKeyObject { - Symbol(kKeyType): "private", - }, - Symbol(kAlgorithm): Object { - "hash": Object { - "name": "SHA-512", - }, - "modulusLength": 4096, - "name": "RSA-PSS", - "publicExponent": Uint8Array [ - 1, - 0, - 1, - ], - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "sign", - ], - }, - "publicKey": CryptoKey { - Symbol(kKeyObject): PublicKeyObject { - Symbol(kKeyType): "public", - }, - Symbol(kAlgorithm): Object { - "hash": Object { - "name": "SHA-512", - }, - "modulusLength": 4096, - "name": "RSA-PSS", - "publicExponent": Uint8Array [ - 1, - 0, - 1, - ], - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "verify", - ], - }, -} -`; diff --git a/src/rsa/__tests__/__snapshots__/rsassa_pkcs1_v1_5.test.ts.snap b/src/rsa/__tests__/__snapshots__/rsassa_pkcs1_v1_5.test.ts.snap index dda1455..929a5e0 100644 --- a/src/rsa/__tests__/__snapshots__/rsassa_pkcs1_v1_5.test.ts.snap +++ b/src/rsa/__tests__/__snapshots__/rsassa_pkcs1_v1_5.test.ts.snap @@ -93,50 +93,3 @@ Object { }, } `; - -exports[`RSASSA_PKCS1_v1_5 should generate key 1`] = ` -Object { - "privateKey": CryptoKey { - Symbol(kKeyObject): PrivateKeyObject { - Symbol(kKeyType): "private", - }, - Symbol(kAlgorithm): Object { - "hash": Object { - "name": "SHA-512", - }, - "modulusLength": 4096, - "name": "RSASSA-PKCS1-v1_5", - "publicExponent": Uint8Array [ - 1, - 0, - 1, - ], - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "sign", - ], - }, - "publicKey": CryptoKey { - Symbol(kKeyObject): PublicKeyObject { - Symbol(kKeyType): "public", - }, - Symbol(kAlgorithm): Object { - "hash": Object { - "name": "SHA-512", - }, - "modulusLength": 4096, - "name": "RSASSA-PKCS1-v1_5", - "publicExponent": Uint8Array [ - 1, - 0, - 1, - ], - }, - Symbol(kExtractable): true, - Symbol(kKeyUsages): Array [ - "verify", - ], - }, -} -`; From 77c18231f5e57e57fc64813f89bde30bf0bf1628 Mon Sep 17 00:00:00 2001 From: Neal Fennimore Date: Tue, 30 May 2023 07:21:20 -0400 Subject: [PATCH 17/20] Update exports and interface usage --- src/aes/__tests__/aes_cbc.test.ts | 5 +- src/aes/__tests__/aes_ctr.test.ts | 7 +- src/aes/__tests__/aes_gcm.test.ts | 5 +- src/aes/__tests__/aes_kw.test.ts | 5 +- src/aes/aes_cbc.ts | 37 +---- src/aes/aes_ctr.ts | 37 +---- src/aes/aes_gcm.ts | 37 +---- src/aes/aes_kw.ts | 22 +-- src/aes/index.ts | 4 + src/aes/shared.ts | 226 ++++++++++++++---------------- src/ec/index.ts | 6 + src/kdf/__tests__/hkdf.test.ts | 3 +- src/kdf/__tests__/pbkdf2.test.ts | 3 +- src/kdf/hkdf.ts | 27 +--- src/kdf/index.ts | 7 +- src/kdf/pbkdf.ts | 26 +--- src/kdf/shared.ts | 36 +++++ src/rsa/index.ts | 14 +- 18 files changed, 218 insertions(+), 289 deletions(-) diff --git a/src/aes/__tests__/aes_cbc.test.ts b/src/aes/__tests__/aes_cbc.test.ts index a8a35da..f44cc87 100644 --- a/src/aes/__tests__/aes_cbc.test.ts +++ b/src/aes/__tests__/aes_cbc.test.ts @@ -1,11 +1,10 @@ import * as Random from "../../random.js"; -import { AesCbcProxiedCryptoKey } from "../aes_cbc.js"; import * as AES from "../index.js"; describe("AES_CBC", () => { describe("Original", () => { let iv: Uint8Array, - proxiedKey: AesCbcProxiedCryptoKey, + proxiedKey: AES.AesCbcProxiedCryptoKey, key: AES.AesCbcCryptoKey; const text = "brown fox fox fox fox fox fox fox fox fox"; beforeEach(async () => { @@ -85,7 +84,7 @@ describe("AES_CBC", () => { }); }); describe("Proxied", () => { - let iv: Uint8Array, key: AesCbcProxiedCryptoKey; + let iv: Uint8Array, key: AES.AesCbcProxiedCryptoKey; const text = "brown fox fox fox fox fox fox fox fox fox"; beforeEach(async () => { iv = await Random.IV.generate(); diff --git a/src/aes/__tests__/aes_ctr.test.ts b/src/aes/__tests__/aes_ctr.test.ts index 1381480..74a2130 100644 --- a/src/aes/__tests__/aes_ctr.test.ts +++ b/src/aes/__tests__/aes_ctr.test.ts @@ -1,12 +1,11 @@ import * as Random from "../../random.js"; -import { AesCtrProxiedCryptoKey } from "../aes_ctr.js"; import * as AES from "../index.js"; describe("AES_CTR", () => { describe("Original", () => { let iv: Uint8Array, counter: Uint8Array, - proxiedKey: AesCtrProxiedCryptoKey, + proxiedKey: AES.AesCtrProxiedCryptoKey, key: AES.AesCtrCryptoKey; const text = "brown fox fox fox fox fox fox fox fox fox"; beforeEach(async () => { @@ -88,7 +87,9 @@ describe("AES_CTR", () => { }); }); describe("Proxied", () => { - let iv: Uint8Array, counter: Uint8Array, key: AesCtrProxiedCryptoKey; + let iv: Uint8Array, + counter: Uint8Array, + key: AES.AesCtrProxiedCryptoKey; const text = "brown fox fox fox fox fox fox fox fox fox"; beforeEach(async () => { iv = await Random.IV.generate(); diff --git a/src/aes/__tests__/aes_gcm.test.ts b/src/aes/__tests__/aes_gcm.test.ts index ffd2456..81712ef 100644 --- a/src/aes/__tests__/aes_gcm.test.ts +++ b/src/aes/__tests__/aes_gcm.test.ts @@ -1,11 +1,10 @@ import * as Random from "../../random.js"; -import { AesGcmProxiedCryptoKey } from "../aes_gcm.js"; import * as AES from "../index.js"; describe("AES_GCM", () => { describe("Original", () => { let iv: Uint8Array, - proxiedKey: AesGcmProxiedCryptoKey, + proxiedKey: AES.AesGcmProxiedCryptoKey, key: AES.AesGcmCryptoKey; const text = "brown fox fox fox fox fox fox fox fox fox"; beforeEach(async () => { @@ -85,7 +84,7 @@ describe("AES_GCM", () => { }); }); describe("Proxied", () => { - let iv: Uint8Array, key: AesGcmProxiedCryptoKey; + let iv: Uint8Array, key: AES.AesGcmProxiedCryptoKey; const text = "brown fox fox fox fox fox fox fox fox fox"; beforeEach(async () => { iv = await Random.IV.generate(); diff --git a/src/aes/__tests__/aes_kw.test.ts b/src/aes/__tests__/aes_kw.test.ts index 6f3429b..ad6a8ab 100644 --- a/src/aes/__tests__/aes_kw.test.ts +++ b/src/aes/__tests__/aes_kw.test.ts @@ -1,9 +1,8 @@ -import { AesKwProxiedCryptoKey } from "../aes_kw.js"; import * as AES from "../index.js"; describe("AES_KW", () => { describe("Original", () => { - let proxiedKey: AesKwProxiedCryptoKey; + let proxiedKey: AES.AesKwProxiedCryptoKey; let key: AES.AesKwCryptoKey; beforeEach(async () => { proxiedKey = await AES.AES_KW.generateKey(); @@ -38,7 +37,7 @@ describe("AES_KW", () => { }); }); describe("Proxied", () => { - let key: AesKwProxiedCryptoKey; + let key: AES.AesKwProxiedCryptoKey; beforeEach(async () => { key = await AES.AES_KW.generateKey(); }); diff --git a/src/aes/aes_cbc.ts b/src/aes/aes_cbc.ts index 05256a5..a1835ed 100644 --- a/src/aes/aes_cbc.ts +++ b/src/aes/aes_cbc.ts @@ -5,37 +5,12 @@ import * as params from "../params.js"; import * as proxy from "../proxy.js"; -import { AesCbcCryptoKey, AesShared, Alg } from "./shared.js"; - -export interface AesCbcProxiedCryptoKey - extends proxy.ProxiedCryptoKey { - encrypt( - algorithm: Omit, - data: BufferSource - ): Promise; - - decrypt( - algorithm: Omit, - data: BufferSource - ): Promise; - - wrapKey( - format: KeyFormat, - key: CryptoKey, - wrapAlgorithm: Omit - ): Promise; - - unwrapKey( - format: KeyFormat, - wrappedKey: BufferSource, - wrappedKeyAlgorithm: params.EnforcedImportParams, - unwrappingKeyAlgorithm: Omit, - extractable?: boolean, - keyUsages?: KeyUsage[] - ): Promise; - - exportKey: (format: KeyFormat) => Promise; -} +import { + AesCbcCryptoKey, + AesCbcProxiedCryptoKey, + AesShared, + Alg, +} from "./shared.js"; const handler: ProxyHandler = { get(target: AesCbcCryptoKey, prop: string) { diff --git a/src/aes/aes_ctr.ts b/src/aes/aes_ctr.ts index 6dc7d26..69790df 100644 --- a/src/aes/aes_ctr.ts +++ b/src/aes/aes_ctr.ts @@ -6,37 +6,12 @@ import * as params from "../params.js"; import * as proxy from "../proxy.js"; import { getValues } from "../random.js"; -import { AesCtrCryptoKey, AesShared, Alg } from "./shared.js"; - -export interface AesCtrProxiedCryptoKey - extends proxy.ProxiedCryptoKey { - encrypt( - algorithm: Omit, - data: BufferSource - ): Promise; - - decrypt( - algorithm: Omit, - data: BufferSource - ): Promise; - - wrapKey( - format: KeyFormat, - key: CryptoKey, - wrapAlgorithm: Omit - ): Promise; - - unwrapKey( - format: KeyFormat, - wrappedKey: BufferSource, - wrappedKeyAlgorithm: params.EnforcedImportParams, - unwrappingKeyAlgorithm: Omit, - extractable?: boolean, - keyUsages?: KeyUsage[] - ): Promise; - - exportKey: (format: KeyFormat) => Promise; -} +import { + AesCtrCryptoKey, + AesCtrProxiedCryptoKey, + AesShared, + Alg, +} from "./shared.js"; const handler: ProxyHandler = { get(target: AesCtrCryptoKey, prop: string) { diff --git a/src/aes/aes_gcm.ts b/src/aes/aes_gcm.ts index b68627c..825f978 100644 --- a/src/aes/aes_gcm.ts +++ b/src/aes/aes_gcm.ts @@ -4,37 +4,12 @@ */ import * as params from "../params.js"; import * as proxy from "../proxy.js"; -import { AesGcmCryptoKey, AesShared, Alg } from "./shared.js"; - -export interface AesGcmProxiedCryptoKey - extends proxy.ProxiedCryptoKey { - encrypt( - algorithm: Omit, - data: BufferSource - ): Promise; - - decrypt( - algorithm: Omit, - data: BufferSource - ): Promise; - - wrapKey( - format: KeyFormat, - key: CryptoKey, - wrapAlgorithm: Omit - ): Promise; - - unwrapKey( - format: KeyFormat, - wrappedKey: BufferSource, - wrappedKeyAlgorithm: params.EnforcedImportParams, - unwrappingKeyAlgorithm: Omit, - extractable?: boolean, - keyUsages?: KeyUsage[] - ): Promise; - - exportKey: (format: KeyFormat) => Promise; -} +import { + AesGcmCryptoKey, + AesGcmProxiedCryptoKey, + AesShared, + Alg, +} from "./shared.js"; const handler: ProxyHandler = { get(target: AesGcmCryptoKey, prop: string) { diff --git a/src/aes/aes_kw.ts b/src/aes/aes_kw.ts index bcb6363..0e0f674 100644 --- a/src/aes/aes_kw.ts +++ b/src/aes/aes_kw.ts @@ -4,22 +4,12 @@ */ import * as params from "../params.js"; import * as proxy from "../proxy.js"; -import { AesKwCryptoKey, AesShared, Alg } from "./shared.js"; - -export interface AesKwProxiedCryptoKey - extends proxy.ProxiedCryptoKey { - wrapKey(format: KeyFormat, key: CryptoKey): Promise; - - unwrapKey( - format: KeyFormat, - wrappedKey: BufferSource, - wrappedKeyAlgorithm: params.EnforcedImportParams, - extractable?: boolean, - keyUsages?: KeyUsage[] - ): Promise; - - exportKey: (format: KeyFormat) => Promise; -} +import { + AesKwCryptoKey, + AesKwProxiedCryptoKey, + AesShared, + Alg, +} from "./shared.js"; const handler: ProxyHandler = { get(target: AesKwCryptoKey, prop: string) { diff --git a/src/aes/index.ts b/src/aes/index.ts index a1b5d45..4d8c64c 100644 --- a/src/aes/index.ts +++ b/src/aes/index.ts @@ -9,8 +9,12 @@ export * as AES_KW from "./aes_kw.js"; export { Alg } from "./shared.js"; export type { AesCbcCryptoKey, + AesCbcProxiedCryptoKey, AesCryptoKeys, AesCtrCryptoKey, + AesCtrProxiedCryptoKey, AesGcmCryptoKey, + AesGcmProxiedCryptoKey, AesKwCryptoKey, + AesKwProxiedCryptoKey, } from "./shared.js"; diff --git a/src/aes/shared.ts b/src/aes/shared.ts index 01fda9b..d61e06d 100644 --- a/src/aes/shared.ts +++ b/src/aes/shared.ts @@ -41,127 +41,6 @@ export namespace Alg { export type Modes = `${Mode}`; } -export interface AesProxiedCryptoKey - extends proxy.ProxiedCryptoKey { - encrypt( - algorithm: Exclude< - params.EnforcedAesParams, - params.EnforcedAesKwParams - >, - data: BufferSource - ): Promise; - - decrypt( - algorithm: Exclude< - params.EnforcedAesParams, - params.EnforcedAesKwParams - >, - data: BufferSource - ): Promise; - - wrapKey( - format: KeyFormat, - key: CryptoKey, - wrapAlgorithm: params.EnforcedAesParams - ): Promise; - - unwrapKey( - format: KeyFormat, - wrappedKey: BufferSource, - wrappedKeyAlgorithm: params.EnforcedImportParams, - unwrappingKeyAlgorithm: params.EnforcedAesParams, - extractable: boolean, - keyUsages?: KeyUsage[] - ): Promise; - - exportKey: (format: KeyFormat) => Promise; -} - -export const handler: ProxyHandler = { - get(target: AesCryptoKeys, prop: string) { - switch (prop) { - case "self": - return target; - - case "encrypt": - if ( - !target.usages.every((u) => - usages.EncryptionKeyUsagePair.includes(u) - ) - ) { - throw new Error("encrypt is not supported"); - } - return ( - algorithm: Exclude< - params.EnforcedAesParams, - params.EnforcedAesKwParams - >, - data: BufferSource - ) => AesShared.encrypt(algorithm, target, data); - - case "decrypt": - if ( - !target.usages.every((u) => - usages.EncryptionKeyUsagePair.includes(u) - ) - ) { - throw new Error("decrypt is not supported"); - } - return ( - algorithm: Exclude< - params.EnforcedAesParams, - params.EnforcedAesKwParams - >, - data: BufferSource - ) => AesShared.decrypt(algorithm, target, data); - - case "wrapKey": - if ( - !target.usages.every((u) => - usages.WrappingKeyUsagePair.includes(u) - ) - ) { - throw new Error("wrapKey is not supported"); - } - return ( - format: KeyFormat, - key: CryptoKey, - wrapAlgorithm: params.EnforcedAesParams - ) => AesShared.wrapKey(format, key, target, wrapAlgorithm); - case "unwrapKey": - if ( - !target.usages.every((u) => - usages.WrappingKeyUsagePair.includes(u) - ) - ) { - throw new Error("unwrapKey is not supported"); - } - return ( - format: KeyFormat, - wrappedKey: BufferSource, - wrappedKeyAlgorithm: params.EnforcedImportParams, - unwrappingKeyAlgorithm: params.EnforcedAesParams, - extractable?: boolean, - keyUsages?: KeyUsage[] - ) => - AesShared.unwrapKey( - format, - wrappedKey, - wrappedKeyAlgorithm, - target, - unwrappingKeyAlgorithm, - extractable, - keyUsages - ); - case "exportKey": - return (format: KeyFormat) => - AesShared.exportKey(format, target); - } - - return Reflect.get(target, prop); - }, -}; - export namespace AesShared { export async function generateKey( algorithm: params.EnforcedAesKeyGenParams, @@ -253,3 +132,108 @@ export namespace AesShared { ); } } + +export interface AesCbcProxiedCryptoKey + extends proxy.ProxiedCryptoKey { + encrypt( + algorithm: Omit, + data: BufferSource + ): Promise; + + decrypt( + algorithm: Omit, + data: BufferSource + ): Promise; + + wrapKey( + format: KeyFormat, + key: CryptoKey, + wrapAlgorithm: Omit + ): Promise; + + unwrapKey( + format: KeyFormat, + wrappedKey: BufferSource, + wrappedKeyAlgorithm: params.EnforcedImportParams, + unwrappingKeyAlgorithm: Omit, + extractable?: boolean, + keyUsages?: KeyUsage[] + ): Promise; + + exportKey: (format: KeyFormat) => Promise; +} + +export interface AesCtrProxiedCryptoKey + extends proxy.ProxiedCryptoKey { + encrypt( + algorithm: Omit, + data: BufferSource + ): Promise; + + decrypt( + algorithm: Omit, + data: BufferSource + ): Promise; + + wrapKey( + format: KeyFormat, + key: CryptoKey, + wrapAlgorithm: Omit + ): Promise; + + unwrapKey( + format: KeyFormat, + wrappedKey: BufferSource, + wrappedKeyAlgorithm: params.EnforcedImportParams, + unwrappingKeyAlgorithm: Omit, + extractable?: boolean, + keyUsages?: KeyUsage[] + ): Promise; + + exportKey: (format: KeyFormat) => Promise; +} + +export interface AesGcmProxiedCryptoKey + extends proxy.ProxiedCryptoKey { + encrypt( + algorithm: Omit, + data: BufferSource + ): Promise; + + decrypt( + algorithm: Omit, + data: BufferSource + ): Promise; + + wrapKey( + format: KeyFormat, + key: CryptoKey, + wrapAlgorithm: Omit + ): Promise; + + unwrapKey( + format: KeyFormat, + wrappedKey: BufferSource, + wrappedKeyAlgorithm: params.EnforcedImportParams, + unwrappingKeyAlgorithm: Omit, + extractable?: boolean, + keyUsages?: KeyUsage[] + ): Promise; + + exportKey: (format: KeyFormat) => Promise; +} + +export interface AesKwProxiedCryptoKey + extends proxy.ProxiedCryptoKey { + wrapKey(format: KeyFormat, key: CryptoKey): Promise; + + unwrapKey( + format: KeyFormat, + wrappedKey: BufferSource, + wrappedKeyAlgorithm: params.EnforcedImportParams, + extractable?: boolean, + keyUsages?: KeyUsage[] + ): Promise; + + exportKey: (format: KeyFormat) => Promise; +} diff --git a/src/ec/index.ts b/src/ec/index.ts index 333b959..68f069f 100644 --- a/src/ec/index.ts +++ b/src/ec/index.ts @@ -9,7 +9,13 @@ export type { EcCryptoKeyPairs, EcCryptoKeys, EcdhPrivCryptoKey, + EcdhProxiedCryptoKeyPair, + EcdhProxiedPrivCryptoKey, + EcdhProxiedPubCryptoKey, EcdhPubCryptoKey, EcdsaPrivCryptoKey, + EcdsaProxiedCryptoKeyPair, + EcdsaProxiedPrivCryptoKey, + EcdsaProxiedPubCryptoKey, EcdsaPubCryptoKey, } from "./shared.js"; diff --git a/src/kdf/__tests__/hkdf.test.ts b/src/kdf/__tests__/hkdf.test.ts index 5f2c4a4..13c8fa3 100644 --- a/src/kdf/__tests__/hkdf.test.ts +++ b/src/kdf/__tests__/hkdf.test.ts @@ -3,9 +3,8 @@ import { Alg as Authentication } from "../../hmac/index.js"; import * as params from "../../params.js"; import * as Random from "../../random.js"; import { Alg as SHA } from "../../sha/shared.js"; -import { HkdfProxiedKeyMaterial } from "../hkdf.js"; import * as KDF from "../index.js"; -import type { HkdfKeyMaterial } from "../shared.js"; +import type { HkdfKeyMaterial, HkdfProxiedKeyMaterial } from "../shared.js"; const { HKDF } = KDF; describe("HKDF", () => { diff --git a/src/kdf/__tests__/pbkdf2.test.ts b/src/kdf/__tests__/pbkdf2.test.ts index d0db79c..5ed1a89 100644 --- a/src/kdf/__tests__/pbkdf2.test.ts +++ b/src/kdf/__tests__/pbkdf2.test.ts @@ -4,8 +4,7 @@ import * as params from "../../params.js"; import * as Random from "../../random.js"; import { Alg as SHA } from "../../sha/shared.js"; import * as KDF from "../index.js"; -import { Pbkdf2ProxiedKeyMaterial } from "../pbkdf.js"; -import { Pbkdf2KeyMaterial } from "../shared.js"; +import { Pbkdf2KeyMaterial, Pbkdf2ProxiedKeyMaterial } from "../shared.js"; const { PBKDF2 } = KDF; diff --git a/src/kdf/hkdf.ts b/src/kdf/hkdf.ts index 9b5e81d..5e79e32 100644 --- a/src/kdf/hkdf.ts +++ b/src/kdf/hkdf.ts @@ -2,29 +2,14 @@ * Code related to HKDF * @module */ -import type { AesCryptoKeys } from "../aes/index.js"; -import { HmacCryptoKey } from "../hmac/index.js"; import * as params from "../params.js"; import * as proxy from "../proxy.js"; -import { Alg, HkdfKeyMaterial, KdfShared } from "./shared.js"; - -export interface HkdfProxiedKeyMaterial - extends proxy.ProxiedCryptoKey { - deriveKey( - algorithm: Omit, - derivedKeyType: - | params.EnforcedAesKeyGenParams - | params.EnforcedHmacKeyGenParams, - extractable?: boolean, - keyUsages?: KeyUsage[] - ): Promise; - - deriveBits( - algorithm: Omit, - length: number - ): Promise; - exportKey: (format: KeyFormat) => Promise; -} +import { + Alg, + HkdfKeyMaterial, + HkdfProxiedKeyMaterial, + KdfShared, +} from "./shared.js"; const handler: ProxyHandler = { get(target: HkdfKeyMaterial, prop: string) { diff --git a/src/kdf/index.ts b/src/kdf/index.ts index e3a5908..664f401 100644 --- a/src/kdf/index.ts +++ b/src/kdf/index.ts @@ -5,4 +5,9 @@ export * as HKDF from "./hkdf.js"; export * as PBKDF2 from "./pbkdf.js"; export { Alg } from "./shared.js"; -export type { HkdfKeyMaterial, Pbkdf2KeyMaterial } from "./shared.js"; +export type { + HkdfKeyMaterial, + HkdfProxiedKeyMaterial, + Pbkdf2KeyMaterial, + Pbkdf2ProxiedKeyMaterial, +} from "./shared.js"; diff --git a/src/kdf/pbkdf.ts b/src/kdf/pbkdf.ts index 082b425..b2d590f 100644 --- a/src/kdf/pbkdf.ts +++ b/src/kdf/pbkdf.ts @@ -2,29 +2,15 @@ * Code related to PBKDF2 * @module */ -import type { AesCryptoKeys } from "../aes/index.js"; -import { HmacCryptoKey } from "../hmac/index.js"; import * as params from "../params.js"; import * as proxy from "../proxy.js"; import { Alg as SHA } from "../sha/shared.js"; -import { Alg, KdfShared, Pbkdf2KeyMaterial } from "./shared.js"; - -export interface Pbkdf2ProxiedKeyMaterial - extends proxy.ProxiedCryptoKey { - deriveKey( - algorithm: Omit, - derivedKeyType: - | params.EnforcedAesKeyGenParams - | params.EnforcedHmacKeyGenParams, - extractable?: boolean, - keyUsages?: KeyUsage[] - ): Promise; - deriveBits( - algorithm: Omit, - length: number - ): Promise; - exportKey: (format: KeyFormat) => Promise; -} +import { + Alg, + KdfShared, + Pbkdf2KeyMaterial, + Pbkdf2ProxiedKeyMaterial, +} from "./shared.js"; const handler: ProxyHandler = { get(target: Pbkdf2KeyMaterial, prop: string) { diff --git a/src/kdf/shared.ts b/src/kdf/shared.ts index cefd6df..1675a78 100644 --- a/src/kdf/shared.ts +++ b/src/kdf/shared.ts @@ -6,6 +6,7 @@ import type { AesCryptoKeys } from "../aes/index.js"; import { HmacCryptoKey } from "../hmac/index.js"; import { DeriveKeyUsagePair, getKeyUsagePairsByAlg } from "../key_usages.js"; import * as params from "../params.js"; +import * as proxy from "../proxy.js"; import * as WebCrypto from "../webcrypto.js"; export interface Pbkdf2KeyMaterial extends CryptoKey { @@ -68,3 +69,38 @@ export namespace KdfShared { return await WebCrypto.deriveBits(algorithm, baseKey, length); } } + +export interface HkdfProxiedKeyMaterial + extends proxy.ProxiedCryptoKey { + deriveKey( + algorithm: Omit, + derivedKeyType: + | params.EnforcedAesKeyGenParams + | params.EnforcedHmacKeyGenParams, + extractable?: boolean, + keyUsages?: KeyUsage[] + ): Promise; + + deriveBits( + algorithm: Omit, + length: number + ): Promise; + exportKey: (format: KeyFormat) => Promise; +} + +export interface Pbkdf2ProxiedKeyMaterial + extends proxy.ProxiedCryptoKey { + deriveKey( + algorithm: Omit, + derivedKeyType: + | params.EnforcedAesKeyGenParams + | params.EnforcedHmacKeyGenParams, + extractable?: boolean, + keyUsages?: KeyUsage[] + ): Promise; + deriveBits( + algorithm: Omit, + length: number + ): Promise; + exportKey: (format: KeyFormat) => Promise; +} diff --git a/src/rsa/index.ts b/src/rsa/index.ts index d3054ae..5428768 100644 --- a/src/rsa/index.ts +++ b/src/rsa/index.ts @@ -2,17 +2,29 @@ * All RSA algorithms * @module */ -export * as RSASSA_PKCS1_v1_5 from "./rsassa_pkcs1_v1_5.js"; export * as RSA_OAEP from "./rsa_oaep.js"; export * as RSA_PSS from "./rsa_pss.js"; +export * as RSASSA_PKCS1_v1_5 from "./rsassa_pkcs1_v1_5.js"; export { Alg } from "./shared.js"; export type { RsaCryptoKeyPairs, RsaCryptoKeys, + RsaOaepCryptoKeyPair, RsaOaepPrivCryptoKey, + RsaOaepProxiedCryptoKeyPair, + RsaOaepProxiedPrivCryptoKey, + RsaOaepProxiedPubCryptoKey, RsaOaepPubCryptoKey, + RsaPssCryptoKeyPair, RsaPssPrivCryptoKey, + RsaPssProxiedCryptoKeyPair, + RsaPssProxiedPrivCryptoKey, + RsaPssProxiedPubCryptoKey, RsaPssPubCryptoKey, + RsassaPkcs1V15CryptoKeyPair, RsassaPkcs1V15PrivCryptoKey, + RsassaPkcs1V15ProxiedCryptoKeyPair, + RsassaPkcs1V15ProxiedPrivCryptoKey, + RsassaPkcs1V15ProxiedPubCryptoKey, RsassaPkcs1V15PubCryptoKey, } from "./shared.js"; From 09987136a302fff99548ffa92d6acab864b71e3a Mon Sep 17 00:00:00 2001 From: Neal Fennimore Date: Tue, 30 May 2023 07:35:09 -0400 Subject: [PATCH 18/20] Add in test skip for node16 HMAC dues to wrong snapshot hash size --- .github/workflows/test.yml | 3 ++- src/hmac/__tests__/hmac.test.ts | 7 ++++--- src/hmac/index.ts | 16 ++++++---------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2d09bf5..4123183 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,6 @@ jobs: fail-fast: false matrix: node-version: [16.x, 18.x, 20.x] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - uses: actions/checkout@v3 @@ -35,6 +34,8 @@ jobs: run: npm ci - run: npm run test -- --coverage + env: + NODE_VERSION: ${{ matrix.node-version }} - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 diff --git a/src/hmac/__tests__/hmac.test.ts b/src/hmac/__tests__/hmac.test.ts index de2b966..48248ed 100644 --- a/src/hmac/__tests__/hmac.test.ts +++ b/src/hmac/__tests__/hmac.test.ts @@ -1,5 +1,7 @@ import * as HMAC from "../index.js"; +const skipIf = process.env.NODE_VERSION === "16.x" ? it.skip : it; + describe("HMAC", () => { describe("Original", () => { let proxiedKey: HMAC.HmacProxiedCryptoKey; @@ -9,7 +11,7 @@ describe("HMAC", () => { proxiedKey = await HMAC.generateKey(); key = proxiedKey.self; }); - it("should generate key", async () => { + skipIf("should generate key", async () => { expect(key).toMatchSnapshot(); }); it("should import and export key", async () => { @@ -32,10 +34,9 @@ describe("HMAC", () => { describe("Proxied", () => { let key: HMAC.HmacProxiedCryptoKey; beforeEach(async () => { - // @ts-ignore key = await HMAC.generateKey(); }); - it("should generate key", async () => { + skipIf("should generate key", async () => { expect(key).toMatchSnapshot(); }); it("should import and export key", async () => { diff --git a/src/hmac/index.ts b/src/hmac/index.ts index d2ed231..cd6f1d6 100644 --- a/src/hmac/index.ts +++ b/src/hmac/index.ts @@ -56,7 +56,7 @@ export const generateKey = async ( }, extractable: boolean = true, keyUsages?: KeyUsage[] -): Promise> => { +): Promise => { const key = await WebCrypto.generateKey< HmacCryptoKey, params.EnforcedHmacKeyGenParams @@ -68,10 +68,7 @@ export const generateKey = async ( extractable, keyUsages ?? getKeyUsagePairsByAlg(Alg.Code.HMAC) ); - return proxy.proxifyKey< - HmacCryptoKey, - proxy.ProxiedCryptoKey - >(handler)(key); + return proxy.proxifyKey(handler)(key); }; /** @@ -87,7 +84,7 @@ export const importKey = async ( algorithm: Omit, extractable: boolean = true, keyUsages?: KeyUsage[] -): Promise> => { +): Promise => { const importedKey = await WebCrypto.importKey< HmacCryptoKey, params.EnforcedHmacImportParams @@ -99,10 +96,9 @@ export const importKey = async ( keyUsages ?? getKeyUsagePairsByAlg(Alg.Code.HMAC) ); - return proxy.proxifyKey< - HmacCryptoKey, - proxy.ProxiedCryptoKey - >(handler)(importedKey); + return proxy.proxifyKey(handler)( + importedKey + ); }; /** From 365e84d16442794757d54160b847d51b43454f76 Mon Sep 17 00:00:00 2001 From: Neal Fennimore Date: Tue, 30 May 2023 08:04:52 -0400 Subject: [PATCH 19/20] Update documentation --- README.md | 59 +++++++++++++++++++++++++++++++++++++++++++++ src/rsa/rsa_oaep.ts | 7 +++--- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d3bd55e..caa91dc 100644 --- a/README.md +++ b/README.md @@ -15,3 +15,62 @@ Algorithms are split into their own modules, which enforces consumption of crypt ```sh npm i @nfen/webcrypto-ts ``` + +## Examples + +Many more examples in the [Documentation](https://neal.codes/webcrypto-ts/). + +### ECDSA + +```ts +import ECDSA from "@nfen/webcrypto-ts/lib/ec/ecdsa"; +const keyPair = await ECDSA.generateKey(); + +const message = new TextEncoder().encode("a message"); +const signature = await keyPair.privateKey.sign({ hash: "SHA-512" }, message); + +const isVerified = await someOtherKeypair.publicKey.verify( + { hash: "SHA-512" }, + signature, + message +); +``` + +### RSA-OAEP + +```ts +import * as RSA_OAEP from "@nfen/webcrypto-ts/lib/rsa/rsa_oaep"; +import * as AES_CBC from "@nfen/webcrypto-ts/lib/aes/aes_cbc"; +import * as Random from "@nfen/webcrypto-ts/random"; + +const kek = await RSA_OAEP.generateKey( + { + hash: "SHA-512", + modulusLength: 4096, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + }, + true, + ["wrapKey", "unwrapKey"] +); +const dek = await AES_CBC.generateKey(); +const label = await Random.getValues(8); +const wrappedCbcKey = await kek.wrapKey("raw", dek.self, { label }); +``` + +### AES-GCM + +```ts +import * as AES_GCM from "@nfen/webcrypto-ts/lib/aes/aes_gcm"; +import { IV } from "@nfen/webcrypto-ts/lib/random"; + +const iv = await IV.generate(); +const key = await AES_GCM.generateKey(); +const message = "a message"; +const cipherText = await key.encrypt( + { iv }, + new TextEncoder().encode("a message") +); +console.assert( + new TextDecoder().decode(await key.decrypt({ iv }, message)) === message +); +``` diff --git a/src/rsa/rsa_oaep.ts b/src/rsa/rsa_oaep.ts index cdeddf8..31173d8 100644 --- a/src/rsa/rsa_oaep.ts +++ b/src/rsa/rsa_oaep.ts @@ -214,7 +214,6 @@ export async function encrypt( * const data = await keyPair.privateKey.decrypt({label}, data); * ``` */ - export async function decrypt( algorithm: Omit = {}, key: RsaOaepPrivCryptoKey, @@ -236,13 +235,15 @@ export async function decrypt( * ```ts * const kek = await RSA_OAEP.generateKey(undefined, true, ['wrapKey', 'unwrapKey']); * const dek = await RSA_OAEP.generateKey(); - * const wrappedKey = await RSA_OAEP.wrapKey("raw", dek.self, kek.self, {iv}); + * const label = await Random.getValues(8); + * const wrappedKey = await RSA_OAEP.wrapKey("raw", dek.self, kek.self, {label}); * ``` * @example * ```ts * const kek = await RSA_OAEP.generateKey(undefined, true, ['wrapKey', 'unwrapKey']); * const dek = await RSA_OAEP.generateKey(); - * const wrappedKey = await kek.wrapKey("raw", dek.self, {iv}); + * const label = await Random.getValues(8); + * const wrappedKey = await kek.wrapKey("raw", dek.self, {label}); * ``` */ export async function wrapKey( From 7fbcb35ba5885254a60ddc7549a596f869e44381 Mon Sep 17 00:00:00 2001 From: Neal Fennimore Date: Tue, 30 May 2023 08:11:39 -0400 Subject: [PATCH 20/20] Update tests for coverage --- src/aes/__tests__/aes_gcm.test.ts | 3 +-- src/rsa/__tests__/rsa_oaep.test.ts | 17 +++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/aes/__tests__/aes_gcm.test.ts b/src/aes/__tests__/aes_gcm.test.ts index 81712ef..73edbb5 100644 --- a/src/aes/__tests__/aes_gcm.test.ts +++ b/src/aes/__tests__/aes_gcm.test.ts @@ -132,11 +132,10 @@ describe("AES_GCM", () => { const wrappedKey = await kek.wrapKey("raw", dek.self, { iv, }); - const unwrappedKey = (await AES.AES_GCM.unwrapKey( + const unwrappedKey = (await kek.unwrapKey( "raw", wrappedKey, { name: AES.Alg.Mode.AES_GCM }, - kek.self, { iv } )) as AES.AesGcmCryptoKey; diff --git a/src/rsa/__tests__/rsa_oaep.test.ts b/src/rsa/__tests__/rsa_oaep.test.ts index 6b3f8dc..f4c2032 100644 --- a/src/rsa/__tests__/rsa_oaep.test.ts +++ b/src/rsa/__tests__/rsa_oaep.test.ts @@ -97,20 +97,25 @@ describe("RSA_OAEP", () => { it("should import and export key", async () => { const keyPair = await RSA_OAEP.generateKey(); - const jwk = await keyPair.publicKey.exportKey("jwk"); + const pubJwk = await keyPair.publicKey.exportKey("jwk"); + const privJwk = await keyPair.privateKey.exportKey("jwk"); const pubKey = (await RSA_OAEP.importKey( "jwk", - jwk, + pubJwk, { hash: "SHA-512" }, false, ["encrypt"] )) as RsaOaepProxiedPubCryptoKey; const text = encode("a message"); const ciphertext = await pubKey.encrypt({ label }, text); - const plaintext = await keyPair.privateKey.decrypt( - { label }, - ciphertext - ); + const privKey = (await RSA_OAEP.importKey( + "jwk", + privJwk, + { hash: "SHA-512" }, + false, + ["decrypt"] + )) as RSA.RsaOaepProxiedPrivCryptoKey; + const plaintext = await privKey.decrypt({ label }, ciphertext); expect(decode(plaintext)).toEqual(decode(text)); }); it("should encrypt and decrypt", async () => {