Skip to content

Commit

Permalink
add some comments
Browse files Browse the repository at this point in the history
  • Loading branch information
paulmillr committed Jul 14, 2023
1 parent 12210d9 commit 4d46445
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 24 deletions.
54 changes: 47 additions & 7 deletions src/_micro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,39 +130,59 @@ export function hchacha(c: Uint32Array, key: Uint8Array, nonce: Uint8Array): Uin
return u.u8(new Uint32Array([x[0], x[1], x[2], x[3], x[12], x[13], x[14], x[15]]));
}

// Specific implementations
/**
* salsa20, 12-byte nonce.
*/
export const salsa20 = salsaBasic({ core: salsaCore, counterRight: true });

/**
* xsalsa20, 24-byte nonce.
*/
export const xsalsa20 = salsaBasic({
core: salsaCore,
counterRight: true,
extendNonceFn: hsalsa,
allow128bitKeys: false,
});
// Original DJB ChaCha20, 8 bytes nonce, 8 bytes counter

/**
* chacha20 non-RFC, original version by djb. 8-byte nonce, 8-byte counter.
*/
export const chacha20orig = salsaBasic({ core: chachaCore, counterRight: false, counterLen: 8 });
// Also known as chacha20-tls (IETF), 12 bytes nonce, 4 bytes counter
/**
* chacha20 RFC 8439 (IETF / TLS). 12-byte nonce, 4-byte counter.
*/
export const chacha20 = salsaBasic({
core: chachaCore,
counterRight: false,
counterLen: 4,
allow128bitKeys: false,
});

// xchacha draft RFC https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
/**
* xchacha20 eXtended-nonce. https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
*/
export const xchacha20 = salsaBasic({
core: chachaCore,
counterRight: false,
counterLen: 8,
extendNonceFn: hchacha,
allow128bitKeys: false,
});
// Reduced-round chacha, described in original paper

/**
* 8-round chacha from the original paper.
*/
export const chacha8 = salsaBasic({
core: chachaCore,
counterRight: false,
counterLen: 4,
rounds: 8,
});

