refactor: cipher specific endianness for nonce

This commit is contained in:
Richard Ramos 2023-11-21 13:19:09 -04:00
parent 938789d7cd
commit 550e26533f
No known key found for this signature in database
GPG Key ID: 1CE87DB518195760
5 changed files with 14 additions and 10 deletions

View File

@ -2,15 +2,18 @@ import { ChaCha20Poly1305 } from "@stablelib/chacha20poly1305";
import { bytes32 } from "./@types/basic.js"; import { bytes32 } from "./@types/basic.js";
import { Cipher } from "./crypto.js"; import { Cipher } from "./crypto.js";
import { Nonce } from "./nonce.js";
export class ChaChaPoly implements Cipher { 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 ctx = new ChaCha20Poly1305(k);
const n = nonce.getBytes(true);
return ctx.seal(n, plaintext, ad); 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 ctx = new ChaCha20Poly1305(k);
const n = nonce.getBytes(true);
return ctx.open(n, ciphertext, ad); return ctx.open(n, ciphertext, ad);
} }
} }

View File

@ -5,6 +5,7 @@ import { concat as uint8ArrayConcat } from "uint8arrays/concat";
import type { bytes32 } from "./@types/basic.js"; import type { bytes32 } from "./@types/basic.js";
import type { KeyPair } from "./@types/keypair.js"; import type { KeyPair } from "./@types/keypair.js";
import { Nonce } from "./nonce.js";
/** /**
* HKDF key derivation function * HKDF key derivation function
@ -63,7 +64,7 @@ export interface Cipher {
* @param plaintext data to encrypt * @param plaintext data to encrypt
* @returns sealed ciphertext including authentication tag * @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 * Authenticate and decrypt data
@ -73,7 +74,7 @@ export interface Cipher {
* @param ciphertext data to decrypt * @param ciphertext data to decrypt
* @returns plaintext if decryption was successful, `null` otherwise * @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;
} }
/** /**

View File

@ -134,7 +134,7 @@ describe("js-noise", () => {
plaintext = randomBytes(128, rng); 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) // 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 // 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 // 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

View File

@ -116,7 +116,7 @@ export class CipherState {
if (this.hasKey()) { if (this.hasKey()) {
// If an encryption key is set in the Cipher state, we proceed with encryption // 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.increment();
this.n.assertValue(); this.n.assertValue();
@ -140,7 +140,7 @@ export class CipherState {
this.n.assertValue(); this.n.assertValue();
if (this.hasKey()) { 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) { if (!plaintext) {
throw new Error("decryptWithAd failed"); throw new Error("decryptWithAd failed");
} }

View File

@ -31,11 +31,11 @@ export class Nonce {
increment(): void { increment(): void {
this.n++; 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; return this.bytes;
} }