refactor: cipher specific endianness for nonce

This commit is contained in:
Richard Ramos 2023-11-21 13:19:09 -04:00 committed by richΛrd
parent 32e64030ed
commit 0f9b146776
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 { 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);
}
}

View File

@ -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;
}
/**

View File

@ -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

View File

@ -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");
}

View File

@ -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;
}