Pithos: share links work inside Theseus (ChaCha20-Poly1305 in plain JS)
Creating a public link in the Theseus add-on failed with 'Unknown cipher': Electron's BoringSSL has neither chacha20 nor chacha20-poly1305 in createCipheriv, and the tests only ran under Node's OpenSSL. Opening Sia's 32-byte data key now uses RFC 8439 ChaCha20 and Poly1305 written out in JS; the Go x/crypto vectors pass under Node and inside Electron 44.
This commit is contained in:
parent
f9eccdc4b5
commit
e4e4b3b587
1 changed files with 53 additions and 3 deletions
|
|
@ -58,13 +58,63 @@ export function hchacha20(key, nonce16) {
|
|||
[0, 1, 2, 3, 12, 13, 14, 15].forEach((w, i) => out.writeUInt32LE(s[w], i * 4));
|
||||
return out;
|
||||
}
|
||||
// ChaCha20 and Poly1305 in plain JS (RFC 8439): Electron's BoringSSL has no
|
||||
// chacha20 or chacha20-poly1305 in createCipheriv ("Unknown cipher" in the
|
||||
// Theseus add-on), and all this ever opens is a 32-byte key.
|
||||
function chachaBlock(key, counter, nonce12) {
|
||||
const s = new Uint32Array(16);
|
||||
s.set([0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]);
|
||||
for (let i = 0; i < 8; i++) s[4 + i] = key.readUInt32LE(i * 4);
|
||||
s[12] = counter >>> 0;
|
||||
for (let i = 0; i < 3; i++) s[13 + i] = nonce12.readUInt32LE(i * 4);
|
||||
const w = Uint32Array.from(s);
|
||||
for (let r = 0; r < 10; r++) {
|
||||
quarter(w, 0, 4, 8, 12); quarter(w, 1, 5, 9, 13); quarter(w, 2, 6, 10, 14); quarter(w, 3, 7, 11, 15);
|
||||
quarter(w, 0, 5, 10, 15); quarter(w, 1, 6, 11, 12); quarter(w, 2, 7, 8, 13); quarter(w, 3, 4, 9, 14);
|
||||
}
|
||||
const out = Buffer.alloc(64);
|
||||
for (let i = 0; i < 16; i++) out.writeUInt32LE((w[i] + s[i]) >>> 0, i * 4);
|
||||
return out;
|
||||
}
|
||||
|
||||
function chachaXor(key, counter, nonce12, data) {
|
||||
const out = Buffer.alloc(data.length);
|
||||
for (let off = 0, c = counter; off < data.length; off += 64, c++) {
|
||||
const ks = chachaBlock(key, c, nonce12);
|
||||
for (let i = 0; i < 64 && off + i < data.length; i++) out[off + i] = data[off + i] ^ ks[i];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function poly1305(key32, msg) {
|
||||
const le = (b) => b.reduceRight((acc, x) => (acc << 8n) | BigInt(x), 0n);
|
||||
const r = le(key32.subarray(0, 16)) & 0x0ffffffc0ffffffc0ffffffc0fffffffn;
|
||||
const s = le(key32.subarray(16, 32));
|
||||
const p = (1n << 130n) - 5n;
|
||||
let acc = 0n;
|
||||
for (let off = 0; off < msg.length; off += 16) {
|
||||
const block = msg.subarray(off, off + 16);
|
||||
acc = ((acc + le(block) + (1n << BigInt(8 * block.length))) * r) % p;
|
||||
}
|
||||
acc = (acc + s) & ((1n << 128n) - 1n);
|
||||
const tag = Buffer.alloc(16);
|
||||
for (let i = 0; i < 16; i++) { tag[i] = Number(acc & 0xffn); acc >>= 8n; }
|
||||
return tag;
|
||||
}
|
||||
|
||||
const pad16 = (n) => Buffer.alloc((16 - (n % 16)) % 16);
|
||||
const le64 = (n) => { const b = Buffer.alloc(8); b.writeBigUInt64LE(BigInt(n)); return b; };
|
||||
|
||||
export function xchachaOpen(key, nonce24, sealed) {
|
||||
const sub = hchacha20(Buffer.from(key), Buffer.from(nonce24).subarray(0, 16));
|
||||
const nonce12 = Buffer.concat([Buffer.alloc(4), Buffer.from(nonce24).subarray(16, 24)]);
|
||||
const ct = Buffer.from(sealed);
|
||||
const d = crypto.createDecipheriv('chacha20-poly1305', sub, nonce12, { authTagLength: 16 });
|
||||
d.setAuthTag(ct.subarray(ct.length - 16));
|
||||
return Buffer.concat([d.update(ct.subarray(0, ct.length - 16)), d.final()]);
|
||||
if (ct.length < 16) throw new Error('sealed data is too short');
|
||||
const body = ct.subarray(0, ct.length - 16);
|
||||
const polyKey = chachaBlock(sub, 0, nonce12).subarray(0, 32);
|
||||
const want = poly1305(polyKey, Buffer.concat([body, pad16(body.length), le64(0), le64(body.length)]));
|
||||
if (!crypto.timingSafeEqual(want, ct.subarray(ct.length - 16))) throw new Error('unable to authenticate data');
|
||||
return chachaXor(sub, 1, nonce12, body);
|
||||
}
|
||||
|
||||
// ---- s3d's database: <drive>/<name> -> Sia object id -------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue