diff --git a/packages/crypto/src/decrypt.test.ts b/packages/crypto/src/decrypt.test.ts index c5fae9b..9c777e9 100644 --- a/packages/crypto/src/decrypt.test.ts +++ b/packages/crypto/src/decrypt.test.ts @@ -2,6 +2,7 @@ import { expect } from "@std/expect"; import { generateKeys } from "./keys.ts"; import { encryptText } from "./encrypt.ts"; import { decryptText } from "./decrypt.ts"; +import { sha3_512 } from "@noble/hashes/sha3.js"; Deno.test({ name: "decrypts an encrypted value", @@ -14,3 +15,29 @@ Deno.test({ expect(decrypted).toEqual(text); }, }); + +Deno.test({ + name: "errors when provided an incorrect hash", + fn() { + const keys = generateKeys(); + const text = "Hello, world!"; + const encrypted = encryptText(keys.publicKey, text); + + encrypted.hash = sha3_512(new Uint8Array(24)).toBase64(); + + expect(() => decryptText(keys.secretKey, encrypted)).toThrow(); + }, +}); + +Deno.test({ + name: "errors when provided an incorrect content length", + fn() { + const keys = generateKeys(); + const text = "Hello, world!"; + const encrypted = encryptText(keys.publicKey, text); + + encrypted.length = Math.round(Math.random() * 1000); + + expect(() => decryptText(keys.secretKey, encrypted)).toThrow(); + }, +}); diff --git a/packages/crypto/src/decrypt.ts b/packages/crypto/src/decrypt.ts index 86a40c9..1bffbb6 100644 --- a/packages/crypto/src/decrypt.ts +++ b/packages/crypto/src/decrypt.ts @@ -1,5 +1,6 @@ import { XWing } from "@noble/post-quantum/hybrid.js"; import { xchacha20poly1305 } from "@noble/ciphers/chacha.js"; +import { sha3_512 } from "@noble/hashes/sha3.js"; import type { EncryptedPayload } from "./types.ts"; export function decryptText( @@ -12,6 +13,17 @@ export function decryptText( const sharedSecret = XWing.decapsulate(cipherText, secretKey); const cipher = xchacha20poly1305(sharedSecret, nonce); const decrypted = cipher.decrypt(content); + const hash = sha3_512(decrypted); + + if (decrypted.byteLength !== payload.length) { + throw new Error( + `content lengths do not match: got ${decrypted.byteLength}, expected ${payload.length}`, + ); + } else if (hash.toBase64() !== payload.hash) { + throw new Error( + `hashes do not match: got ${hash.toBase64()}, expected ${payload.hash}`, + ); + } return new TextDecoder().decode(decrypted); } diff --git a/packages/crypto/src/encrypt.test.ts b/packages/crypto/src/encrypt.test.ts index 8c58e2b..6298e05 100644 --- a/packages/crypto/src/encrypt.test.ts +++ b/packages/crypto/src/encrypt.test.ts @@ -3,17 +3,18 @@ import { generateKeys } from "./keys.ts"; import { encryptText } from "./encrypt.ts"; Deno.test({ - name: "generates non-empty encrypted payload", + name: "generates an encrypted payload", fn() { const keys = generateKeys(); const text = "Hello, world!"; const result = encryptText(keys.publicKey, text); - const entries = Array.from(Object.values(result)); - expect(entries).toHaveLength(4); + expect(Object.entries(result)).toHaveLength(5); - for (const [key, val] of entries) { - expect(val.length, `${key} is empty`).toBeGreaterThan(0); - } + expect(result.cipherText.length).toBeGreaterThan(0); + expect(result.content.length).toBeGreaterThan(0); + expect(result.hash.length).toBeGreaterThan(0); + expect(result.nonce.length).toBeGreaterThan(0); + expect(result.length).toBeGreaterThan(0); }, }); diff --git a/packages/crypto/src/encrypt.ts b/packages/crypto/src/encrypt.ts index e92e330..911a16b 100644 --- a/packages/crypto/src/encrypt.ts +++ b/packages/crypto/src/encrypt.ts @@ -1,7 +1,7 @@ import { XWing } from "@noble/post-quantum/hybrid.js"; -import { sha256 } from "@noble/hashes/sha2.js"; import { xchacha20poly1305 } from "@noble/ciphers/chacha.js"; import { randomBytes } from "@noble/hashes/utils.js"; +import { sha3_512 } from "@noble/hashes/sha3.js"; import type { EncryptedPayload } from "./types.ts"; export function encryptText( @@ -13,12 +13,13 @@ export function encryptText( const contentBytes = new TextEncoder().encode(text); const cipher = xchacha20poly1305(sharedSecret, nonce); const content = cipher.encrypt(contentBytes); - const hash = sha256(content); + const hash = sha3_512(contentBytes); return { cipherText: cipherText.toBase64(), content: content.toBase64(), nonce: nonce.toBase64(), hash: hash.toBase64(), + length: contentBytes.byteLength, }; } diff --git a/packages/crypto/src/types.ts b/packages/crypto/src/types.ts index 25eb83e..5515d9f 100644 --- a/packages/crypto/src/types.ts +++ b/packages/crypto/src/types.ts @@ -3,4 +3,5 @@ export interface EncryptedPayload { content: string; nonce: string; hash: string; + length: number; }