From 0f9b146776e3bc96ab8018ebce8b0c46111c220f Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Tue, 21 Nov 2023 13:19:09 -0400 Subject: [PATCH] refactor: cipher specific endianness for nonce --- src/chachapoly.ts | 7 +++++-- src/crypto.ts | 5 +++-- src/index.spec.ts | 2 +- src/noise.ts | 4 ++-- src/nonce.ts | 6 +++--- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/chachapoly.ts b/src/chachapoly.ts index 9294c0d..1d7e155 100644 --- a/src/chachapoly.ts +++ b/src/chachapoly.ts @@ -2,15 +2,18 @@ import { ChaCha20Poly1305 } from "@stablelib/chacha20poly1305"; import { bytes32 } from "./@types/basic.js"; import { Cipher } from "./crypto.js"; +import { Nonce } from "./nonce.js"; export class ChaChaPoly implements Cipher { - encrypt(k: bytes32, n: Uint8Array, ad: Uint8Array, plaintext: Uint8Array): Uint8Array { + encrypt(k: bytes32, nonce: Nonce, ad: Uint8Array, plaintext: Uint8Array): Uint8Array { const ctx = new ChaCha20Poly1305(k); + const n = nonce.getBytes(true); return ctx.seal(n, plaintext, ad); } - decrypt(k: bytes32, n: Uint8Array, ad: Uint8Array, ciphertext: Uint8Array): Uint8Array | null { + decrypt(k: bytes32, nonce: Nonce, ad: Uint8Array, ciphertext: Uint8Array): Uint8Array | null { const ctx = new ChaCha20Poly1305(k); + const n = nonce.getBytes(true); return ctx.open(n, ciphertext, ad); } } diff --git a/src/crypto.ts b/src/crypto.ts index 5c2eff5..7ef584e 100644 --- a/src/crypto.ts +++ b/src/crypto.ts @@ -5,6 +5,7 @@ import { concat as uint8ArrayConcat } from "uint8arrays/concat"; import type { bytes32 } from "./@types/basic.js"; import type { KeyPair } from "./@types/keypair.js"; +import { Nonce } from "./nonce.js"; /** * HKDF key derivation function @@ -63,7 +64,7 @@ export interface Cipher { * @param plaintext data to encrypt * @returns sealed ciphertext including authentication tag */ - encrypt(k: bytes32, n: Uint8Array, ad: Uint8Array, plaintext: Uint8Array): Uint8Array; + encrypt(k: bytes32, n: Nonce, ad: Uint8Array, plaintext: Uint8Array): Uint8Array; /** * Authenticate and decrypt data @@ -73,7 +74,7 @@ export interface Cipher { * @param ciphertext data to decrypt * @returns plaintext if decryption was successful, `null` otherwise */ - decrypt(k: bytes32, n: Uint8Array, ad: Uint8Array, ciphertext: Uint8Array): Uint8Array | null; + decrypt(k: bytes32, n: Nonce, ad: Uint8Array, ciphertext: Uint8Array): Uint8Array | null; } /** diff --git a/src/index.spec.ts b/src/index.spec.ts index 1864e24..0e28115 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -134,7 +134,7 @@ describe("js-noise", () => { plaintext = randomBytes(128, rng); // We perform encryption using the Cipher State key, NonceMax and ad (not using the cypher state directly so it does not trigger the max nonce error) - ciphertext = cipherState.cipher.encrypt(cipherState.getKey(), cipherState.getNonce().getBytes(), ad, plaintext); + ciphertext = cipherState.cipher.encrypt(cipherState.getKey(), cipherState.getNonce(), ad, plaintext); // At this point ciphertext is a proper encryption of the original plaintext obtained with nonce equal to NonceMax // We can now test if decryption fails with a NoiseNonceMaxError error. Any subsequent decryption call over the Cipher State should fail similarly and leave the nonce unchanged diff --git a/src/noise.ts b/src/noise.ts index c58b35a..1faad65 100644 --- a/src/noise.ts +++ b/src/noise.ts @@ -116,7 +116,7 @@ export class CipherState { if (this.hasKey()) { // If an encryption key is set in the Cipher state, we proceed with encryption - ciphertext = this.cipher.encrypt(this.k, this.n.getBytes(), ad, plaintext); + ciphertext = this.cipher.encrypt(this.k, this.n, ad, plaintext); this.n.increment(); this.n.assertValue(); @@ -140,7 +140,7 @@ export class CipherState { this.n.assertValue(); if (this.hasKey()) { - const plaintext = this.cipher.decrypt(this.k, this.n.getBytes(), ad, ciphertext); + const plaintext = this.cipher.decrypt(this.k, this.n, ad, ciphertext); if (!plaintext) { throw new Error("decryptWithAd failed"); } diff --git a/src/nonce.ts b/src/nonce.ts index c7afac6..bf5cc77 100644 --- a/src/nonce.ts +++ b/src/nonce.ts @@ -31,11 +31,11 @@ export class Nonce { increment(): void { this.n++; - // Even though we're treating the nonce as 8 bytes, RFC7539 specifies 12 bytes for a nonce. - this.view.setUint32(4, this.n, true); } - getBytes(): Uint8Array { + getBytes(littleEndian: boolean): Uint8Array { + // Even though we're treating the nonce as 8 bytes, RFC7539 specifies 12 bytes for a nonce. + this.view.setUint32(4, this.n, littleEndian); return this.bytes; }