diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a89280f..4123183 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,11 +9,10 @@ on: jobs: build: runs-on: ubuntu-latest - strategy: + fail-fast: false matrix: - node-version: [16.x, 18.x] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + node-version: [16.x, 18.x, 20.x] 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/.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/.prettierignore b/.prettierignore new file mode 100644 index 0000000..ad30bfe --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +*.yml \ No newline at end of file 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/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/package-lock.json b/package-lock.json index f1c1ed9..f9293dd 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/src/aes/__tests__/aes_cbc.test.ts b/src/aes/__tests__/aes_cbc.test.ts index 75545de..f44cc87 100644 --- a/src/aes/__tests__/aes_cbc.test.ts +++ b/src/aes/__tests__/aes_cbc.test.ts @@ -2,75 +2,149 @@ import * as Random from "../../random.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: AES.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: AES.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..74a2130 100644 --- a/src/aes/__tests__/aes_ctr.test.ts +++ b/src/aes/__tests__/aes_ctr.test.ts @@ -2,77 +2,159 @@ import * as Random from "../../random.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: AES.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: AES.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..73edbb5 100644 --- a/src/aes/__tests__/aes_gcm.test.ts +++ b/src/aes/__tests__/aes_gcm.test.ts @@ -2,75 +2,149 @@ import * as Random from "../../random.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: AES.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: AES.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 kek.unwrapKey( + "raw", + wrappedKey, + { name: AES.Alg.Mode.AES_GCM }, + { 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..ad6a8ab 100644 --- a/src/aes/__tests__/aes_kw.test.ts +++ b/src/aes/__tests__/aes_kw.test.ts @@ -1,32 +1,64 @@ 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: AES.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: AES.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..a1835ed 100644 --- a/src/aes/aes_cbc.ts +++ b/src/aes/aes_cbc.ts @@ -4,7 +4,69 @@ */ import * as params from "../params.js"; -import { AesCbcCryptoKey, AesShared, Alg } from "./shared.js"; +import * as proxy from "../proxy.js"; +import { + AesCbcCryptoKey, + AesCbcProxiedCryptoKey, + AesShared, + Alg, +} from "./shared.js"; + +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 +80,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 +89,9 @@ export async function generateKey( extractable, keyUsages ); + return proxy.proxifyKey(handler)( + key + ); } /** @@ -44,8 +109,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 +119,9 @@ export async function importKey( }, extractable, keyUsages + )) as AesCbcCryptoKey; + return proxy.proxifyKey(handler)( + importedKey ); } @@ -62,7 +130,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 +149,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 +184,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 +227,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 +257,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..69790df 100644 --- a/src/aes/aes_ctr.ts +++ b/src/aes/aes_ctr.ts @@ -4,8 +4,69 @@ */ import * as params from "../params.js"; +import * as proxy from "../proxy.js"; import { getValues } from "../random.js"; -import { AesCtrCryptoKey, AesShared, Alg } from "./shared.js"; +import { + AesCtrCryptoKey, + AesCtrProxiedCryptoKey, + AesShared, + Alg, +} from "./shared.js"; + +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. @@ -38,8 +99,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 +108,9 @@ export async function generateKey( extractable, keyUsages ); + return proxy.proxifyKey(handler)( + key + ); } /** @@ -62,8 +126,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 +136,9 @@ export async function importKey( }, extractable, keyUsages + )) as AesCtrCryptoKey; + return proxy.proxifyKey(handler)( + importedKey ); } @@ -79,7 +146,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 +166,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 +196,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 +226,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 +253,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..825f978 100644 --- a/src/aes/aes_gcm.ts +++ b/src/aes/aes_gcm.ts @@ -3,7 +3,68 @@ * @module */ import * as params from "../params.js"; -import { AesGcmCryptoKey, AesShared, Alg } from "./shared.js"; +import * as proxy from "../proxy.js"; +import { + AesGcmCryptoKey, + AesGcmProxiedCryptoKey, + AesShared, + Alg, +} from "./shared.js"; + +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 @@ -18,8 +79,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 +88,9 @@ export async function generateKey( extractable, keyUsages ); + return proxy.proxifyKey(handler)( + key + ); } /** @@ -42,8 +106,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 +116,9 @@ export async function importKey( }, extractable, keyUsages + )) as AesGcmCryptoKey; + return proxy.proxifyKey(handler)( + importedKey ); } @@ -59,7 +126,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 +145,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 +174,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 +205,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 +233,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..0e0f674 100644 --- a/src/aes/aes_kw.ts +++ b/src/aes/aes_kw.ts @@ -3,7 +3,46 @@ * @module */ import * as params from "../params.js"; -import { AesKwCryptoKey, AesShared, Alg } from "./shared.js"; +import * as proxy from "../proxy.js"; +import { + AesKwCryptoKey, + AesKwProxiedCryptoKey, + AesShared, + Alg, +} from "./shared.js"; + +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 @@ -18,14 +57,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 +83,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 +92,9 @@ export async function importKey( }, extractable, keyUsages + )) as AesKwCryptoKey; + return proxy.proxifyKey(handler)( + importedKey ); } @@ -57,7 +102,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 +118,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 +141,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/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 f49323d..d61e06d 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 { @@ -49,7 +50,7 @@ export namespace AesShared { return await WebCrypto.generateKey( algorithm, extractable, - keyUsages ?? getKeyUsagePairsByAlg(algorithm.name) + keyUsages ?? usages.getKeyUsagePairsByAlg(algorithm.name) ); } @@ -65,7 +66,7 @@ export namespace AesShared { key as any, algorithm, extractable, - keyUsages ?? getKeyUsagePairsByAlg(algorithm.name) + keyUsages ?? usages.getKeyUsagePairsByAlg(algorithm.name) ); } @@ -127,7 +128,112 @@ export namespace AesShared { unwrappingKeyAlgorithm, wrappedKeyAlgorithm, extractable, - keyUsages ?? getKeyUsagePairsByAlg(wrappedKeyAlgorithm.name) + keyUsages ?? usages.getKeyUsagePairsByAlg(wrappedKeyAlgorithm.name) ); } } + +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/__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__/__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__/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/__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/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/ecdsa.ts b/src/ec/ecdsa.ts index 5fff681..3b28134 100644 --- a/src/ec/ecdsa.ts +++ b/src/ec/ecdsa.ts @@ -3,21 +3,74 @@ * @module */ import * as params from "../params.js"; +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 = { @@ -25,18 +78,31 @@ export const generateKey = async ( }, extractable?: boolean, keyUsages?: KeyUsage[] -): Promise => - await EcShared.generateKey( +): Promise => { + const keyPair = (await EcShared.generateKey( { ...algorithm, name: Alg.Variant.ECDSA }, extractable, keyUsages - ); + )) as EcdsaCryptoKeyPair; + + return proxy.proxifyKeyPair< + EcdsaCryptoKeyPair, + EcdsaPrivCryptoKey, + 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 ( @@ -47,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 }, @@ -56,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 ( @@ -73,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( @@ -96,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/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/ec/shared.ts b/src/ec/shared.ts index 63eaa14..bb04aee 100644 --- a/src/ec/shared.ts +++ b/src/ec/shared.ts @@ -2,8 +2,11 @@ * 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"; import * as WebCrypto from "../webcrypto.js"; export interface EcdhPubCryptoKey extends CryptoKey { @@ -28,6 +31,66 @@ 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 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 | EcdhPrivCryptoKey diff --git a/src/hmac/__tests__/__snapshots__/hmac.test.ts.snap b/src/hmac/__tests__/__snapshots__/hmac.test.ts.snap index 74ade7a..2dd296c 100644 --- a/src/hmac/__tests__/__snapshots__/hmac.test.ts.snap +++ b/src/hmac/__tests__/__snapshots__/hmac.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`HMAC should generate key 1`] = ` +exports[`HMAC Original should generate key 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -9,7 +9,27 @@ CryptoKey { "hash": Object { "name": "SHA-512", }, - "length": 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, diff --git a/src/hmac/__tests__/hmac.test.ts b/src/hmac/__tests__/hmac.test.ts index aae6613..48248ed 100644 --- a/src/hmac/__tests__/hmac.test.ts +++ b/src/hmac/__tests__/hmac.test.ts @@ -1,25 +1,59 @@ import * as HMAC from "../index.js"; +const skipIf = process.env.NODE_VERSION === "16.x" ? it.skip : it; + 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; + }); + skipIf("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.self)).toEqual( + jwk + ); }); + it("should sign and verify", async () => { + const text = encode("a message"); + const signature = await HMAC.sign(key, text); - expect(await HMAC.exportKey("jwk", importedPubKey)).toEqual(jwk); + 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 () => { + key = await HMAC.generateKey(); + }); + skipIf("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..cd6f1d6 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,8 @@ export const generateKey = async ( extractable, keyUsages ?? getKeyUsagePairsByAlg(Alg.Code.HMAC) ); + return proxy.proxifyKey(handler)(key); +}; /** * Import an HMAC key from the specified format @@ -53,8 +84,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 +96,20 @@ export const importKey = async ( keyUsages ?? getKeyUsagePairsByAlg(Alg.Code.HMAC) ); + return proxy.proxifyKey(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 +124,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 +150,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, diff --git a/src/kdf/__tests__/__snapshots__/hkdf.test.ts.snap b/src/kdf/__tests__/__snapshots__/hkdf.test.ts.snap index 6c68cf1..1778f25 100644 --- a/src/kdf/__tests__/__snapshots__/hkdf.test.ts.snap +++ b/src/kdf/__tests__/__snapshots__/hkdf.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`HKDF should derive keys: AES_CBC_128_SHA_256 1`] = ` +exports[`HKDF Original should derive keys: AES_CBC_128_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -17,7 +17,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_CBC_128_SHA_384 1`] = ` +exports[`HKDF Original should derive keys: AES_CBC_128_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -34,7 +34,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_CBC_128_SHA_512 1`] = ` +exports[`HKDF Original should derive keys: AES_CBC_128_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -51,7 +51,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_CBC_192_SHA_256 1`] = ` +exports[`HKDF Original should derive keys: AES_CBC_192_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -68,7 +68,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_CBC_192_SHA_384 1`] = ` +exports[`HKDF Original should derive keys: AES_CBC_192_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -85,7 +85,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_CBC_192_SHA_512 1`] = ` +exports[`HKDF Original should derive keys: AES_CBC_192_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -102,7 +102,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_CBC_256_SHA_256 1`] = ` +exports[`HKDF Original should derive keys: AES_CBC_256_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -119,7 +119,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_CBC_256_SHA_384 1`] = ` +exports[`HKDF Original should derive keys: AES_CBC_256_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -136,7 +136,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_CBC_256_SHA_512 1`] = ` +exports[`HKDF Original should derive keys: AES_CBC_256_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -153,7 +153,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_CTR_128_SHA_256 1`] = ` +exports[`HKDF Original should derive keys: AES_CTR_128_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -170,7 +170,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_CTR_128_SHA_384 1`] = ` +exports[`HKDF Original should derive keys: AES_CTR_128_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -187,7 +187,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_CTR_128_SHA_512 1`] = ` +exports[`HKDF Original should derive keys: AES_CTR_128_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -204,7 +204,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_CTR_192_SHA_256 1`] = ` +exports[`HKDF Original should derive keys: AES_CTR_192_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -221,7 +221,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_CTR_192_SHA_384 1`] = ` +exports[`HKDF Original should derive keys: AES_CTR_192_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -238,7 +238,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_CTR_192_SHA_512 1`] = ` +exports[`HKDF Original should derive keys: AES_CTR_192_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -255,7 +255,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_CTR_256_SHA_256 1`] = ` +exports[`HKDF Original should derive keys: AES_CTR_256_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -272,7 +272,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_CTR_256_SHA_384 1`] = ` +exports[`HKDF Original should derive keys: AES_CTR_256_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -289,7 +289,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_CTR_256_SHA_512 1`] = ` +exports[`HKDF Original should derive keys: AES_CTR_256_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -306,7 +306,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_GCM_128_SHA_256 1`] = ` +exports[`HKDF Original should derive keys: AES_GCM_128_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -323,7 +323,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_GCM_128_SHA_384 1`] = ` +exports[`HKDF Original should derive keys: AES_GCM_128_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -340,7 +340,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_GCM_128_SHA_512 1`] = ` +exports[`HKDF Original should derive keys: AES_GCM_128_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -357,7 +357,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_GCM_192_SHA_256 1`] = ` +exports[`HKDF Original should derive keys: AES_GCM_192_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -374,7 +374,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_GCM_192_SHA_384 1`] = ` +exports[`HKDF Original should derive keys: AES_GCM_192_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -391,7 +391,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_GCM_192_SHA_512 1`] = ` +exports[`HKDF Original should derive keys: AES_GCM_192_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -408,7 +408,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_GCM_256_SHA_256 1`] = ` +exports[`HKDF Original should derive keys: AES_GCM_256_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -425,7 +425,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_GCM_256_SHA_384 1`] = ` +exports[`HKDF Original should derive keys: AES_GCM_256_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -442,7 +442,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_GCM_256_SHA_512 1`] = ` +exports[`HKDF Original should derive keys: AES_GCM_256_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -459,7 +459,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_KW_128_SHA_256 1`] = ` +exports[`HKDF Original should derive keys: AES_KW_128_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -476,7 +476,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_KW_128_SHA_384 1`] = ` +exports[`HKDF Original should derive keys: AES_KW_128_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -493,7 +493,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_KW_128_SHA_512 1`] = ` +exports[`HKDF Original should derive keys: AES_KW_128_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -510,7 +510,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_KW_192_SHA_256 1`] = ` +exports[`HKDF Original should derive keys: AES_KW_192_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -527,7 +527,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_KW_192_SHA_384 1`] = ` +exports[`HKDF Original should derive keys: AES_KW_192_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -544,7 +544,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_KW_192_SHA_512 1`] = ` +exports[`HKDF Original should derive keys: AES_KW_192_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -561,7 +561,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_KW_256_SHA_256 1`] = ` +exports[`HKDF Original should derive keys: AES_KW_256_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -578,7 +578,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_KW_256_SHA_384 1`] = ` +exports[`HKDF Original should derive keys: AES_KW_256_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -595,7 +595,7 @@ CryptoKey { } `; -exports[`HKDF should derive keys: AES_KW_256_SHA_512 1`] = ` +exports[`HKDF Original should derive keys: AES_KW_256_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -612,7 +612,639 @@ CryptoKey { } `; -exports[`HKDF should derive keys: HMAC_SHA_512 1`] = ` +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", diff --git a/src/kdf/__tests__/__snapshots__/pbkdf2.test.ts.snap b/src/kdf/__tests__/__snapshots__/pbkdf2.test.ts.snap index 4c830b7..d33ffa7 100644 --- a/src/kdf/__tests__/__snapshots__/pbkdf2.test.ts.snap +++ b/src/kdf/__tests__/__snapshots__/pbkdf2.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PBKDF2 should derive keys: AES_CBC_128_SHA_256 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_CBC_128_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -17,7 +17,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_CBC_128_SHA_384 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_CBC_128_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -34,7 +34,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_CBC_128_SHA_512 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_CBC_128_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -51,7 +51,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_CBC_192_SHA_256 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_CBC_192_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -68,7 +68,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_CBC_192_SHA_384 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_CBC_192_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -85,7 +85,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_CBC_192_SHA_512 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_CBC_192_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -102,7 +102,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_CBC_256_SHA_256 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_CBC_256_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -119,7 +119,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_CBC_256_SHA_384 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_CBC_256_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -136,7 +136,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_CBC_256_SHA_512 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_CBC_256_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -153,7 +153,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_CTR_128_SHA_256 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_CTR_128_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -170,7 +170,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_CTR_128_SHA_384 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_CTR_128_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -187,7 +187,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_CTR_128_SHA_512 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_CTR_128_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -204,7 +204,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_CTR_192_SHA_256 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_CTR_192_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -221,7 +221,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_CTR_192_SHA_384 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_CTR_192_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -238,7 +238,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_CTR_192_SHA_512 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_CTR_192_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -255,7 +255,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_CTR_256_SHA_256 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_CTR_256_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -272,7 +272,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_CTR_256_SHA_384 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_CTR_256_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -289,7 +289,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_CTR_256_SHA_512 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_CTR_256_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -306,7 +306,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_GCM_128_SHA_256 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_GCM_128_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -323,7 +323,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_GCM_128_SHA_384 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_GCM_128_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -340,7 +340,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_GCM_128_SHA_512 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_GCM_128_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -357,7 +357,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_GCM_192_SHA_256 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_GCM_192_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -374,7 +374,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_GCM_192_SHA_384 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_GCM_192_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -391,7 +391,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_GCM_192_SHA_512 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_GCM_192_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -408,7 +408,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_GCM_256_SHA_256 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_GCM_256_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -425,7 +425,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_GCM_256_SHA_384 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_GCM_256_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -442,7 +442,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_GCM_256_SHA_512 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_GCM_256_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -459,7 +459,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_KW_128_SHA_256 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_KW_128_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -476,7 +476,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_KW_128_SHA_384 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_KW_128_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -493,7 +493,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_KW_128_SHA_512 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_KW_128_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -510,7 +510,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_KW_192_SHA_256 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_KW_192_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -527,7 +527,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_KW_192_SHA_384 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_KW_192_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -544,7 +544,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_KW_192_SHA_512 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_KW_192_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -561,7 +561,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_KW_256_SHA_256 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_KW_256_SHA_256 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -578,7 +578,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_KW_256_SHA_384 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_KW_256_SHA_384 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -595,7 +595,7 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: AES_KW_256_SHA_512 1`] = ` +exports[`PBKDF2 Original should derive keys: AES_KW_256_SHA_512 1`] = ` CryptoKey { Symbol(kKeyObject): SecretKeyObject { Symbol(kKeyType): "secret", @@ -612,7 +612,639 @@ CryptoKey { } `; -exports[`PBKDF2 should derive keys: HMAC_SHA_512 1`] = ` +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", diff --git a/src/kdf/__tests__/hkdf.test.ts b/src/kdf/__tests__/hkdf.test.ts index f35d4e6..13c8fa3 100644 --- a/src/kdf/__tests__/hkdf.test.ts +++ b/src/kdf/__tests__/hkdf.test.ts @@ -1,76 +1,147 @@ 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 * as KDF from "../index.js"; -import type { HkdfKeyMaterial } from "../shared.js"; +import type { HkdfKeyMaterial, HkdfProxiedKeyMaterial } 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..5ed1a89 100644 --- a/src/kdf/__tests__/pbkdf2.test.ts +++ b/src/kdf/__tests__/pbkdf2.test.ts @@ -4,73 +4,138 @@ 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 { Pbkdf2KeyMaterial, Pbkdf2ProxiedKeyMaterial } 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..5e79e32 100644 --- a/src/kdf/hkdf.ts +++ b/src/kdf/hkdf.ts @@ -3,7 +3,45 @@ * @module */ import * as params from "../params.js"; -import { Alg, HkdfKeyMaterial, KdfShared } from "./shared.js"; +import * as proxy from "../proxy.js"; +import { + Alg, + HkdfKeyMaterial, + HkdfProxiedKeyMaterial, + KdfShared, +} from "./shared.js"; + +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 @@ -12,18 +50,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 +83,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 +130,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/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 ff6bf1e..b2d590f 100644 --- a/src/kdf/pbkdf.ts +++ b/src/kdf/pbkdf.ts @@ -3,8 +3,49 @@ * @module */ 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"; +import { + Alg, + KdfShared, + Pbkdf2KeyMaterial, + Pbkdf2ProxiedKeyMaterial, +} from "./shared.js"; + +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, @@ -19,18 +60,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 +87,7 @@ export const generateKeyMaterial = ( * length: 512, * }; * let key = await PBKDF2.deriveKey( - * { hash: "SHA-512" }, + * { hash: "SHA512" }, * keyMaterial, * hmacParams * ); 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/proxy.ts b/src/proxy.ts new file mode 100644 index 0000000..a373f0a --- /dev/null +++ b/src/proxy.ts @@ -0,0 +1,82 @@ +/** + * Code related to proxying CryptoKey and CryptoKeyPair + * @module + */ + +export interface ProxiedCryptoKey { + self: T; +} +export interface ProxiedPubCryptoKey + extends ProxiedCryptoKey {} + +export interface ProxiedPrivCryptoKey + extends ProxiedCryptoKey {} + +export interface ProxiedCryptoKeyPair< + TKeyPair extends CryptoKeyPair, + TPrivKey extends CryptoKey, + TProxPrivKey extends ProxiedPrivCryptoKey, + TPubKey extends CryptoKey, + TProxPubKey extends ProxiedPubCryptoKey +> { + self: TKeyPair; + privateKey: TProxPrivKey; + publicKey: TProxPubKey; +} +export function proxifyKey< + TKey extends CryptoKey, + TProxKey extends ProxiedCryptoKey +>(handler: ProxyHandler) { + return function _proxifyKey(key: TKey) { + return new Proxy(key, handler); + }; +} + +export const proxifyPubKey = proxifyKey; +export const proxifyPrivKey = proxifyKey; + +export interface ProxyKeyPairHandlers< + TPrivKey extends CryptoKey, + TPubKey extends CryptoKey +> { + privHandler: ProxyHandler; + pubHandler: ProxyHandler; +} + +export function proxifyKeyPair< + TKeyPair extends CryptoKeyPair, + TPrivKey extends CryptoKey, + 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); + }, + }); + }; +} 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__/__snapshots__/rsa_pss.test.ts.snap b/src/rsa/__tests__/__snapshots__/rsa_pss.test.ts.snap index 4cecb25..e7766bd 100644 --- a/src/rsa/__tests__/__snapshots__/rsa_pss.test.ts.snap +++ b/src/rsa/__tests__/__snapshots__/rsa_pss.test.ts.snap @@ -1,6 +1,53 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`RSA_PSS should generate key 1`] = ` +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 { 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..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 @@ -1,6 +1,53 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`RSASSA_PKCS1_v1_5 should generate key 1`] = ` +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 { diff --git a/src/rsa/__tests__/rsa_oaep.test.ts b/src/rsa/__tests__/rsa_oaep.test.ts index ff7e1b9..f4c2032 100644 --- a/src/rsa/__tests__/rsa_oaep.test.ts +++ b/src/rsa/__tests__/rsa_oaep.test.ts @@ -1,81 +1,160 @@ 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 pubJwk = await keyPair.publicKey.exportKey("jwk"); + const privJwk = await keyPair.privateKey.exportKey("jwk"); + const pubKey = (await RSA_OAEP.importKey( + "jwk", + pubJwk, + { hash: "SHA-512" }, + false, + ["encrypt"] + )) as RsaOaepProxiedPubCryptoKey; + const text = encode("a message"); + const ciphertext = await pubKey.encrypt({ label }, text); + 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 () => { + 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/__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/__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/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"; diff --git a/src/rsa/rsa_oaep.ts b/src/rsa/rsa_oaep.ts index 459551f..31173d8 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,10 +207,13 @@ 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); * ``` */ - export async function decrypt( algorithm: Omit = {}, key: RsaOaepPrivCryptoKey, @@ -126,7 +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, kek, {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 label = await Random.getValues(8); + * const wrappedKey = await kek.wrapKey("raw", dek.self, {label}); * ``` */ export async function wrapKey( @@ -151,12 +268,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/rsa_pss.ts b/src/rsa/rsa_pss.ts index b561426..76ba726 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,11 +112,28 @@ 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 * ```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 ( @@ -77,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 ( @@ -99,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 e814527..3285686 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,11 +109,28 @@ 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 * ```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 ( @@ -77,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 ( @@ -97,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 ( diff --git a/src/rsa/shared.ts b/src/rsa/shared.ts index da24679..32f0255 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,94 @@ 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 + > {} + +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 + > {} + +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 + > {} 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 */