A share link from s3d only opened where s3d is reachable, which on a default setup is this computer. Pithos now makes a Sia shared-object URL for the file the way the Sia SDK does (the indexer's sealed object, its data key opened with a key derived from s3d's app key) and registers it at navigate.st, which streams the file from Sia hosts for anyone, with seeking, even with this computer off. A shared-object URL reads that one object and nothing else in the account. Files still waiting on this computer are uploaded first. A folder gets a page with its files and a player. Settings lists the links, each one can be switched off. sia-share is the navigate.st side, in Go on the Sia SDK: it keeps short ids, checks every URL against an allowed indexer, serves files sandboxed so a shared page cannot run script on navigate.st, and caps sign-ups per address and downloads per link per day. Host reads are paid by its own app on Silent Mode's indexer account.
38 lines
2.3 KiB
JavaScript
38 lines
2.3 KiB
JavaScript
// Sia share links: the data key is unwrapped exactly as the Sia SDK does it.
|
|
// Vectors were produced with golang.org/x/crypto (hkdf + blake2b.New256,
|
|
// chacha20poly1305.NewX), the calls indexd keys.Derive and siastorage use.
|
|
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { hkdfBlake2b256, hmacBlake2b256, hchacha20, xchachaOpen } from '../core/sia-share.js';
|
|
|
|
const H = (x) => Buffer.from(x, 'hex');
|
|
const V = {
|
|
appKey: '8985f2c92de21efb5ed77756b23f0d1045505f573f12046c5aebf5d30ba384ab2041560b41c83e59ee41030ccf72b53a7724f65463fb2c81dc3e1aa179e500f6',
|
|
objectId: 'a0adf11767768dcaa3e7b2367b191cd29c121ca0e4a2d479995ab6bd55a10a0a',
|
|
kek: 'aa62f4b76f7956e9275811021b493eb35365b38326f4ca8d2d6220fd69647ddf',
|
|
encryptedDataKey: '75b150c47e412e8353420dffe0096e514c2ac4edca06fd9301131ff1c5b31560173bf90185a396143d011775cfee27a68a6150a2b657db1b99f08fa9d91bd31bca28d6a14abd89a4',
|
|
dataKey: '6f73cb85e254a32ebf32eda4e027305c40207fa1688fa456196c538d71d83eac',
|
|
longOkm: '7a113b37916fdc7c05e4edc4c15826475bbf15320e68c341707d95c907fabcc99ad9d14a8b36895b14696e3fbdc5f3dc0c878e045bb733df6fe86a779b67c841659ab68e138aee453ca9218cc5ddabe1',
|
|
};
|
|
|
|
test('HKDF-BLAKE2b-256 matches keys.Derive', () => {
|
|
assert.equal(hkdfBlake2b256(H(V.appKey), H(V.objectId), Buffer.from('dataKey'), 32).toString('hex'), V.kek);
|
|
assert.equal(hkdfBlake2b256(H(V.appKey), Buffer.alloc(0), Buffer.from('x'), 80).toString('hex'), V.longOkm);
|
|
});
|
|
|
|
test('HChaCha20 matches draft-irtf-cfrg-xchacha 2.2.1', () => {
|
|
const key = Buffer.from([...Array(32).keys()]);
|
|
assert.equal(hchacha20(key, H('000000090000004a0000000031415927')).toString('hex'), '82413b4227b27bfed30e42508a877d73a0f9e4d58a74a853c12ec41326d3ecdc');
|
|
});
|
|
|
|
test('XChaCha20-Poly1305 opens a key sealed by chacha20poly1305.NewX, and rejects tampering', () => {
|
|
const enc = H(V.encryptedDataKey);
|
|
assert.equal(xchachaOpen(H(V.kek), enc.subarray(0, 24), enc.subarray(24)).toString('hex'), V.dataKey);
|
|
const bad = Buffer.from(enc); bad[30] ^= 1;
|
|
assert.throws(() => xchachaOpen(H(V.kek), bad.subarray(0, 24), bad.subarray(24)));
|
|
});
|
|
|
|
test('HMAC-BLAKE2b-256 hashes long keys first (RFC 2104)', () => {
|
|
assert.equal(hmacBlake2b256(Buffer.alloc(200, 7), Buffer.from('m')).length, 32);
|
|
assert.notDeepEqual(hmacBlake2b256(Buffer.alloc(129, 1), 'm'), hmacBlake2b256(Buffer.alloc(128, 1), 'm'));
|
|
});
|