/**
* 12-round chacha from the original paper.
*/
export const chacha12 = salsaBasic({
core: chachaCore,
counterRight: false,
Expand All @@ -172,7 +192,7 @@ export const chacha12 = salsaBasic({

const POW_2_130_5 = 2n ** 130n - 5n;
const POW_2_128_1 = 2n ** (16n * 8n) - 1n;
// Can be speed-up using BigUint64Array, but use take more code
// Can be speed-up using BigUint64Array, but would be more complicated
export function poly1305(msg: Uint8Array, key: Uint8Array): Uint8Array {
u.ensureBytes(msg);
u.ensureBytes(key);
Expand Down Expand Up @@ -215,7 +235,9 @@ function computeTag(
return poly1305(u.concatBytes(...res), authKey);
}

// Also known as 'tweetnacl secretbox'
/**
* xsalsa20-poly1305 eXtended-nonce (24 bytes) salsa.
*/
export function xsalsa20_poly1305(key: Uint8Array, nonce: Uint8Array) {
u.ensureBytes(key);
u.ensureBytes(nonce);
Expand All @@ -241,6 +263,16 @@ export function xsalsa20_poly1305(key: Uint8Array, nonce: Uint8Array) {
};
}

/**
* Alias to xsalsa20-poly1305
*/
export function secretbox(key: Uint8Array, nonce: Uint8Array) {
u.ensureBytes(key);
u.ensureBytes(nonce);
const xs = xsalsa20_poly1305(key, nonce);
return { seal: xs.encrypt, open: xs.decrypt };
}

export const _poly1305_aead =
(fn: typeof chacha20) =>
(key: Uint8Array, nonce: Uint8Array, AAD?: Uint8Array): u.Cipher => {
Expand Down Expand Up @@ -269,5 +301,13 @@ export const _poly1305_aead =
};
};

/**
* chacha20-poly1305 12-byte-nonce chacha.
*/
export const chacha20_poly1305 = _poly1305_aead(chacha20);

/**
* xchacha20-poly1305 eXtended-nonce (24 bytes) chacha.
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
*/
export const xchacha20_poly1305 = _poly1305_aead(xchacha20);
2 changes: 1 addition & 1 deletion src/_salsa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import assert from './_assert.js';
import { u32, utf8ToBytes, checkOpts } from './utils.js';

/*
RFC7539 requires multi-step cipher stream, where
RFC8439 requires multi-step cipher stream, where
authKey starts with counter: 0, actual msg with counter: 1.
For this, we need a way to re-use nonce / counter:
Expand Down
63 changes: 49 additions & 14 deletions src/chacha.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import { salsaBasic } from './_salsa.js';
// Left rotate for uint32
const rotl = (a: number, b: number) => (a << b) | (a >>> (32 - b));

/**
* ChaCha core function.
*/
// prettier-ignore
function chachaCore(c: Uint32Array, k: Uint32Array, n: Uint32Array, out: Uint32Array, cnt: number, rounds = 20): void {
let y00 = c[0], y01 = c[1], y02 = c[2], y03 = c[3]; // "expa" "nd 3" "2-by" "te k"
Expand Down Expand Up @@ -73,11 +76,17 @@ function chachaCore(c: Uint32Array, k: Uint32Array, n: Uint32Array, out: Uint32A
out[oi++] = (y12 + x12) | 0; out[oi++] = (y13 + x13) | 0;
out[oi++] = (y14 + x14) | 0; out[oi++] = (y15 + x15) | 0;
}
/**
* hchacha helper method, used primarily in xchacha, to hash
* key and nonce into key' and nonce'.
* Same as chachaCore, but there doesn't seem to be a way to move the block
* out without 25% performance hit.
*/
// prettier-ignore
export function hchacha(c: Uint32Array, key: Uint8Array, src: Uint8Array, dst: Uint8Array): Uint8Array {
export function hchacha(c: Uint32Array, key: Uint8Array, src: Uint8Array, out: Uint8Array): Uint8Array {
const k32 = u32(key);
const i32 = u32(src);
const o32 = u32(dst);
const o32 = u32(out);
let x00 = c[0], x01 = c[1], x02 = c[2], x03 = c[3];
let x04 = k32[0], x05 = k32[1], x06 = k32[2], x07 = k32[3];
let x08 = k32[4], x09 = k32[5], x10 = k32[6], x11 = k32[7]
Expand Down Expand Up @@ -131,19 +140,28 @@ export function hchacha(c: Uint32Array, key: Uint8Array, src: Uint8Array, dst: U
o32[5] = x13;
o32[6] = x14;
o32[7] = x15;
return dst;
return out;
}
// Original DJB ChaCha20, 8 bytes nonce, 8 bytes counter
/**
* Original, non-RFC chacha20 from DJB. 8-byte nonce, 8-byte counter.
*/
export const chacha20orig = salsaBasic({ core: chachaCore, counterRight: false, counterLen: 8 });
// Also known as chacha20-tls (IETF), 12 bytes nonce, 4 bytes counter
/**
* ChaCha stream cipher. Conforms to RFC 8439 (IETF, TLS). 12-byte nonce, 4-byte counter.
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
*/
export const chacha20 = salsaBasic({
core: chachaCore,
counterRight: false,
counterLen: 4,
allow128bitKeys: false,
});

// xchacha draft RFC https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
/**
* XChaCha eXtended-nonce ChaCha. 24-byte nonce.
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
*/
export const xchacha20 = salsaBasic({
core: chachaCore,
counterRight: false,
Expand All @@ -152,13 +170,19 @@ export const xchacha20 = salsaBasic({
allow128bitKeys: false,
});

// Reduced-round chacha, described in original paper
/**
* Reduced 8-round chacha, described in original paper.
*/
export const chacha8 = salsaBasic({
core: chachaCore,
counterRight: false,
counterLen: 4,
rounds: 8,
});

/**
* Reduced 12-round chacha, described in original paper.
*/
export const chacha12 = salsaBasic({
core: chachaCore,
counterRight: false,
Expand Down Expand Up @@ -195,13 +219,15 @@ const computeTag = (
return res;
};

// salsa and chacha (RFC 7539) use poly1305 differently.
// We could have composed them such as in:
// https://github.com/paulmillr/scure-base/blob/b266c73dde977b1dd7ef40ef7a23cc15aab526b3/index.ts#L250
// But authKey construction makes it hard:
// - salsa: authKey changes position in salsa stream
// - chacha: authKey can't be computed inside computeTag, it modifies the counter
// Algo from RFC 7539.
/**
* AEAD algorithm from RFC 8439.
* Salsa20 and chacha (RFC 8439) use poly1305 differently.
* We could have composed them similar to:
* https://github.com/paulmillr/scure-base/blob/b266c73dde977b1dd7ef40ef7a23cc15aab526b3/index.ts#L250
* But it's hard because of authKey:
* In salsa20, authKey changes position in salsa stream.
* In chacha, authKey can't be computed inside computeTag, it modifies the counter.
*/
export const _poly1305_aead =
(fn: typeof chacha20) =>
(key: Uint8Array, nonce: Uint8Array, AAD?: Uint8Array): Cipher => {
Expand Down Expand Up @@ -229,5 +255,14 @@ export const _poly1305_aead =
};
};

/**
* ChaCha20-Poly1305 from RFC 8439.
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
*/
export const chacha20_poly1305 = _poly1305_aead(chacha20);
/**
* XChaCha20-Poly1305 extended-nonce chacha.
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
*/
export const xchacha20_poly1305 = _poly1305_aead(xchacha20);
36 changes: 34 additions & 2 deletions src/salsa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import { poly1305 } from './_poly1305.js';
// Left rotate for uint32
const rotl = (a: number, b: number) => (a << b) | (a >>> (32 - b));

// NOTE: same as hsalsa, but we cannot move out block without 25% perf loss :(
/**
* Salsa20 core function.
*/
// prettier-ignore
function salsaCore(c: Uint32Array, k: Uint32Array, i: Uint32Array, out: Uint32Array, cnt: number, rounds = 20): void {
// Based on https://cr.yp.to/salsa20.html
Expand Down Expand Up @@ -53,6 +55,13 @@ function salsaCore(c: Uint32Array, k: Uint32Array, i: Uint32Array, out: Uint32Ar
out[oi++] = (y12 + x12) | 0; out[oi++] = (y13 + x13) | 0;
out[oi++] = (y14 + x14) | 0; out[oi++] = (y15 + x15) | 0;
}

/**
* hsalsa hashing function, used primarily in xsalsa, to hash
* key and nonce into key' and nonce'.
* Same as salsaCore, but there doesn't seem to be a way to move the block
* out without 25% performance hit.
*/
// prettier-ignore
export function hsalsa(c: Uint32Array, key: Uint8Array, nonce: Uint8Array, out: Uint8Array): Uint8Array {
const k32 = u32(key);
Expand Down Expand Up @@ -92,15 +101,28 @@ export function hsalsa(c: Uint32Array, key: Uint8Array, nonce: Uint8Array, out:
return out;
}

/**
* Salsa20 from original paper.
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
*/
export const salsa20 = salsaBasic({ core: salsaCore, counterRight: true });

/**
* xsalsa20 eXtended-nonce salsa.
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
*/
export const xsalsa20 = salsaBasic({
core: salsaCore,
counterRight: true,
extendNonceFn: hsalsa,
allow128bitKeys: false,
});

// Also known as 'tweetnacl secretbox'
/**
* xsalsa20-poly1305 eXtended-nonce salsa.
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
* Also known as secretbox from libsodium / nacl.
*/
export const xsalsa20_poly1305 = (key: Uint8Array, nonce: Uint8Array): Cipher => {
const tagLength = 16;
ensureBytes(key, 32);
Expand Down Expand Up @@ -139,3 +161,13 @@ export const xsalsa20_poly1305 = (key: Uint8Array, nonce: Uint8Array): Cipher =>
},
};
};

/**
* Alias to xsalsa20poly1305, for compatibility with libsodium / nacl
*/
export function secretbox(key: Uint8Array, nonce: Uint8Array) {
ensureBytes(key);
ensureBytes(nonce);
const xs = xsalsa20_poly1305(key, nonce);
return { seal: xs.encrypt, open: xs.decrypt };
}

0 comments on commit 4d46445

Please sign in to comment.