Skip to content

Commit

Permalink
Refactor salsa and chacha more
Browse files Browse the repository at this point in the history
  • Loading branch information
paulmillr committed Oct 19, 2023
1 parent a580207 commit b1f55c7
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 48 deletions.
19 changes: 17 additions & 2 deletions benchmark/aead.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { createCipheriv, createDecipheriv } from 'node:crypto';
import { concatBytes } from '@noble/ciphers/utils';
import { xchacha20poly1305, chacha20poly1305 } from '@noble/ciphers/chacha';
import { xsalsa20poly1305 } from '@noble/ciphers/salsa';
import { gcm, siv } from '@noble/ciphers/aes';
import * as micro from '@noble/ciphers/_micro';

import { ChaCha20Poly1305 as StableChachaPoly } from '@stablelib/chacha20poly1305';
Expand Down Expand Up @@ -31,7 +32,7 @@ const buffers = [
let chainsafe_chacha_poly;

export const ciphers = {
xsalsa20_poly1305: {
xsalsa20poly1305: {
opts: { key: buf(32), nonce: buf(24) },
tweetnacl: {
encrypt: (buf, opts) => tweetnacl.secretbox(buf, opts.nonce, opts.key),
Expand All @@ -46,7 +47,7 @@ export const ciphers = {
decrypt: (buf, opts) => micro.xsalsa20poly1305(opts.key, opts.nonce).decrypt(buf),
},
},
chacha20_poly1305: {
chacha20poly1305: {
opts: { key: buf(32), nonce: buf(12) },
node: {
encrypt: (buf, opts) => {
Expand Down Expand Up @@ -103,6 +104,20 @@ export const ciphers = {
decrypt: (buf, opts) => micro.xchacha20poly1305(opts.key, opts.nonce).decrypt(buf),
},
},
'aes-256-gcm': {
opts: { key: buf(32), nonce: buf(12) },
noble: {
encrypt: (buf, opts) => gcm(opts.key, opts.nonce).encrypt(buf),
decrypt: (buf, opts) => gcm(opts.key, opts.nonce).decrypt(buf),
},
},
'aes-256-gcm-siv': {
opts: { key: buf(32), nonce: buf(12) },
noble: {
encrypt: (buf, opts) => siv(opts.key, opts.nonce).encrypt(buf),
decrypt: (buf, opts) => siv(opts.key, opts.nonce).decrypt(buf),
},
}
};

export async function main() {
Expand Down
41 changes: 18 additions & 23 deletions src/_arx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ export type CipherCoreFn = (

export type ExtendNonceFn = (
sigma: Uint32Array,
key: Uint8Array,
input: Uint8Array,
output: Uint8Array
key: Uint32Array,
input: Uint32Array,
output: Uint32Array
) => void;

export type CipherOpts = {
Expand Down Expand Up @@ -87,15 +87,13 @@ const U32_EMPTY = new Uint32Array();
function runCipher(
core: CipherCoreFn,
sigma: Uint32Array,
key: Uint8Array,
nonce: Uint8Array,
key: Uint32Array,
nonce: Uint32Array,
data: Uint8Array,
output: Uint8Array,
counter: number,
rounds: number
): void {
const key32 = u32(key);
const nonce32 = u32(nonce);
const len = data.length;
const block = new Uint8Array(BLOCK_LEN);
const b32 = u32(block);
Expand All @@ -104,7 +102,7 @@ function runCipher(
const d32 = isAligned ? u32(data) : U32_EMPTY;
const o32 = isAligned ? u32(output) : U32_EMPTY;
for (let pos = 0; pos < len; counter++) {
core(sigma, key32, nonce32, b32, counter, rounds);
core(sigma, key, nonce, b32, counter, rounds);
if (counter >= MAX_COUNTER) throw new Error('arx: counter overflow');
const take = Math.min(BLOCK_LEN, len - pos);
// aligned to 4 bytes
Expand Down Expand Up @@ -158,23 +156,21 @@ export function createCipher(core: CipherCoreFn, opts: CipherOpts): XorStream {
// Key & sigma
// key=16 -> sigma16, k=key|key
// key=32 -> sigma32, k=key
let k: Uint8Array, sigma: Uint32Array;
if (key.length === 32) {
if (isAligned32(key)) k = key;
else {
// Align key to 4 bytes
k = key.slice();
toClean.push(k);
}
let l = key.length,
k: Uint8Array,
sigma: Uint32Array;
if (l === 32) {
k = key.slice();
toClean.push(k);
sigma = sigma32_32;
} else if (key.length === 16 && allowShortKeys) {
} else if (l === 16 && allowShortKeys) {
k = new Uint8Array(32);
k.set(key);
k.set(key, 16);
sigma = sigma16_32;
toClean.push(k);
} else {
throw new Error(`arx: invalid 32-byte key, got length=${key.length}`);
throw new Error(`arx: invalid 32-byte key, got length=${l}`);
}

// Nonce
Expand All @@ -189,13 +185,11 @@ export function createCipher(core: CipherCoreFn, opts: CipherOpts): XorStream {
toClean.push(nonce);
}

const k32 = u32(k);
// hsalsa & hchacha: handle extended nonce
if (extendNonceFn) {
if (nonce.length !== 24) throw new Error(`arx: extended nonce must be 24 bytes`);
let _k = new Uint8Array(32);
extendNonceFn(sigma, k, nonce.subarray(0, 16), _k);
// toClean.push(k);
k = _k;
extendNonceFn(sigma, k32, u32(nonce.subarray(0, 16)), k32);
nonce = nonce.subarray(16);
}

Expand All @@ -211,7 +205,8 @@ export function createCipher(core: CipherCoreFn, opts: CipherOpts): XorStream {
nonce = nc;
toClean.push(nonce);
}
runCipher(core, sigma, k, nonce, data, output, counter, rounds);
const n32 = u32(nonce);
runCipher(core, sigma, k32, n32, data, output, counter, rounds);
while (toClean.length > 0) toClean.pop()!.fill(0);
return output;
};
Expand Down
12 changes: 3 additions & 9 deletions src/_micro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

// prettier-ignore
import {
Cipher, XorStream, createView, setBigUint64, wrapCipher, u32,
Cipher, XorStream, createView, setBigUint64, wrapCipher,
bytesToHex, concatBytes, ensureBytes, equalBytes, hexToNumber, numberToBytesBE,
} from './utils.js';
import { createCipher, rotl } from './_arx.js';
Expand Down Expand Up @@ -80,10 +80,7 @@ function salsaCore(
}

// prettier-ignore
export function hsalsa(s: Uint32Array, key: Uint8Array, input: Uint8Array, output: Uint8Array) {
const k = u32(key);
const i = u32(input);
const o32 = u32(output);
export function hsalsa(s: Uint32Array, k: Uint32Array, i: Uint32Array, o32: Uint32Array) {
const x = new Uint32Array([
s[0], k[0], k[1], k[2],
k[3], s[1], i[0], i[1],
Expand Down Expand Up @@ -119,10 +116,7 @@ function chachaCore(
}

// prettier-ignore
export function hchacha(s: Uint32Array, key: Uint8Array, input: Uint8Array, output: Uint8Array) {
const k = u32(key);
const i = u32(input);
const o32 = u32(output);
export function hchacha(s: Uint32Array, k: Uint32Array, i: Uint32Array, o32: Uint32Array) {
const x = new Uint32Array([
s[0], s[1], s[2], s[3],
k[0], k[1], k[2], k[3],
Expand Down
6 changes: 1 addition & 5 deletions src/chacha.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
ensureBytes,
equalBytes,
setBigUint64,
u32,
} from './utils.js';
import { poly1305 } from './_poly1305.js';
import { createCipher, rotl } from './_arx.js';
Expand Down Expand Up @@ -91,11 +90,8 @@ function chachaCore(
*/
// prettier-ignore
export function hchacha(
s: Uint32Array, key: Uint8Array, input: Uint8Array, out: Uint8Array
s: Uint32Array, k: Uint32Array, i: Uint32Array, o32: Uint32Array
) {
const k = u32(key);
const i = u32(input);
const o32 = u32(out);
let x00 = s[0], x01 = s[1], x02 = s[2], x03 = s[3],
x04 = k[0], x05 = k[1], x06 = k[2], x07 = k[3],
x08 = k[4], x09 = k[5], x10 = k[6], x11 = k[7],
Expand Down
7 changes: 2 additions & 5 deletions src/salsa.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { wrapCipher, Cipher, ensureBytes, equalBytes, u32 } from './utils.js';
import { wrapCipher, Cipher, ensureBytes, equalBytes } from './utils.js';
import { poly1305 } from './_poly1305.js';
import { createCipher, rotl } from './_arx.js';

Expand Down Expand Up @@ -62,11 +62,8 @@ function salsaCore(
*/
// prettier-ignore
export function hsalsa(
s: Uint32Array, key: Uint8Array, input: Uint8Array, out: Uint8Array
s: Uint32Array, k: Uint32Array, i: Uint32Array, o32: Uint32Array
) {
const k = u32(key);
const i = u32(input);
const o32 = u32(out);
let x00 = s[0], x01 = k[0], x02 = k[1], x03 = k[2],
x04 = k[3], x05 = s[1], x06 = i[0], x07 = i[1],
x08 = i[2], x09 = i[3], x10 = s[2], x11 = k[4],
Expand Down
10 changes: 6 additions & 4 deletions test/arx.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const sigma16 = utils.utf8ToBytes('expand 16-byte k');
const sigma32 = utils.utf8ToBytes('expand 32-byte k');
const sigma16_32 = utils.u32(sigma16);
const sigma32_32 = utils.u32(sigma32);
const { u32 } = utils;

const getKey = (key) => {
if (key.length === 32) return { key, sigma: sigma32_32 };
Expand Down Expand Up @@ -60,12 +61,13 @@ describe('Salsa20', () => {
const src = hex.decode('fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0');
const good = 'c6cb53882782b5b86df1ab2ed9b810ec8a88c0a7f29211e693f0019fe0728858';
const dst = new Uint8Array(32);

const { key, sigma } = getKey(
hex.decode('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f')
);
hsalsa(sigma, key, src, dst);
hsalsa(sigma, u32(key), u32(src), u32(dst));
deepStrictEqual(hex.encode(dst), good);
slow.hsalsa(sigma, key, src, dst);
slow.hsalsa(sigma, u32(key), u32(src), u32(dst));
deepStrictEqual(hex.encode(dst), good);
});
should('xsalsa20', () => {
Expand Down Expand Up @@ -112,10 +114,10 @@ describe('chacha', () => {
const nonce = hex.decode('000000090000004a0000000031415927');
const good = '82413b4227b27bfed30e42508a877d73a0f9e4d58a74a853c12ec41326d3ecdc';
const subkey = new Uint8Array(32);
hchacha(sigma, key, nonce.subarray(0, 16), subkey);
hchacha(sigma, u32(key), u32(nonce.subarray(0, 16)), u32(subkey));
deepStrictEqual(hex.encode(subkey), good);
const subkeySlow = new Uint8Array(32);
slow.hchacha(sigma, key, nonce.subarray(0, 16), subkeySlow);
slow.hchacha(sigma, u32(key), u32(nonce.subarray(0, 16)), u32(subkeySlow));
deepStrictEqual(hex.encode(subkeySlow), good);
});

Expand Down

0 comments on commit b1f55c7

Please sign in to comment.