pithos/core/blake2b.js
Local Dev cdbc8ad93b Pithos hosting: fixes from an independent review
A server-sent file list could name paths outside the s3d folder (or the key
file in it), and a failed move back deleted every listed path. Transfers now
accept only s3d data paths, check all of them before writing, never
overwrite, and clean up only what they wrote.

A lost reply could lose or fork the drives: move-up and move-down now ask the
server who holds them before touching the local copy, and once the server is
told to release, the local copy is never removed (the release is retried).

The gateway no longer lets one account route another's access key id, refuses
. and .. in shared-key paths, strips presign parameters before re-signing,
counts upload size on disk, stops s3d before it can be restarted mid-move, and
claims sign-up proofs before checking them. The key file is never replaced
when unreadable, a local edit removes the older encrypted copy, listing pages
no longer repeat, and data hashes stream instead of loading whole files.
2026-10-04 03:15:42 +02:00

72 lines
2.8 KiB
JavaScript

// BLAKE2b (RFC 7693) with a chosen digest length. Node's OpenSSL only offers
// BLAKE2b-512, and BLAKE2b-256 is not a truncation of it (the length is part
// of the parameter block). Sia hashes with BLAKE2b-256 everywhere; Pithos only
// needs it for short messages (signing indexer requests), so this favours
// clarity over speed.
const MASK = (1n << 64n) - 1n;
const IV = [
0x6a09e667f3bcc908n, 0xbb67ae8584caa73bn, 0x3c6ef372fe94f82bn, 0xa54ff53a5f1d36f1n,
0x510e527fade682d1n, 0x9b05688c2b3e6c1fn, 0x1f83d9abfb41bd6bn, 0x5be0cd19137e2179n,
];
const SIGMA = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
];
const rotr = (x, n) => ((x >> n) | (x << (64n - n))) & MASK;
function compress(h, block, t, last) {
const m = [];
for (let i = 0; i < 16; i++) m.push(block.readBigUInt64LE(i * 8));
const v = [...h, ...IV];
v[12] ^= t & MASK;
v[13] ^= t >> 64n;
if (last) v[14] ^= MASK;
const g = (a, b, c, d, x, y) => {
v[a] = (v[a] + v[b] + x) & MASK; v[d] = rotr(v[d] ^ v[a], 32n);
v[c] = (v[c] + v[d]) & MASK; v[b] = rotr(v[b] ^ v[c], 24n);
v[a] = (v[a] + v[b] + y) & MASK; v[d] = rotr(v[d] ^ v[a], 16n);
v[c] = (v[c] + v[d]) & MASK; v[b] = rotr(v[b] ^ v[c], 63n);
};
for (let r = 0; r < 12; r++) {
const s = SIGMA[r % 10];
g(0, 4, 8, 12, m[s[0]], m[s[1]]); g(1, 5, 9, 13, m[s[2]], m[s[3]]);
g(2, 6, 10, 14, m[s[4]], m[s[5]]); g(3, 7, 11, 15, m[s[6]], m[s[7]]);
g(0, 5, 10, 15, m[s[8]], m[s[9]]); g(1, 6, 11, 12, m[s[10]], m[s[11]]);
g(2, 7, 8, 13, m[s[12]], m[s[13]]); g(3, 4, 9, 14, m[s[14]], m[s[15]]);
}
for (let i = 0; i < 8; i++) h[i] ^= v[i] ^ v[i + 8];
}
export function blake2b(data, outLen = 32) {
const input = Buffer.from(data);
const h = [...IV];
h[0] ^= 0x01010000n ^ BigInt(outLen);
let t = 0n;
let off = 0;
// Every block but the last is compressed as "not final"; an empty input is
// one zero block marked final.
while (input.length - off > 128) {
t += 128n;
compress(h, input.subarray(off, off + 128), t, false);
off += 128;
}
const last = Buffer.alloc(128);
input.copy(last, 0, off);
t += BigInt(input.length - off);
compress(h, last, t, true);
const out = Buffer.alloc(64);
h.forEach((x, i) => out.writeBigUInt64LE(x, i * 8));
return out.subarray(0, outLen);
}
export const blake2b256 = (data) => blake2b(data, 32);