From ccf15fa54932a3e329391ae74d761194e8876557 Mon Sep 17 00:00:00 2001 From: Ajitomi Daisuke Date: Sun, 13 Aug 2023 09:29:26 +0900 Subject: [PATCH] Split ciphersuite test. --- test/cipherSuite.test.ts | 1457 +++++--------------------------------- test/sample.test.ts | 1159 ++++++++++++++++++++++++++++++ 2 files changed, 1324 insertions(+), 1292 deletions(-) create mode 100644 test/sample.test.ts diff --git a/test/cipherSuite.test.ts b/test/cipherSuite.test.ts index cef478099..68ce36972 100644 --- a/test/cipherSuite.test.ts +++ b/test/cipherSuite.test.ts @@ -1,5 +1,4 @@ import { assertEquals, assertRejects, assertThrows } from "testing/asserts.ts"; - import { describe, it } from "testing/bdd.ts"; import { AeadId, KdfId, KemId } from "../src/identifiers.ts"; @@ -21,9 +20,9 @@ import * as errors from "../src/errors.ts"; import { hexStringToBytes } from "./utils.ts"; -describe("CipherSuite", () => { +describe("constructor", () => { // RFC9180 A.1. - describe("constructor with DhkemX25519HkdfSha256/HkdfSha256/Aes128Gcm", () => { + describe("with DhkemX25519HkdfSha256/HkdfSha256/Aes128Gcm", () => { it("should have a correct ciphersuite", () => { const suite: CipherSuite = new CipherSuite({ kem: KemId.DhkemX25519HkdfSha256, @@ -49,7 +48,7 @@ describe("CipherSuite", () => { }); // RFC9180 A.2. - describe("constructor with DhkemX25519HkdfSha256/HkdfSha256/ChaCha20Poly1305", () => { + describe("with DhkemX25519HkdfSha256/HkdfSha256/ChaCha20Poly1305", () => { it("should have a correct ciphersuite", () => { const suite: CipherSuite = new CipherSuite({ kem: KemId.DhkemX25519HkdfSha256, @@ -68,7 +67,7 @@ describe("CipherSuite", () => { }); // RFC9180 A.3. - describe("constructor with DhkemP256HkdfSha256/HkdfSha256/Aes128Gcm", () => { + describe("with DhkemP256HkdfSha256/HkdfSha256/Aes128Gcm", () => { it("should have ciphersuites", () => { const suite: CipherSuite = new CipherSuite({ kem: KemId.DhkemP256HkdfSha256, @@ -87,7 +86,7 @@ describe("CipherSuite", () => { }); // RFC9180 A.4. - describe("constructor with DhkemP256HkdfSha256/HkdfSha512/Aes128Gcm", () => { + describe("with DhkemP256HkdfSha256/HkdfSha512/Aes128Gcm", () => { it("should have ciphersuites", () => { const suite: CipherSuite = new CipherSuite({ kem: KemId.DhkemP256HkdfSha256, @@ -106,7 +105,7 @@ describe("CipherSuite", () => { }); // RFC9180 A.5. - describe("constructor with DhkemP256HkdfSha256/HkdfSha256/ChaCha20Poly1305", () => { + describe("with DhkemP256HkdfSha256/HkdfSha256/ChaCha20Poly1305", () => { it("should have ciphersuites", () => { const suite: CipherSuite = new CipherSuite({ kem: KemId.DhkemP256HkdfSha256, @@ -125,7 +124,7 @@ describe("CipherSuite", () => { }); // RFC9180 A.6. - describe("constructor with DhkemP521HkdfSha512/HkdfSha512/Aes256Gcm", () => { + describe("with DhkemP521HkdfSha512/HkdfSha512/Aes256Gcm", () => { it("should have ciphersuites", () => { const suite: CipherSuite = new CipherSuite({ kem: KemId.DhkemP521HkdfSha512, @@ -144,7 +143,7 @@ describe("CipherSuite", () => { }); // RFC9180 A.7. - describe("constructor with DhkemP256HkdfSha256/HkdfSha256/ExportOnly", () => { + describe("with DhkemP256HkdfSha256/HkdfSha256/ExportOnly", () => { it("should have ciphersuites", () => { const suite: CipherSuite = new CipherSuite({ kem: KemId.DhkemP256HkdfSha256, @@ -162,7 +161,7 @@ describe("CipherSuite", () => { }); }); - describe("constructor with DhkemSecp256KHkdfSha256/HkdfSha256/ExportOnly", () => { + describe("with DhkemSecp256KHkdfSha256/HkdfSha256/ExportOnly", () => { it("should throw InvalidParamError", async () => { // assert await assertThrows( @@ -177,9 +176,11 @@ describe("CipherSuite", () => { ); }); }); +}); - describe("A README example of Base mode", () => { - it("should work normally with ids", async () => { +describe("createRecipientContext", () => { + describe("with a private key as recipientKey", () => { + it("should work normally", async () => { // setup const suite = new CipherSuite({ kem: KemId.DhkemP256HkdfSha256, @@ -194,7 +195,7 @@ describe("CipherSuite", () => { }); const recipient = await suite.createRecipientContext({ - recipientKey: rkp, + recipientKey: rkp.privateKey, enc: sender.enc, }); @@ -208,44 +209,36 @@ describe("CipherSuite", () => { // assert assertEquals(new TextDecoder().decode(pt), "my-secret-message"); - await assertRejects(() => recipient.seal(pt), errors.NotSupportedError); - await assertRejects(() => sender.open(ct), errors.NotSupportedError); }); + }); - it("should work normally with instances", async () => { + describe("with too long info", () => { + it("should throw InvalidParamError", async () => { // setup const suite = new CipherSuite({ - kem: new DhkemP256HkdfSha256(), - kdf: new HkdfSha256(), - aead: new Aes128Gcm(), + kem: KemId.DhkemP256HkdfSha256, + kdf: KdfId.HkdfSha256, + aead: AeadId.Aes128Gcm, }); const rkp = await suite.kem.generateKeyPair(); - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - }); - - const recipient = await suite.createRecipientContext({ - recipientKey: rkp, - enc: sender.enc, - }); - - // encrypt - const ct = await sender.seal( - new TextEncoder().encode("my-secret-message"), + await assertRejects( + () => + suite.createSenderContext({ + info: (new Uint8Array(8193)).buffer, + recipientPublicKey: rkp.publicKey, + }), + errors.InvalidParamError, + "Too long info", ); - - // decrypt - const pt = await recipient.open(ct); - - // assert - assertEquals(new TextDecoder().decode(pt), "my-secret-message"); - await assertRejects(() => recipient.seal(pt), errors.NotSupportedError); - await assertRejects(() => sender.open(ct), errors.NotSupportedError); }); + }); +}); - it("should work normally with importKey('jwk')", async () => { +describe("createSenderContext", () => { + describe("with a privatekey as senderKey", () => { + it("should work normally", async () => { // setup const suite = new CipherSuite({ kem: KemId.DhkemP256HkdfSha256, @@ -253,33 +246,18 @@ describe("CipherSuite", () => { aead: AeadId.Aes128Gcm, }); - const jwkPkR = { - kty: "EC", - crv: "P-256", - kid: "P-256-01", - x: "-eZXC6nV-xgthy8zZMCN8pcYSeE2XfWWqckA2fsxHPc", - y: "BGU5soLgsu_y7GN2I3EPUXS9EZ7Sw0qif-V70JtInFI", - key_ops: [], - }; - const pkR = await suite.kem.importKey("jwk", jwkPkR, true); + const rkp = await suite.kem.generateKeyPair(); + const skp = await suite.kem.generateKeyPair(); const sender = await suite.createSenderContext({ - recipientPublicKey: pkR, + recipientPublicKey: rkp.publicKey, + senderKey: skp.privateKey, }); - const jwkSkR = { - kty: "EC", - crv: "P-256", - kid: "P-256-01", - x: "-eZXC6nV-xgthy8zZMCN8pcYSeE2XfWWqckA2fsxHPc", - y: "BGU5soLgsu_y7GN2I3EPUXS9EZ7Sw0qif-V70JtInFI", - d: "kwibx3gas6Kz1V2fyQHKSnr-ybflddSjN0eOnbmLmyo", - key_ops: ["deriveBits"], - }; - const skR = await suite.kem.importKey("jwk", jwkSkR, false); const recipient = await suite.createRecipientContext({ - recipientKey: skR, + recipientKey: rkp, enc: sender.enc, + senderPublicKey: skp.publicKey, }); // encrypt @@ -292,171 +270,161 @@ describe("CipherSuite", () => { // assert assertEquals(new TextDecoder().decode(pt), "my-secret-message"); - await assertRejects(() => recipient.seal(pt), errors.NotSupportedError); - await assertRejects(() => sender.open(ct), errors.NotSupportedError); }); }); - describe("A README example of Base mode (KemId.DhkemP384HkdfSha384/KdfId.HkdfSha384)", () => { - it("should work normally with ids", async () => { + describe("with too long psk.key", () => { + it("should throw InvalidParamError", async () => { // setup const suite = new CipherSuite({ - kem: KemId.DhkemP384HkdfSha384, - kdf: KdfId.HkdfSha384, + kem: KemId.DhkemP256HkdfSha256, + kdf: KdfId.HkdfSha256, aead: AeadId.Aes128Gcm, }); const rkp = await suite.kem.generateKeyPair(); - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - }); - - const recipient = await suite.createRecipientContext({ - recipientKey: rkp, - enc: sender.enc, - }); - - // encrypt - const ct = await sender.seal( - new TextEncoder().encode("my-secret-message"), + await assertRejects( + () => + suite.createSenderContext({ + psk: { + key: (new Uint8Array(8193)).buffer, + id: new Uint8Array([1, 2, 3, 4]), + }, + recipientPublicKey: rkp.publicKey, + }), + errors.InvalidParamError, + "Too long psk.key", ); - - // decrypt - const pt = await recipient.open(ct); - - // assert - assertEquals(new TextDecoder().decode(pt), "my-secret-message"); }); + }); - it("should work normally with instances", async () => { + describe("with short psk.key", () => { + it("should throw InvalidParamError", async () => { // setup const suite = new CipherSuite({ - kem: new DhkemP384HkdfSha384(), - kdf: new HkdfSha384(), + kem: KemId.DhkemP256HkdfSha256, + kdf: KdfId.HkdfSha256, aead: AeadId.Aes128Gcm, }); const rkp = await suite.kem.generateKeyPair(); - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - }); - - const recipient = await suite.createRecipientContext({ - recipientKey: rkp, - enc: sender.enc, - }); - - // encrypt - const ct = await sender.seal( - new TextEncoder().encode("my-secret-message"), + await assertRejects( + () => + suite.createSenderContext({ + psk: { + key: (new Uint8Array(31)).buffer, + id: new Uint8Array([1, 2, 3, 4]), + }, + recipientPublicKey: rkp.publicKey, + }), + errors.InvalidParamError, + "PSK must have at least 32 bytes", ); - - // decrypt - const pt = await recipient.open(ct); - - // assert - assertEquals(new TextDecoder().decode(pt), "my-secret-message"); }); + }); - it("should work normally with importKey('jwk')", async () => { + describe("with too long psk.id", () => { + it("should throw InvalidParamError", async () => { // setup const suite = new CipherSuite({ - kem: KemId.DhkemP384HkdfSha384, - kdf: KdfId.HkdfSha384, + kem: KemId.DhkemP256HkdfSha256, + kdf: KdfId.HkdfSha256, aead: AeadId.Aes128Gcm, }); - const jwkPkR = { - kty: "EC", - crv: "P-384", - kid: "P-384-01", - x: "_XyN9woHaS0mPimSW-etwJMEDSzxIMjp4PjezavU8SHJoClz1bQrcmPb1ZJxHxhI", - y: "GCNfc32p9sRotx7u2oDGJ3Eqz6q5zPHLdizNn83oRsUTN31eCWfGLHWRury3xF50", - key_ops: [], - }; - const pkR = await suite.kem.importKey("jwk", jwkPkR, true); + const rkp = await suite.kem.generateKeyPair(); - const sender = await suite.createSenderContext({ - recipientPublicKey: pkR, - }); + await assertRejects( + () => + suite.createSenderContext({ + psk: { + key: new Uint8Array(32), + id: (new Uint8Array(8193)).buffer, + }, + recipientPublicKey: rkp.publicKey, + }), + errors.InvalidParamError, + "Too long psk.id", + ); + }); + }); +}); - const jwkSkR = { - kty: "EC", - crv: "P-384", - kid: "P-384-01", - x: "_XyN9woHaS0mPimSW-etwJMEDSzxIMjp4PjezavU8SHJoClz1bQrcmPb1ZJxHxhI", - y: "GCNfc32p9sRotx7u2oDGJ3Eqz6q5zPHLdizNn83oRsUTN31eCWfGLHWRury3xF50", - d: "1pImEKbrr771-RKi8Tb7tou_WjiR7kwui_nMu16449rk3lzAqf9buUhTkJ-pogkb", - key_ops: ["deriveBits"], - }; - const skR = await suite.kem.importKey("jwk", jwkSkR, false); - const recipient = await suite.createRecipientContext({ - recipientKey: skR, - enc: sender.enc, +describe("seal/open", () => { + describe("with DhkemP256HkdfSha256", () => { + it("should work normally", async () => { + // setup + const suite = new CipherSuite({ + kem: KemId.DhkemP256HkdfSha256, + kdf: KdfId.HkdfSha256, + aead: AeadId.Aes128Gcm, }); + const rkp = await suite.kem.generateKeyPair(); + // encrypt - const ct = await sender.seal( + const { ct, enc } = await suite.seal( + { + recipientPublicKey: rkp.publicKey, + }, new TextEncoder().encode("my-secret-message"), ); // decrypt - const pt = await recipient.open(ct); + const pt = await suite.open( + { + recipientKey: rkp, + enc: enc, + }, + ct, + ); // assert assertEquals(new TextDecoder().decode(pt), "my-secret-message"); - await assertRejects(() => recipient.seal(pt), errors.NotSupportedError); - await assertRejects(() => sender.open(ct), errors.NotSupportedError); }); }); - describe("A README example of Base mode (KemId.DhkemP521HkdfSha512/KdfId.HkdfSha512)", () => { - it("should work normally with ids", async () => { - if (isDeno()) { - return; - } - + describe("with DhkemX25519HkdfSha256", () => { + it("should work normally.", async () => { // setup const suite = new CipherSuite({ - kem: KemId.DhkemP521HkdfSha512, - kdf: KdfId.HkdfSha512, + kem: KemId.DhkemX25519HkdfSha256, + kdf: KdfId.HkdfSha256, aead: AeadId.Aes128Gcm, }); const rkp = await suite.kem.generateKeyPair(); - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - }); - - const recipient = await suite.createRecipientContext({ - recipientKey: rkp, - enc: sender.enc, - }); - // encrypt - const ct = await sender.seal( + const { ct, enc } = await suite.seal( + { + recipientPublicKey: rkp.publicKey, + }, new TextEncoder().encode("my-secret-message"), ); // decrypt - const pt = await recipient.open(ct); + const pt = await suite.open( + { + recipientKey: rkp, + enc: enc, + }, + ct, + ); // assert assertEquals(new TextDecoder().decode(pt), "my-secret-message"); }); + }); - it("should work normally with instances", async () => { - if (isDeno()) { - return; - } - + describe("seal with empty byte string", () => { + it("should work normally", async () => { // setup const suite = new CipherSuite({ - kem: new DhkemP521HkdfSha512(), - kdf: new HkdfSha512(), + kem: KemId.DhkemP256HkdfSha256, + kdf: KdfId.HkdfSha256, aead: AeadId.Aes128Gcm, }); @@ -472,894 +440,63 @@ describe("CipherSuite", () => { }); // encrypt - const ct = await sender.seal( - new TextEncoder().encode("my-secret-message"), - ); + const ct = await sender.seal(new TextEncoder().encode("")); // decrypt const pt = await recipient.open(ct); // assert - assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + assertEquals(new TextDecoder().decode(pt), ""); }); + }); +}); - it("should work normally with importKey('jwk')", async () => { - if (isDeno()) { - return; - } - +describe("deriveKeyPair", () => { + describe("with too long ikm", () => { + it("should throw InvalidParamError", async () => { // setup const suite = new CipherSuite({ - kem: KemId.DhkemP521HkdfSha512, - kdf: KdfId.HkdfSha512, + kem: KemId.DhkemP256HkdfSha256, + kdf: KdfId.HkdfSha256, aead: AeadId.Aes128Gcm, }); - const jwkPkR = { - kty: "EC", - crv: "P-521", - kid: "P-521-01", - x: "APkZitSJMJUMB-iPCt47sWu_CrnUHg6IAR4qjmHON-2u41Rjg6DNOS0LZYJJt-AVH5NgGVi8ElIfjo71b9HXCTOc", - y: "ASx-Cb--149HJ-e1KlSaY-1BOhwOdcTkxSt8BGbW7_hnGfzHsoXM3ywwNcp1Yad-FHUKwmCyMelMQEn2Rh4V2l3I", - key_ops: [], - }; - const pkR = await suite.kem.importKey("jwk", jwkPkR, true); - - const sender = await suite.createSenderContext({ - recipientPublicKey: pkR, - }); - - const jwkSkR = { - kty: "EC", - crv: "P-521", - kid: "P-521-01", - x: "APkZitSJMJUMB-iPCt47sWu_CrnUHg6IAR4qjmHON-2u41Rjg6DNOS0LZYJJt-AVH5NgGVi8ElIfjo71b9HXCTOc", - y: "ASx-Cb--149HJ-e1KlSaY-1BOhwOdcTkxSt8BGbW7_hnGfzHsoXM3ywwNcp1Yad-FHUKwmCyMelMQEn2Rh4V2l3I", - d: "ADYyo73ZKicOjwGDYQ_ybZKnVzdAcxGm9OVAxQjzgVM4jaS-Iwtkz90oLdDz3shgKlDgtRK2Aa9lMhqR94hBo4IE", - key_ops: ["deriveBits"], - }; - const skR = await suite.kem.importKey("jwk", jwkSkR, false); - const recipient = await suite.createRecipientContext({ - recipientKey: skR, - enc: sender.enc, - }); - - // encrypt - const ct = await sender.seal( - new TextEncoder().encode("my-secret-message"), + await assertRejects( + () => suite.kem.deriveKeyPair((new Uint8Array(8193)).buffer), + errors.InvalidParamError, + "Too long ikm", ); - - // decrypt - const pt = await recipient.open(ct); - - // assert - assertEquals(new TextDecoder().decode(pt), "my-secret-message"); - await assertRejects(() => recipient.seal(pt), errors.NotSupportedError); - await assertRejects(() => sender.open(ct), errors.NotSupportedError); }); }); +}); - describe("A README example of Base mode (KemId.DhkemX25519HkdfSha256/KdfId.HkdfSha256)", () => { - it("should work normally with ids", async () => { +describe("importKey", () => { + describe("with invalid EC(P-256) public key", () => { + it("should throw DeserializeError", async () => { // setup const suite = new CipherSuite({ - kem: KemId.DhkemX25519HkdfSha256, + kem: KemId.DhkemP256HkdfSha256, kdf: KdfId.HkdfSha256, aead: AeadId.Aes128Gcm, }); - const rkp = await suite.kem.generateKeyPair(); - - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - }); - - const recipient = await suite.createRecipientContext({ - recipientKey: rkp, - enc: sender.enc, - }); - - // encrypt - const ct = await sender.seal( - new TextEncoder().encode("my-secret-message"), - ); - - // decrypt - const pt = await recipient.open(ct); + const kStr = "aabbccddeeff"; + const k = hexStringToBytes(kStr); // assert - assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + await assertRejects( + () => suite.kem.importKey("raw", k), + errors.DeserializeError, + ); }); + }); - it("should work normally with instances", async () => { + describe("with invalid EC(P-256) private key", () => { + it("should throw DeserializeError", async () => { // setup const suite = new CipherSuite({ - kem: new DhkemX25519HkdfSha256(), - kdf: new HkdfSha256(), - aead: AeadId.Aes128Gcm, - }); - - const rkp = await suite.kem.generateKeyPair(); - - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - }); - - const recipient = await suite.createRecipientContext({ - recipientKey: rkp, - enc: sender.enc, - }); - - // encrypt - const ct = await sender.seal( - new TextEncoder().encode("my-secret-message"), - ); - - // decrypt - const pt = await recipient.open(ct); - - // assert - assertEquals(new TextDecoder().decode(pt), "my-secret-message"); - }); - - it("should work normally with importKey('jwk')", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemX25519HkdfSha256, - kdf: KdfId.HkdfSha256, - aead: AeadId.Aes128Gcm, - }); - - const jwkPkR = { - kty: "OKP", - crv: "X25519", - kid: "X25519-01", - x: "y3wJq3uXPHeoCO4FubvTc7VcBuqpvUrSvU6ZMbHDTCI", - key_ops: [], - }; - const pkR = await suite.kem.importKey("jwk", jwkPkR, true); - - const sender = await suite.createSenderContext({ - recipientPublicKey: pkR, - }); - - const jwkSkR = { - kty: "OKP", - crv: "X25519", - kid: "X25519-01", - x: "y3wJq3uXPHeoCO4FubvTc7VcBuqpvUrSvU6ZMbHDTCI", - d: "vsJ1oX5NNi0IGdwGldiac75r-Utmq3Jq4LGv48Q_Qc4", - key_ops: ["deriveBits"], - }; - const skR = await suite.kem.importKey("jwk", jwkSkR, false); - const recipient = await suite.createRecipientContext({ - recipientKey: skR, - enc: sender.enc, - }); - - // encrypt - const ct = await sender.seal( - new TextEncoder().encode("my-secret-message"), - ); - - // decrypt - const pt = await recipient.open(ct); - - // assert - assertEquals(new TextDecoder().decode(pt), "my-secret-message"); - await assertRejects(() => recipient.seal(pt), errors.NotSupportedError); - await assertRejects(() => sender.open(ct), errors.NotSupportedError); - }); - }); - - describe("A README example of Base mode (KemId.DhkemX448HkdfSha256/KdfId.HkdfSha512)", () => { - it("should work normally with ids", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemX448HkdfSha512, - kdf: KdfId.HkdfSha512, - aead: AeadId.Aes256Gcm, - }); - - const rkp = await suite.kem.generateKeyPair(); - - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - }); - - const recipient = await suite.createRecipientContext({ - recipientKey: rkp, - enc: sender.enc, - }); - - // encrypt - const ct = await sender.seal( - new TextEncoder().encode("my-secret-message"), - ); - - // decrypt - const pt = await recipient.open(ct); - - // assert - assertEquals(new TextDecoder().decode(pt), "my-secret-message"); - }); - - it("should work normally with instances", async () => { - // setup - const suite = new CipherSuite({ - kem: new DhkemX448HkdfSha512(), - kdf: new HkdfSha512(), - aead: AeadId.Aes256Gcm, - }); - - const rkp = await suite.kem.generateKeyPair(); - - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - }); - - const recipient = await suite.createRecipientContext({ - recipientKey: rkp, - enc: sender.enc, - }); - - // encrypt - const ct = await sender.seal( - new TextEncoder().encode("my-secret-message"), - ); - - // decrypt - const pt = await recipient.open(ct); - - // assert - assertEquals(new TextDecoder().decode(pt), "my-secret-message"); - }); - - it("should work normally with importKey('jwk')", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemX448HkdfSha512, - kdf: KdfId.HkdfSha512, - aead: AeadId.Aes256Gcm, - }); - - const jwkPkR = { - kty: "OKP", - crv: "X448", - kid: "X448-01", - x: "IkLmc0klvEMXYneHMKAB6ePohryAwAPVe2pRSffIDY6NrjeYNWVX5J-fG4NV2OoU77C88A0mvxI", - key_ops: [], - }; - const pkR = await suite.kem.importKey("jwk", jwkPkR, true); - - const sender = await suite.createSenderContext({ - recipientPublicKey: pkR, - }); - - const jwkSkR = { - kty: "OKP", - crv: "X448", - kid: "X448-01", - x: "IkLmc0klvEMXYneHMKAB6ePohryAwAPVe2pRSffIDY6NrjeYNWVX5J-fG4NV2OoU77C88A0mvxI", - d: "rJJRG3nshyCtd9CgXld8aNaB9YXKR0UOi7zj7hApg9YH4XdBO0G8NcAFNz_uPH2GnCZVcSDgV5c", - key_ops: ["deriveBits"], - }; - const skR = await suite.kem.importKey("jwk", jwkSkR, false); - const recipient = await suite.createRecipientContext({ - recipientKey: skR, - enc: sender.enc, - }); - - // encrypt - const ct = await sender.seal( - new TextEncoder().encode("my-secret-message"), - ); - - // decrypt - const pt = await recipient.open(ct); - - // assert - assertEquals(new TextDecoder().decode(pt), "my-secret-message"); - await assertRejects(() => recipient.seal(pt), errors.NotSupportedError); - await assertRejects(() => sender.open(ct), errors.NotSupportedError); - }); - }); - - describe("A README example of Base mode (ExportOnly)", () => { - it("should work normally", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemP256HkdfSha256, - kdf: KdfId.HkdfSha256, - aead: AeadId.ExportOnly, - }); - - const rkp = await suite.kem.generateKeyPair(); - - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - }); - - const recipient = await suite.createRecipientContext({ - recipientKey: rkp, - enc: sender.enc, - }); - - const te = new TextEncoder(); - - // export - const pskS = sender.export(te.encode("jugemujugemu"), 32); - const pskR = recipient.export(te.encode("jugemujugemu"), 32); - assertEquals(pskR, pskS); - - // other functions are disabled. - await assertRejects( - () => sender.seal(te.encode("my-secret-message")), - errors.NotSupportedError, - ); - await assertRejects( - () => sender.open(te.encode("xxxxxxxxxxxxxxxxx")), - errors.NotSupportedError, - ); - }); - }); - - describe("A README example of Base mode (ExportOnly/X25519)", () => { - it("should work normally", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemX25519HkdfSha256, - kdf: KdfId.HkdfSha256, - aead: AeadId.ExportOnly, - }); - - const rkp = await suite.kem.generateKeyPair(); - - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - }); - - const recipient = await suite.createRecipientContext({ - recipientKey: rkp, - enc: sender.enc, - }); - - const te = new TextEncoder(); - - // export - const pskS = sender.export(te.encode("jugemujugemu"), 32); - const pskR = recipient.export(te.encode("jugemujugemu"), 32); - assertEquals(pskR, pskS); - - // other functions are disabled. - await assertRejects( - () => sender.seal(te.encode("my-secret-message")), - errors.NotSupportedError, - ); - await assertRejects( - () => sender.open(te.encode("xxxxxxxxxxxxxxxxx")), - errors.NotSupportedError, - ); - }); - }); - - describe("A README example of PSK mode", () => { - it("should work normally", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemP256HkdfSha256, - kdf: KdfId.HkdfSha256, - aead: AeadId.Aes128Gcm, - }); - - const rkp = await suite.kem.generateKeyPair(); - - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - psk: { - id: new TextEncoder().encode("our-pre-shared-key-id"), - key: new TextEncoder().encode("jugemujugemugokounosurikirekaija"), - }, - }); - - const recipient = await suite.createRecipientContext({ - recipientKey: rkp, - enc: sender.enc, - psk: { - id: new TextEncoder().encode("our-pre-shared-key-id"), - key: new TextEncoder().encode("jugemujugemugokounosurikirekaija"), - }, - }); - - // encrypt - const ct = await sender.seal( - new TextEncoder().encode("my-secret-message"), - ); - - // decrypt - const pt = await recipient.open(ct); - - // assert - assertEquals(new TextDecoder().decode(pt), "my-secret-message"); - }); - }); - - describe("A README example of Auth mode", () => { - it("should work normally", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemP256HkdfSha256, - kdf: KdfId.HkdfSha256, - aead: AeadId.Aes128Gcm, - }); - - const rkp = await suite.kem.generateKeyPair(); - const skp = await suite.kem.generateKeyPair(); - - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - senderKey: skp, - }); - - const recipient = await suite.createRecipientContext({ - recipientKey: rkp, - enc: sender.enc, - senderPublicKey: skp.publicKey, - }); - - // encrypt - const ct = await sender.seal( - new TextEncoder().encode("my-secret-message"), - ); - - // decrypt - const pt = await recipient.open(ct); - - // assert - assertEquals(new TextDecoder().decode(pt), "my-secret-message"); - }); - }); - - describe("A README example of AuthPSK mode", () => { - it("should work normally", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemP256HkdfSha256, - kdf: KdfId.HkdfSha256, - aead: AeadId.Aes128Gcm, - }); - - const rkp = await suite.kem.generateKeyPair(); - const skp = await suite.kem.generateKeyPair(); - - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - senderKey: skp, - psk: { - id: new TextEncoder().encode("our-pre-shared-key-id"), - key: new TextEncoder().encode("jugemujugemugokounosurikirekaija"), - }, - }); - - const recipient = await suite.createRecipientContext({ - recipientKey: rkp, - enc: sender.enc, - senderPublicKey: skp.publicKey, - psk: { - id: new TextEncoder().encode("our-pre-shared-key-id"), - key: new TextEncoder().encode("jugemujugemugokounosurikirekaija"), - }, - }); - - // encrypt - const ct = await sender.seal( - new TextEncoder().encode("my-secret-message"), - ); - - // decrypt - const pt = await recipient.open(ct); - - // assert - assertEquals(new TextDecoder().decode(pt), "my-secret-message"); - }); - }); - - describe("A README example of AuthPSK mode (X25519)", () => { - it("should work normally", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemX25519HkdfSha256, - kdf: KdfId.HkdfSha256, - aead: AeadId.Aes128Gcm, - }); - - const rkp = await suite.kem.generateKeyPair(); - const skp = await suite.kem.generateKeyPair(); - - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - senderKey: skp, - psk: { - id: new TextEncoder().encode("our-pre-shared-key-id"), - key: new TextEncoder().encode("jugemujugemugokounosurikirekaija"), - }, - }); - - const recipient = await suite.createRecipientContext({ - recipientKey: rkp, - enc: sender.enc, - senderPublicKey: skp.publicKey, - psk: { - id: new TextEncoder().encode("our-pre-shared-key-id"), - key: new TextEncoder().encode("jugemujugemugokounosurikirekaija"), - }, - }); - - // encrypt - const ct = await sender.seal( - new TextEncoder().encode("my-secret-message"), - ); - - // decrypt - const pt = await recipient.open(ct); - - // assert - assertEquals(new TextDecoder().decode(pt), "my-secret-message"); - }); - }); - - describe("A README example of AuthPSK mode (X448)", () => { - it("should work normally", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemX448HkdfSha512, - kdf: KdfId.HkdfSha256, - aead: AeadId.Aes128Gcm, - }); - - const rkp = await suite.kem.generateKeyPair(); - const skp = await suite.kem.generateKeyPair(); - - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - senderKey: skp, - psk: { - id: new TextEncoder().encode("our-pre-shared-key-id"), - key: new TextEncoder().encode("jugemujugemugokounosurikirekaija"), - }, - }); - - const recipient = await suite.createRecipientContext({ - recipientKey: rkp, - enc: sender.enc, - senderPublicKey: skp.publicKey, - psk: { - id: new TextEncoder().encode("our-pre-shared-key-id"), - key: new TextEncoder().encode("jugemujugemugokounosurikirekaija"), - }, - }); - - // encrypt - const ct = await sender.seal( - new TextEncoder().encode("my-secret-message"), - ); - - // decrypt - const pt = await recipient.open(ct); - - // assert - assertEquals(new TextDecoder().decode(pt), "my-secret-message"); - }); - }); - - describe("createRecipientContext with a private key as recipientKey", () => { - it("should work normally", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemP256HkdfSha256, - kdf: KdfId.HkdfSha256, - aead: AeadId.Aes128Gcm, - }); - - const rkp = await suite.kem.generateKeyPair(); - - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - }); - - const recipient = await suite.createRecipientContext({ - recipientKey: rkp.privateKey, - enc: sender.enc, - }); - - // encrypt - const ct = await sender.seal( - new TextEncoder().encode("my-secret-message"), - ); - - // decrypt - const pt = await recipient.open(ct); - - // assert - assertEquals(new TextDecoder().decode(pt), "my-secret-message"); - }); - }); - - describe("createSenderContext with a privatekey as senderKey", () => { - it("should work normally", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemP256HkdfSha256, - kdf: KdfId.HkdfSha256, - aead: AeadId.Aes128Gcm, - }); - - const rkp = await suite.kem.generateKeyPair(); - const skp = await suite.kem.generateKeyPair(); - - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - senderKey: skp.privateKey, - }); - - const recipient = await suite.createRecipientContext({ - recipientKey: rkp, - enc: sender.enc, - senderPublicKey: skp.publicKey, - }); - - // encrypt - const ct = await sender.seal( - new TextEncoder().encode("my-secret-message"), - ); - - // decrypt - const pt = await recipient.open(ct); - - // assert - assertEquals(new TextDecoder().decode(pt), "my-secret-message"); - }); - }); - - describe("seal and open (single-shot apis)", () => { - it("should work normally", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemP256HkdfSha256, - kdf: KdfId.HkdfSha256, - aead: AeadId.Aes128Gcm, - }); - - const rkp = await suite.kem.generateKeyPair(); - - // encrypt - const { ct, enc } = await suite.seal( - { - recipientPublicKey: rkp.publicKey, - }, - new TextEncoder().encode("my-secret-message"), - ); - - // decrypt - const pt = await suite.open( - { - recipientKey: rkp, - enc: enc, - }, - ct, - ); - - // assert - assertEquals(new TextDecoder().decode(pt), "my-secret-message"); - }); - - it("should work normally (X25519)", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemX25519HkdfSha256, - kdf: KdfId.HkdfSha256, - aead: AeadId.Aes128Gcm, - }); - - const rkp = await suite.kem.generateKeyPair(); - - // encrypt - const { ct, enc } = await suite.seal( - { - recipientPublicKey: rkp.publicKey, - }, - new TextEncoder().encode("my-secret-message"), - ); - - // decrypt - const pt = await suite.open( - { - recipientKey: rkp, - enc: enc, - }, - ct, - ); - - // assert - assertEquals(new TextDecoder().decode(pt), "my-secret-message"); - }); - }); - - describe("seal empty byte string", () => { - it("should work normally", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemP256HkdfSha256, - kdf: KdfId.HkdfSha256, - aead: AeadId.Aes128Gcm, - }); - - const rkp = await suite.kem.generateKeyPair(); - - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - }); - - const recipient = await suite.createRecipientContext({ - recipientKey: rkp, - enc: sender.enc, - }); - - // encrypt - const ct = await sender.seal(new TextEncoder().encode("")); - - // decrypt - const pt = await recipient.open(ct); - - // assert - assertEquals(new TextDecoder().decode(pt), ""); - }); - }); - - describe("deriveKeyPair with too long ikm", () => { - it("should throw InvalidParamError", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemP256HkdfSha256, - kdf: KdfId.HkdfSha256, - aead: AeadId.Aes128Gcm, - }); - - await assertRejects( - () => suite.kem.deriveKeyPair((new Uint8Array(8193)).buffer), - errors.InvalidParamError, - "Too long ikm", - ); - }); - }); - - describe("createSenderContext with too long info", () => { - it("should throw InvalidParamError", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemP256HkdfSha256, - kdf: KdfId.HkdfSha256, - aead: AeadId.Aes128Gcm, - }); - - const rkp = await suite.kem.generateKeyPair(); - - await assertRejects( - () => - suite.createSenderContext({ - info: (new Uint8Array(8193)).buffer, - recipientPublicKey: rkp.publicKey, - }), - errors.InvalidParamError, - "Too long info", - ); - }); - }); - - describe("createSenderContext with too long psk.key", () => { - it("should throw InvalidParamError", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemP256HkdfSha256, - kdf: KdfId.HkdfSha256, - aead: AeadId.Aes128Gcm, - }); - - const rkp = await suite.kem.generateKeyPair(); - - await assertRejects( - () => - suite.createSenderContext({ - psk: { - key: (new Uint8Array(8193)).buffer, - id: new Uint8Array([1, 2, 3, 4]), - }, - recipientPublicKey: rkp.publicKey, - }), - errors.InvalidParamError, - "Too long psk.key", - ); - }); - }); - - describe("createSenderContext with short psk.key", () => { - it("should throw InvalidParamError", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemP256HkdfSha256, - kdf: KdfId.HkdfSha256, - aead: AeadId.Aes128Gcm, - }); - - const rkp = await suite.kem.generateKeyPair(); - - await assertRejects( - () => - suite.createSenderContext({ - psk: { - key: (new Uint8Array(31)).buffer, - id: new Uint8Array([1, 2, 3, 4]), - }, - recipientPublicKey: rkp.publicKey, - }), - errors.InvalidParamError, - "PSK must have at least 32 bytes", - ); - }); - }); - - describe("createSenderContext with too long psk.id", () => { - it("should throw InvalidParamError", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemP256HkdfSha256, - kdf: KdfId.HkdfSha256, - aead: AeadId.Aes128Gcm, - }); - - const rkp = await suite.kem.generateKeyPair(); - - await assertRejects( - () => - suite.createSenderContext({ - psk: { - key: new Uint8Array(32), - id: (new Uint8Array(8193)).buffer, - }, - recipientPublicKey: rkp.publicKey, - }), - errors.InvalidParamError, - "Too long psk.id", - ); - }); - }); - - describe("importKey with invalid EC(P-256) public key", () => { - it("should throw DeserializeError", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemP256HkdfSha256, - kdf: KdfId.HkdfSha256, - aead: AeadId.Aes128Gcm, - }); - - const kStr = "aabbccddeeff"; - const k = hexStringToBytes(kStr); - - // assert - await assertRejects( - () => suite.kem.importKey("raw", k), - errors.DeserializeError, - ); - }); - }); - - describe("importKey with invalid EC(P-256) private key", () => { - it("should throw DeserializeError", async () => { - // setup - const suite = new CipherSuite({ - kem: KemId.DhkemP256HkdfSha256, - kdf: KdfId.HkdfSha256, + kem: KemId.DhkemP256HkdfSha256, + kdf: KdfId.HkdfSha256, aead: AeadId.Aes128Gcm, }); @@ -1374,7 +511,7 @@ describe("CipherSuite", () => { }); }); - describe("importKey with invalid x25519 public key", () => { + describe("with invalid x25519 public key", () => { it("should throw DeserializeError", async () => { // setup const suite = new CipherSuite({ @@ -1394,7 +531,7 @@ describe("CipherSuite", () => { }); }); - describe("importKey with invalid x25519 private key", () => { + describe("with invalid x25519 private key", () => { it("should throw DeserializeError", async () => { // setup const suite = new CipherSuite({ @@ -1414,7 +551,7 @@ describe("CipherSuite", () => { }); }); - describe("importKey with invalid x448 public key", () => { + describe("with invalid x448 public key", () => { it("should throw DeserializeError", async () => { // setup const suite = new CipherSuite({ @@ -1434,7 +571,7 @@ describe("CipherSuite", () => { }); }); - describe("importKey with invalid x448 private key", () => { + describe("with invalid x448 private key", () => { it("should throw DeserializeError", async () => { // setup const suite = new CipherSuite({ @@ -1453,268 +590,4 @@ describe("CipherSuite", () => { ); }); }); - - describe("A README example of Oblivious HTTP", () => { - it("should work normally", async () => { - const te = new TextEncoder(); - const cryptoApi = await loadCrypto(); - - const suite = new CipherSuite({ - kem: KemId.DhkemP256HkdfSha256, - kdf: KdfId.HkdfSha256, - aead: AeadId.Aes128Gcm, - }); - const rkp = await suite.kem.generateKeyPair(); - - // The sender (OHTTP client) side: - const response = te.encode("This is the response."); - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - }); - - const secretS = await sender.export( - te.encode("message/bhttp response"), - suite.aead.keySize, - ); - - const responseNonce = new Uint8Array(suite.aead.keySize); - cryptoApi.getRandomValues(responseNonce); - const saltS = concat(new Uint8Array(sender.enc), responseNonce); - - const kdfS = suite.kdf; - const prkS = await kdfS.extract(saltS, new Uint8Array(secretS)); - const keyS = await kdfS.expand( - prkS, - te.encode("key"), - suite.aead.keySize, - ); - const nonceS = await kdfS.expand( - prkS, - te.encode("nonce"), - suite.aead.nonceSize, - ); - - const aeadKeyS = await suite.aead.createEncryptionContext(keyS); - const ct = await aeadKeyS.seal(nonceS, response, te.encode("")); - const encResponse = concat(responseNonce, new Uint8Array(ct)); - - // The recipient (OHTTP server) side: - const recipient = await suite.createRecipientContext({ - recipientKey: rkp.privateKey, - enc: sender.enc, - }); - - const secretR = await recipient.export( - te.encode("message/bhttp response"), - suite.aead.keySize, - ); - - const saltR = concat( - new Uint8Array(sender.enc), - encResponse.slice(0, suite.aead.keySize), - ); - const kdfR = suite.kdf; - const prkR = await kdfR.extract( - saltR, - new Uint8Array(secretR), - ); - const keyR = await kdfR.expand( - prkR, - te.encode("key"), - suite.aead.keySize, - ); - const nonceR = await kdfR.expand( - prkR, - te.encode("nonce"), - suite.aead.nonceSize, - ); - const aeadKeyR = await suite.aead.createEncryptionContext(keyR); - const pt = await aeadKeyR.open( - nonceR, - encResponse.slice(suite.aead.keySize), - te.encode(""), - ); - - // pt === "This is the response." - assertEquals(response, new Uint8Array(pt)); - }); - }); - - describe("A README example of Oblivious HTTP (HKDF-SHA384)", () => { - it("should work normally", async () => { - if (isDeno()) { - return; - } - const te = new TextEncoder(); - const cryptoApi = await loadCrypto(); - - const suite = new CipherSuite({ - kem: KemId.DhkemP384HkdfSha384, - kdf: KdfId.HkdfSha384, - aead: AeadId.Aes256Gcm, - }); - const rkp = await suite.kem.generateKeyPair(); - - // The sender (OHTTP client) side: - const response = te.encode("This is the response."); - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - }); - - const secretS = await sender.export( - te.encode("message/bhttp response"), - suite.aead.keySize, - ); - - const responseNonce = new Uint8Array(suite.aead.keySize); - cryptoApi.getRandomValues(responseNonce); - const saltS = concat(new Uint8Array(sender.enc), responseNonce); - - const kdfS = suite.kdf; - const prkS = await kdfS.extract(saltS, new Uint8Array(secretS)); - const keyS = await kdfS.expand( - prkS, - te.encode("key"), - suite.aead.keySize, - ); - const nonceS = await kdfS.expand( - prkS, - te.encode("nonce"), - suite.aead.nonceSize, - ); - - const aeadKeyS = await suite.aead.createEncryptionContext(keyS); - const ct = await aeadKeyS.seal(nonceS, response, te.encode("")); - const encResponse = concat(responseNonce, new Uint8Array(ct)); - - // The recipient (OHTTP server) side: - const recipient = await suite.createRecipientContext({ - recipientKey: rkp.privateKey, - enc: sender.enc, - }); - - const secretR = await recipient.export( - te.encode("message/bhttp response"), - suite.aead.keySize, - ); - - const saltR = concat( - new Uint8Array(sender.enc), - encResponse.slice(0, suite.aead.keySize), - ); - const kdfR = suite.kdf; - const prkR = await kdfR.extract( - saltR, - new Uint8Array(secretR), - ); - const keyR = await kdfR.expand( - prkR, - te.encode("key"), - suite.aead.keySize, - ); - const nonceR = await kdfR.expand( - prkR, - te.encode("nonce"), - suite.aead.nonceSize, - ); - const aeadKeyR = await suite.aead.createEncryptionContext(keyR); - const pt = await aeadKeyR.open( - nonceR, - encResponse.slice(suite.aead.keySize), - te.encode(""), - ); - - // pt === "This is the response." - assertEquals(response, new Uint8Array(pt)); - }); - }); - - describe("A README example of Oblivious HTTP (HKDF-SHA512)", () => { - it("should work normally", async () => { - if (isDeno()) { - return; - } - const te = new TextEncoder(); - const cryptoApi = await loadCrypto(); - - const suite = new CipherSuite({ - kem: KemId.DhkemP521HkdfSha512, - kdf: KdfId.HkdfSha512, - aead: AeadId.Aes256Gcm, - }); - const rkp = await suite.kem.generateKeyPair(); - - // The sender (OHTTP client) side: - const response = te.encode("This is the response."); - const sender = await suite.createSenderContext({ - recipientPublicKey: rkp.publicKey, - }); - - const secretS = await sender.export( - te.encode("message/bhttp response"), - suite.aead.keySize, - ); - - const responseNonce = new Uint8Array(suite.aead.keySize); - cryptoApi.getRandomValues(responseNonce); - const saltS = concat(new Uint8Array(sender.enc), responseNonce); - - const kdfS = suite.kdf; - const prkS = await kdfS.extract(saltS, new Uint8Array(secretS)); - const keyS = await kdfS.expand( - prkS, - te.encode("key"), - suite.aead.keySize, - ); - const nonceS = await kdfS.expand( - prkS, - te.encode("nonce"), - suite.aead.nonceSize, - ); - - const aeadKeyS = await suite.aead.createEncryptionContext(keyS); - const ct = await aeadKeyS.seal(nonceS, response, te.encode("")); - const encResponse = concat(responseNonce, new Uint8Array(ct)); - - // The recipient (OHTTP server) side: - const recipient = await suite.createRecipientContext({ - recipientKey: rkp.privateKey, - enc: sender.enc, - }); - - const secretR = await recipient.export( - te.encode("message/bhttp response"), - suite.aead.keySize, - ); - - const saltR = concat( - new Uint8Array(sender.enc), - encResponse.slice(0, suite.aead.keySize), - ); - const kdfR = suite.kdf; - const prkR = await kdfR.extract( - saltR, - new Uint8Array(secretR), - ); - const keyR = await kdfR.expand( - prkR, - te.encode("key"), - suite.aead.keySize, - ); - const nonceR = await kdfR.expand( - prkR, - te.encode("nonce"), - suite.aead.nonceSize, - ); - const aeadKeyR = await suite.aead.createEncryptionContext(keyR); - const pt = await aeadKeyR.open( - nonceR, - encResponse.slice(suite.aead.keySize), - te.encode(""), - ); - - // pt === "This is the response." - assertEquals(response, new Uint8Array(pt)); - }); - }); }); diff --git a/test/sample.test.ts b/test/sample.test.ts new file mode 100644 index 000000000..0e25f5308 --- /dev/null +++ b/test/sample.test.ts @@ -0,0 +1,1159 @@ +import { assertEquals, assertRejects, assertThrows } from "testing/asserts.ts"; +import { describe, it } from "testing/bdd.ts"; + +import { AeadId, KdfId, KemId } from "../src/identifiers.ts"; +import { CipherSuite } from "../src/cipherSuite.ts"; +import { isDeno } from "../src/utils/misc.ts"; +import { loadCrypto } from "../src/webCrypto.ts"; +import { concat } from "../src/utils/misc.ts"; +import { DhkemP256HkdfSha256 } from "../src/kems/dhkemP256.ts"; +import { DhkemP384HkdfSha384 } from "../src/kems/dhkemP384.ts"; +import { DhkemP521HkdfSha512 } from "../src/kems/dhkemP521.ts"; +import { DhkemX25519HkdfSha256 } from "../src/kems/dhkemX25519.ts"; +import { DhkemX448HkdfSha512 } from "../src/kems/dhkemX448.ts"; +import { HkdfSha256 } from "../src/kdfs/hkdfSha256.ts"; +import { HkdfSha384 } from "../src/kdfs/hkdfSha384.ts"; +import { HkdfSha512 } from "../src/kdfs/hkdfSha512.ts"; +import { Aes128Gcm } from "../src/aeads/aesGcm.ts"; + +import * as errors from "../src/errors.ts"; + +describe("README examples", () => { + describe("Base mode with DhkemP256HkdfSha256/HkdfSha256/Aes128Gcm", () => { + it("should work normally with ids", async () => { + // setup + const suite = new CipherSuite({ + kem: KemId.DhkemP256HkdfSha256, + kdf: KdfId.HkdfSha256, + aead: AeadId.Aes128Gcm, + }); + + const rkp = await suite.kem.generateKeyPair(); + + const sender = await suite.createSenderContext({ + recipientPublicKey: rkp.publicKey, + }); + + const recipient = await suite.createRecipientContext({ + recipientKey: rkp, + enc: sender.enc, + }); + + // encrypt + const ct = await sender.seal( + new TextEncoder().encode("my-secret-message"), + ); + + // decrypt + const pt = await recipient.open(ct); + + // assert + assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + await assertRejects(() => recipient.seal(pt), errors.NotSupportedError); + await assertRejects(() => sender.open(ct), errors.NotSupportedError); + }); + + it("should work normally with instances", async () => { + // setup + const suite = new CipherSuite({ + kem: new DhkemP256HkdfSha256(), + kdf: new HkdfSha256(), + aead: new Aes128Gcm(), + }); + + const rkp = await suite.kem.generateKeyPair(); + + const sender = await suite.createSenderContext({ + recipientPublicKey: rkp.publicKey, + }); + + const recipient = await suite.createRecipientContext({ + recipientKey: rkp, + enc: sender.enc, + }); + + // encrypt + const ct = await sender.seal( + new TextEncoder().encode("my-secret-message"), + ); + + // decrypt + const pt = await recipient.open(ct); + + // assert + assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + await assertRejects(() => recipient.seal(pt), errors.NotSupportedError); + await assertRejects(() => sender.open(ct), errors.NotSupportedError); + }); + + it("should work normally with importKey('jwk')", async () => { + // setup + const suite = new CipherSuite({ + kem: KemId.DhkemP256HkdfSha256, + kdf: KdfId.HkdfSha256, + aead: AeadId.Aes128Gcm, + }); + + const jwkPkR = { + kty: "EC", + crv: "P-256", + kid: "P-256-01", + x: "-eZXC6nV-xgthy8zZMCN8pcYSeE2XfWWqckA2fsxHPc", + y: "BGU5soLgsu_y7GN2I3EPUXS9EZ7Sw0qif-V70JtInFI", + key_ops: [], + }; + const pkR = await suite.kem.importKey("jwk", jwkPkR, true); + + const sender = await suite.createSenderContext({ + recipientPublicKey: pkR, + }); + + const jwkSkR = { + kty: "EC", + crv: "P-256", + kid: "P-256-01", + x: "-eZXC6nV-xgthy8zZMCN8pcYSeE2XfWWqckA2fsxHPc", + y: "BGU5soLgsu_y7GN2I3EPUXS9EZ7Sw0qif-V70JtInFI", + d: "kwibx3gas6Kz1V2fyQHKSnr-ybflddSjN0eOnbmLmyo", + key_ops: ["deriveBits"], + }; + const skR = await suite.kem.importKey("jwk", jwkSkR, false); + const recipient = await suite.createRecipientContext({ + recipientKey: skR, + enc: sender.enc, + }); + + // encrypt + const ct = await sender.seal( + new TextEncoder().encode("my-secret-message"), + ); + + // decrypt + const pt = await recipient.open(ct); + + // assert + assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + await assertRejects(() => recipient.seal(pt), errors.NotSupportedError); + await assertRejects(() => sender.open(ct), errors.NotSupportedError); + }); + }); + + describe("Base mode with DhkemP384HkdfSha384/HkdfSha384/Aes128Gcm.", () => { + it("should work normally with ids", async () => { + // setup + const suite = new CipherSuite({ + kem: KemId.DhkemP384HkdfSha384, + kdf: KdfId.HkdfSha384, + aead: AeadId.Aes128Gcm, + }); + + const rkp = await suite.kem.generateKeyPair(); + + const sender = await suite.createSenderContext({ + recipientPublicKey: rkp.publicKey, + }); + + const recipient = await suite.createRecipientContext({ + recipientKey: rkp, + enc: sender.enc, + }); + + // encrypt + const ct = await sender.seal( + new TextEncoder().encode("my-secret-message"), + ); + + // decrypt + const pt = await recipient.open(ct); + + // assert + assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + }); + + it("should work normally with instances", async () => { + // setup + const suite = new CipherSuite({ + kem: new DhkemP384HkdfSha384(), + kdf: new HkdfSha384(), + aead: AeadId.Aes128Gcm, + }); + + const rkp = await suite.kem.generateKeyPair(); + + const sender = await suite.createSenderContext({ + recipientPublicKey: rkp.publicKey, + }); + + const recipient = await suite.createRecipientContext({ + recipientKey: rkp, + enc: sender.enc, + }); + + // encrypt + const ct = await sender.seal( + new TextEncoder().encode("my-secret-message"), + ); + + // decrypt + const pt = await recipient.open(ct); + + // assert + assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + }); + + it("should work normally with importKey('jwk')", async () => { + // setup + const suite = new CipherSuite({ + kem: KemId.DhkemP384HkdfSha384, + kdf: KdfId.HkdfSha384, + aead: AeadId.Aes128Gcm, + }); + + const jwkPkR = { + kty: "EC", + crv: "P-384", + kid: "P-384-01", + x: "_XyN9woHaS0mPimSW-etwJMEDSzxIMjp4PjezavU8SHJoClz1bQrcmPb1ZJxHxhI", + y: "GCNfc32p9sRotx7u2oDGJ3Eqz6q5zPHLdizNn83oRsUTN31eCWfGLHWRury3xF50", + key_ops: [], + }; + const pkR = await suite.kem.importKey("jwk", jwkPkR, true); + + const sender = await suite.createSenderContext({ + recipientPublicKey: pkR, + }); + + const jwkSkR = { + kty: "EC", + crv: "P-384", + kid: "P-384-01", + x: "_XyN9woHaS0mPimSW-etwJMEDSzxIMjp4PjezavU8SHJoClz1bQrcmPb1ZJxHxhI", + y: "GCNfc32p9sRotx7u2oDGJ3Eqz6q5zPHLdizNn83oRsUTN31eCWfGLHWRury3xF50", + d: "1pImEKbrr771-RKi8Tb7tou_WjiR7kwui_nMu16449rk3lzAqf9buUhTkJ-pogkb", + key_ops: ["deriveBits"], + }; + const skR = await suite.kem.importKey("jwk", jwkSkR, false); + const recipient = await suite.createRecipientContext({ + recipientKey: skR, + enc: sender.enc, + }); + + // encrypt + const ct = await sender.seal( + new TextEncoder().encode("my-secret-message"), + ); + + // decrypt + const pt = await recipient.open(ct); + + // assert + assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + await assertRejects(() => recipient.seal(pt), errors.NotSupportedError); + await assertRejects(() => sender.open(ct), errors.NotSupportedError); + }); + }); + + describe("Base mode with DhkemP521HkdfSha512/HkdfSha512/Aes128Gcm", () => { + it("should work normally with ids", async () => { + if (isDeno()) { + return; + } + + // setup + const suite = new CipherSuite({ + kem: KemId.DhkemP521HkdfSha512, + kdf: KdfId.HkdfSha512, + aead: AeadId.Aes128Gcm, + }); + + const rkp = await suite.kem.generateKeyPair(); + + const sender = await suite.createSenderContext({ + recipientPublicKey: rkp.publicKey, + }); + + const recipient = await suite.createRecipientContext({ + recipientKey: rkp, + enc: sender.enc, + }); + + // encrypt + const ct = await sender.seal( + new TextEncoder().encode("my-secret-message"), + ); + + // decrypt + const pt = await recipient.open(ct); + + // assert + assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + }); + + it("should work normally with instances", async () => { + if (isDeno()) { + return; + } + + // setup + const suite = new CipherSuite({ + kem: new DhkemP521HkdfSha512(), + kdf: new HkdfSha512(), + aead: AeadId.Aes128Gcm, + }); + + const rkp = await suite.kem.generateKeyPair(); + + const sender = await suite.createSenderContext({ + recipientPublicKey: rkp.publicKey, + }); + + const recipient = await suite.createRecipientContext({ + recipientKey: rkp, + enc: sender.enc, + }); + + // encrypt + const ct = await sender.seal( + new TextEncoder().encode("my-secret-message"), + ); + + // decrypt + const pt = await recipient.open(ct); + + // assert + assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + }); + + it("should work normally with importKey('jwk')", async () => { + if (isDeno()) { + return; + } + + // setup + const suite = new CipherSuite({ + kem: KemId.DhkemP521HkdfSha512, + kdf: KdfId.HkdfSha512, + aead: AeadId.Aes128Gcm, + }); + + const jwkPkR = { + kty: "EC", + crv: "P-521", + kid: "P-521-01", + x: "APkZitSJMJUMB-iPCt47sWu_CrnUHg6IAR4qjmHON-2u41Rjg6DNOS0LZYJJt-AVH5NgGVi8ElIfjo71b9HXCTOc", + y: "ASx-Cb--149HJ-e1KlSaY-1BOhwOdcTkxSt8BGbW7_hnGfzHsoXM3ywwNcp1Yad-FHUKwmCyMelMQEn2Rh4V2l3I", + key_ops: [], + }; + const pkR = await suite.kem.importKey("jwk", jwkPkR, true); + + const sender = await suite.createSenderContext({ + recipientPublicKey: pkR, + }); + + const jwkSkR = { + kty: "EC", + crv: "P-521", + kid: "P-521-01", + x: "APkZitSJMJUMB-iPCt47sWu_CrnUHg6IAR4qjmHON-2u41Rjg6DNOS0LZYJJt-AVH5NgGVi8ElIfjo71b9HXCTOc", + y: "ASx-Cb--149HJ-e1KlSaY-1BOhwOdcTkxSt8BGbW7_hnGfzHsoXM3ywwNcp1Yad-FHUKwmCyMelMQEn2Rh4V2l3I", + d: "ADYyo73ZKicOjwGDYQ_ybZKnVzdAcxGm9OVAxQjzgVM4jaS-Iwtkz90oLdDz3shgKlDgtRK2Aa9lMhqR94hBo4IE", + key_ops: ["deriveBits"], + }; + const skR = await suite.kem.importKey("jwk", jwkSkR, false); + const recipient = await suite.createRecipientContext({ + recipientKey: skR, + enc: sender.enc, + }); + + // encrypt + const ct = await sender.seal( + new TextEncoder().encode("my-secret-message"), + ); + + // decrypt + const pt = await recipient.open(ct); + + // assert + assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + await assertRejects(() => recipient.seal(pt), errors.NotSupportedError); + await assertRejects(() => sender.open(ct), errors.NotSupportedError); + }); + }); + + describe("Base mode with DhkemX25519HkdfSha256/HkdfSha256/Aes128Gcm", () => { + it("should work normally with ids", async () => { + // setup + const suite = new CipherSuite({ + kem: KemId.DhkemX25519HkdfSha256, + kdf: KdfId.HkdfSha256, + aead: AeadId.Aes128Gcm, + }); + + const rkp = await suite.kem.generateKeyPair(); + + const sender = await suite.createSenderContext({ + recipientPublicKey: rkp.publicKey, + }); + + const recipient = await suite.createRecipientContext({ + recipientKey: rkp, + enc: sender.enc, + }); + + // encrypt + const ct = await sender.seal( + new TextEncoder().encode("my-secret-message"), + ); + + // decrypt + const pt = await recipient.open(ct); + + // assert + assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + }); + + it("should work normally with instances", async () => { + // setup + const suite = new CipherSuite({ + kem: new DhkemX25519HkdfSha256(), + kdf: new HkdfSha256(), + aead: AeadId.Aes128Gcm, + }); + + const rkp = await suite.kem.generateKeyPair(); + + const sender = await suite.createSenderContext({ + recipientPublicKey: rkp.publicKey, + }); + + const recipient = await suite.createRecipientContext({ + recipientKey: rkp, + enc: sender.enc, + }); + + // encrypt + const ct = await sender.seal( + new TextEncoder().encode("my-secret-message"), + ); + + // decrypt + const pt = await recipient.open(ct); + + // assert + assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + }); + + it("should work normally with importKey('jwk')", async () => { + // setup + const suite = new CipherSuite({ + kem: KemId.DhkemX25519HkdfSha256, + kdf: KdfId.HkdfSha256, + aead: AeadId.Aes128Gcm, + }); + + const jwkPkR = { + kty: "OKP", + crv: "X25519", + kid: "X25519-01", + x: "y3wJq3uXPHeoCO4FubvTc7VcBuqpvUrSvU6ZMbHDTCI", + key_ops: [], + }; + const pkR = await suite.kem.importKey("jwk", jwkPkR, true); + + const sender = await suite.createSenderContext({ + recipientPublicKey: pkR, + }); + + const jwkSkR = { + kty: "OKP", + crv: "X25519", + kid: "X25519-01", + x: "y3wJq3uXPHeoCO4FubvTc7VcBuqpvUrSvU6ZMbHDTCI", + d: "vsJ1oX5NNi0IGdwGldiac75r-Utmq3Jq4LGv48Q_Qc4", + key_ops: ["deriveBits"], + }; + const skR = await suite.kem.importKey("jwk", jwkSkR, false); + const recipient = await suite.createRecipientContext({ + recipientKey: skR, + enc: sender.enc, + }); + + // encrypt + const ct = await sender.seal( + new TextEncoder().encode("my-secret-message"), + ); + + // decrypt + const pt = await recipient.open(ct); + + // assert + assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + await assertRejects(() => recipient.seal(pt), errors.NotSupportedError); + await assertRejects(() => sender.open(ct), errors.NotSupportedError); + }); + }); + + describe("Base mode with DhkemX448HkdfSha256/HkdfSha512/Aes256Gcm)", () => { + it("should work normally with ids", async () => { + // setup + const suite = new CipherSuite({ + kem: KemId.DhkemX448HkdfSha512, + kdf: KdfId.HkdfSha512, + aead: AeadId.Aes256Gcm, + }); + + const rkp = await suite.kem.generateKeyPair(); + + const sender = await suite.createSenderContext({ + recipientPublicKey: rkp.publicKey, + }); + + const recipient = await suite.createRecipientContext({ + recipientKey: rkp, + enc: sender.enc, + }); + + // encrypt + const ct = await sender.seal( + new TextEncoder().encode("my-secret-message"), + ); + + // decrypt + const pt = await recipient.open(ct); + + // assert + assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + }); + + it("should work normally with instances", async () => { + // setup + const suite = new CipherSuite({ + kem: new DhkemX448HkdfSha512(), + kdf: new HkdfSha512(), + aead: AeadId.Aes256Gcm, + }); + + const rkp = await suite.kem.generateKeyPair(); + + const sender = await suite.createSenderContext({ + recipientPublicKey: rkp.publicKey, + }); + + const recipient = await suite.createRecipientContext({ + recipientKey: rkp, + enc: sender.enc, + }); + + // encrypt + const ct = await sender.seal( + new TextEncoder().encode("my-secret-message"), + ); + + // decrypt + const pt = await recipient.open(ct); + + // assert + assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + }); + + it("should work normally with importKey('jwk')", async () => { + // setup + const suite = new CipherSuite({ + kem: KemId.DhkemX448HkdfSha512, + kdf: KdfId.HkdfSha512, + aead: AeadId.Aes256Gcm, + }); + + const jwkPkR = { + kty: "OKP", + crv: "X448", + kid: "X448-01", + x: "IkLmc0klvEMXYneHMKAB6ePohryAwAPVe2pRSffIDY6NrjeYNWVX5J-fG4NV2OoU77C88A0mvxI", + key_ops: [], + }; + const pkR = await suite.kem.importKey("jwk", jwkPkR, true); + + const sender = await suite.createSenderContext({ + recipientPublicKey: pkR, + }); + + const jwkSkR = { + kty: "OKP", + crv: "X448", + kid: "X448-01", + x: "IkLmc0klvEMXYneHMKAB6ePohryAwAPVe2pRSffIDY6NrjeYNWVX5J-fG4NV2OoU77C88A0mvxI", + d: "rJJRG3nshyCtd9CgXld8aNaB9YXKR0UOi7zj7hApg9YH4XdBO0G8NcAFNz_uPH2GnCZVcSDgV5c", + key_ops: ["deriveBits"], + }; + const skR = await suite.kem.importKey("jwk", jwkSkR, false); + const recipient = await suite.createRecipientContext({ + recipientKey: skR, + enc: sender.enc, + }); + + // encrypt + const ct = await sender.seal( + new TextEncoder().encode("my-secret-message"), + ); + + // decrypt + const pt = await recipient.open(ct); + + // assert + assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + await assertRejects(() => recipient.seal(pt), errors.NotSupportedError); + await assertRejects(() => sender.open(ct), errors.NotSupportedError); + }); + }); + + describe("Base mode with DhkemP256HkdfSha256/HkdfSha256/ExportOnly", () => { + it("should work normally", async () => { + // setup + const suite = new CipherSuite({ + kem: KemId.DhkemP256HkdfSha256, + kdf: KdfId.HkdfSha256, + aead: AeadId.ExportOnly, + }); + + const rkp = await suite.kem.generateKeyPair(); + + const sender = await suite.createSenderContext({ + recipientPublicKey: rkp.publicKey, + }); + + const recipient = await suite.createRecipientContext({ + recipientKey: rkp, + enc: sender.enc, + }); + + const te = new TextEncoder(); + + // export + const pskS = sender.export(te.encode("jugemujugemu"), 32); + const pskR = recipient.export(te.encode("jugemujugemu"), 32); + assertEquals(pskR, pskS); + + // other functions are disabled. + await assertRejects( + () => sender.seal(te.encode("my-secret-message")), + errors.NotSupportedError, + ); + await assertRejects( + () => sender.open(te.encode("xxxxxxxxxxxxxxxxx")), + errors.NotSupportedError, + ); + }); + }); + + describe("Base mode with DhkemX25519HkdfSha256/HkdfSha256/ExportOnly", () => { + it("should work normally", async () => { + // setup + const suite = new CipherSuite({ + kem: KemId.DhkemX25519HkdfSha256, + kdf: KdfId.HkdfSha256, + aead: AeadId.ExportOnly, + }); + + const rkp = await suite.kem.generateKeyPair(); + + const sender = await suite.createSenderContext({ + recipientPublicKey: rkp.publicKey, + }); + + const recipient = await suite.createRecipientContext({ + recipientKey: rkp, + enc: sender.enc, + }); + + const te = new TextEncoder(); + + // export + const pskS = sender.export(te.encode("jugemujugemu"), 32); + const pskR = recipient.export(te.encode("jugemujugemu"), 32); + assertEquals(pskR, pskS); + + // other functions are disabled. + await assertRejects( + () => sender.seal(te.encode("my-secret-message")), + errors.NotSupportedError, + ); + await assertRejects( + () => sender.open(te.encode("xxxxxxxxxxxxxxxxx")), + errors.NotSupportedError, + ); + }); + }); + + describe("PSK mode", () => { + it("should work normally", async () => { + // setup + const suite = new CipherSuite({ + kem: KemId.DhkemP256HkdfSha256, + kdf: KdfId.HkdfSha256, + aead: AeadId.Aes128Gcm, + }); + + const rkp = await suite.kem.generateKeyPair(); + + const sender = await suite.createSenderContext({ + recipientPublicKey: rkp.publicKey, + psk: { + id: new TextEncoder().encode("our-pre-shared-key-id"), + key: new TextEncoder().encode("jugemujugemugokounosurikirekaija"), + }, + }); + + const recipient = await suite.createRecipientContext({ + recipientKey: rkp, + enc: sender.enc, + psk: { + id: new TextEncoder().encode("our-pre-shared-key-id"), + key: new TextEncoder().encode("jugemujugemugokounosurikirekaija"), + }, + }); + + // encrypt + const ct = await sender.seal( + new TextEncoder().encode("my-secret-message"), + ); + + // decrypt + const pt = await recipient.open(ct); + + // assert + assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + }); + }); + + describe("Auth mode", () => { + it("should work normally", async () => { + // setup + const suite = new CipherSuite({ + kem: KemId.DhkemP256HkdfSha256, + kdf: KdfId.HkdfSha256, + aead: AeadId.Aes128Gcm, + }); + + const rkp = await suite.kem.generateKeyPair(); + const skp = await suite.kem.generateKeyPair(); + + const sender = await suite.createSenderContext({ + recipientPublicKey: rkp.publicKey, + senderKey: skp, + }); + + const recipient = await suite.createRecipientContext({ + recipientKey: rkp, + enc: sender.enc, + senderPublicKey: skp.publicKey, + }); + + // encrypt + const ct = await sender.seal( + new TextEncoder().encode("my-secret-message"), + ); + + // decrypt + const pt = await recipient.open(ct); + + // assert + assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + }); + }); + + describe("AuthPSK mode with DhkemP256HkdfSha256", () => { + it("should work normally", async () => { + // setup + const suite = new CipherSuite({ + kem: KemId.DhkemP256HkdfSha256, + kdf: KdfId.HkdfSha256, + aead: AeadId.Aes128Gcm, + }); + + const rkp = await suite.kem.generateKeyPair(); + const skp = await suite.kem.generateKeyPair(); + + const sender = await suite.createSenderContext({ + recipientPublicKey: rkp.publicKey, + senderKey: skp, + psk: { + id: new TextEncoder().encode("our-pre-shared-key-id"), + key: new TextEncoder().encode("jugemujugemugokounosurikirekaija"), + }, + }); + + const recipient = await suite.createRecipientContext({ + recipientKey: rkp, + enc: sender.enc, + senderPublicKey: skp.publicKey, + psk: { + id: new TextEncoder().encode("our-pre-shared-key-id"), + key: new TextEncoder().encode("jugemujugemugokounosurikirekaija"), + }, + }); + + // encrypt + const ct = await sender.seal( + new TextEncoder().encode("my-secret-message"), + ); + + // decrypt + const pt = await recipient.open(ct); + + // assert + assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + }); + }); + + describe("AuthPSK mode with DhkemX25519HkdfSha256", () => { + it("should work normally", async () => { + // setup + const suite = new CipherSuite({ + kem: KemId.DhkemX25519HkdfSha256, + kdf: KdfId.HkdfSha256, + aead: AeadId.Aes128Gcm, + }); + + const rkp = await suite.kem.generateKeyPair(); + const skp = await suite.kem.generateKeyPair(); + + const sender = await suite.createSenderContext({ + recipientPublicKey: rkp.publicKey, + senderKey: skp, + psk: { + id: new TextEncoder().encode("our-pre-shared-key-id"), + key: new TextEncoder().encode("jugemujugemugokounosurikirekaija"), + }, + }); + + const recipient = await suite.createRecipientContext({ + recipientKey: rkp, + enc: sender.enc, + senderPublicKey: skp.publicKey, + psk: { + id: new TextEncoder().encode("our-pre-shared-key-id"), + key: new TextEncoder().encode("jugemujugemugokounosurikirekaija"), + }, + }); + + // encrypt + const ct = await sender.seal( + new TextEncoder().encode("my-secret-message"), + ); + + // decrypt + const pt = await recipient.open(ct); + + // assert + assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + }); + }); + + describe("AuthPSK mode with DhkemX448HkdfSha512", () => { + it("should work normally", async () => { + // setup + const suite = new CipherSuite({ + kem: KemId.DhkemX448HkdfSha512, + kdf: KdfId.HkdfSha256, + aead: AeadId.Aes128Gcm, + }); + + const rkp = await suite.kem.generateKeyPair(); + const skp = await suite.kem.generateKeyPair(); + + const sender = await suite.createSenderContext({ + recipientPublicKey: rkp.publicKey, + senderKey: skp, + psk: { + id: new TextEncoder().encode("our-pre-shared-key-id"), + key: new TextEncoder().encode("jugemujugemugokounosurikirekaija"), + }, + }); + + const recipient = await suite.createRecipientContext({ + recipientKey: rkp, + enc: sender.enc, + senderPublicKey: skp.publicKey, + psk: { + id: new TextEncoder().encode("our-pre-shared-key-id"), + key: new TextEncoder().encode("jugemujugemugokounosurikirekaija"), + }, + }); + + // encrypt + const ct = await sender.seal( + new TextEncoder().encode("my-secret-message"), + ); + + // decrypt + const pt = await recipient.open(ct); + + // assert + assertEquals(new TextDecoder().decode(pt), "my-secret-message"); + }); + }); + + describe("Oblivious HTTP with DhkemP256HkdfSha256/HkdfSha256/Aes128Gcm", () => { + it("should work normally", async () => { + const te = new TextEncoder(); + const cryptoApi = await loadCrypto(); + + const suite = new CipherSuite({ + kem: KemId.DhkemP256HkdfSha256, + kdf: KdfId.HkdfSha256, + aead: AeadId.Aes128Gcm, + }); + const rkp = await suite.kem.generateKeyPair(); + + // The sender (OHTTP client) side: + const response = te.encode("This is the response."); + const sender = await suite.createSenderContext({ + recipientPublicKey: rkp.publicKey, + }); + + const secretS = await sender.export( + te.encode("message/bhttp response"), + suite.aead.keySize, + ); + + const responseNonce = new Uint8Array(suite.aead.keySize); + cryptoApi.getRandomValues(responseNonce); + const saltS = concat(new Uint8Array(sender.enc), responseNonce); + + const kdfS = suite.kdf; + const prkS = await kdfS.extract(saltS, new Uint8Array(secretS)); + const keyS = await kdfS.expand( + prkS, + te.encode("key"), + suite.aead.keySize, + ); + const nonceS = await kdfS.expand( + prkS, + te.encode("nonce"), + suite.aead.nonceSize, + ); + + const aeadKeyS = await suite.aead.createEncryptionContext(keyS); + const ct = await aeadKeyS.seal(nonceS, response, te.encode("")); + const encResponse = concat(responseNonce, new Uint8Array(ct)); + + // The recipient (OHTTP server) side: + const recipient = await suite.createRecipientContext({ + recipientKey: rkp.privateKey, + enc: sender.enc, + }); + + const secretR = await recipient.export( + te.encode("message/bhttp response"), + suite.aead.keySize, + ); + + const saltR = concat( + new Uint8Array(sender.enc), + encResponse.slice(0, suite.aead.keySize), + ); + const kdfR = suite.kdf; + const prkR = await kdfR.extract( + saltR, + new Uint8Array(secretR), + ); + const keyR = await kdfR.expand( + prkR, + te.encode("key"), + suite.aead.keySize, + ); + const nonceR = await kdfR.expand( + prkR, + te.encode("nonce"), + suite.aead.nonceSize, + ); + const aeadKeyR = await suite.aead.createEncryptionContext(keyR); + const pt = await aeadKeyR.open( + nonceR, + encResponse.slice(suite.aead.keySize), + te.encode(""), + ); + + // pt === "This is the response." + assertEquals(response, new Uint8Array(pt)); + }); + }); + + describe("Oblivious HTTP with DhkemP384HkdfSha384/HkdfSha384/Aes256Gcm", () => { + it("should work normally", async () => { + if (isDeno()) { + return; + } + const te = new TextEncoder(); + const cryptoApi = await loadCrypto(); + + const suite = new CipherSuite({ + kem: KemId.DhkemP384HkdfSha384, + kdf: KdfId.HkdfSha384, + aead: AeadId.Aes256Gcm, + }); + const rkp = await suite.kem.generateKeyPair(); + + // The sender (OHTTP client) side: + const response = te.encode("This is the response."); + const sender = await suite.createSenderContext({ + recipientPublicKey: rkp.publicKey, + }); + + const secretS = await sender.export( + te.encode("message/bhttp response"), + suite.aead.keySize, + ); + + const responseNonce = new Uint8Array(suite.aead.keySize); + cryptoApi.getRandomValues(responseNonce); + const saltS = concat(new Uint8Array(sender.enc), responseNonce); + + const kdfS = suite.kdf; + const prkS = await kdfS.extract(saltS, new Uint8Array(secretS)); + const keyS = await kdfS.expand( + prkS, + te.encode("key"), + suite.aead.keySize, + ); + const nonceS = await kdfS.expand( + prkS, + te.encode("nonce"), + suite.aead.nonceSize, + ); + + const aeadKeyS = await suite.aead.createEncryptionContext(keyS); + const ct = await aeadKeyS.seal(nonceS, response, te.encode("")); + const encResponse = concat(responseNonce, new Uint8Array(ct)); + + // The recipient (OHTTP server) side: + const recipient = await suite.createRecipientContext({ + recipientKey: rkp.privateKey, + enc: sender.enc, + }); + + const secretR = await recipient.export( + te.encode("message/bhttp response"), + suite.aead.keySize, + ); + + const saltR = concat( + new Uint8Array(sender.enc), + encResponse.slice(0, suite.aead.keySize), + ); + const kdfR = suite.kdf; + const prkR = await kdfR.extract( + saltR, + new Uint8Array(secretR), + ); + const keyR = await kdfR.expand( + prkR, + te.encode("key"), + suite.aead.keySize, + ); + const nonceR = await kdfR.expand( + prkR, + te.encode("nonce"), + suite.aead.nonceSize, + ); + const aeadKeyR = await suite.aead.createEncryptionContext(keyR); + const pt = await aeadKeyR.open( + nonceR, + encResponse.slice(suite.aead.keySize), + te.encode(""), + ); + + // pt === "This is the response." + assertEquals(response, new Uint8Array(pt)); + }); + }); + + describe("Oblivious HTTP with DhkemP521HkdfSha512/HkdfSha512/Aes256Gcm", () => { + it("should work normally", async () => { + if (isDeno()) { + return; + } + const te = new TextEncoder(); + const cryptoApi = await loadCrypto(); + + const suite = new CipherSuite({ + kem: KemId.DhkemP521HkdfSha512, + kdf: KdfId.HkdfSha512, + aead: AeadId.Aes256Gcm, + }); + const rkp = await suite.kem.generateKeyPair(); + + // The sender (OHTTP client) side: + const response = te.encode("This is the response."); + const sender = await suite.createSenderContext({ + recipientPublicKey: rkp.publicKey, + }); + + const secretS = await sender.export( + te.encode("message/bhttp response"), + suite.aead.keySize, + ); + + const responseNonce = new Uint8Array(suite.aead.keySize); + cryptoApi.getRandomValues(responseNonce); + const saltS = concat(new Uint8Array(sender.enc), responseNonce); + + const kdfS = suite.kdf; + const prkS = await kdfS.extract(saltS, new Uint8Array(secretS)); + const keyS = await kdfS.expand( + prkS, + te.encode("key"), + suite.aead.keySize, + ); + const nonceS = await kdfS.expand( + prkS, + te.encode("nonce"), + suite.aead.nonceSize, + ); + + const aeadKeyS = await suite.aead.createEncryptionContext(keyS); + const ct = await aeadKeyS.seal(nonceS, response, te.encode("")); + const encResponse = concat(responseNonce, new Uint8Array(ct)); + + // The recipient (OHTTP server) side: + const recipient = await suite.createRecipientContext({ + recipientKey: rkp.privateKey, + enc: sender.enc, + }); + + const secretR = await recipient.export( + te.encode("message/bhttp response"), + suite.aead.keySize, + ); + + const saltR = concat( + new Uint8Array(sender.enc), + encResponse.slice(0, suite.aead.keySize), + ); + const kdfR = suite.kdf; + const prkR = await kdfR.extract( + saltR, + new Uint8Array(secretR), + ); + const keyR = await kdfR.expand( + prkR, + te.encode("key"), + suite.aead.keySize, + ); + const nonceR = await kdfR.expand( + prkR, + te.encode("nonce"), + suite.aead.nonceSize, + ); + const aeadKeyR = await suite.aead.createEncryptionContext(keyR); + const pt = await aeadKeyR.open( + nonceR, + encResponse.slice(suite.aead.keySize), + te.encode(""), + ); + + // pt === "This is the response." + assertEquals(response, new Uint8Array(pt)); + }); + }); +});