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.
72 lines
2.8 KiB
JavaScript
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);
|