aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/harness/playlist_store_probe.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/tests/harness/playlist_store_probe.py')
-rwxr-xr-xpackages/meshbay-hub/tests/harness/playlist_store_probe.py51
1 files changed, 42 insertions, 9 deletions
diff --git a/packages/meshbay-hub/tests/harness/playlist_store_probe.py b/packages/meshbay-hub/tests/harness/playlist_store_probe.py
index 9a54a0d..857cf1e 100755
--- a/packages/meshbay-hub/tests/harness/playlist_store_probe.py
+++ b/packages/meshbay-hub/tests/harness/playlist_store_probe.py
@@ -80,15 +80,15 @@ function fakeNode() {
(async () => {
const fail = (why) => parent.postMessage({ error: why, logs: LOGS.slice(0, 10) }, '*');
try {
- // A real HKDF handle over fixed bytes, as `deriveBundleKeys` would produce
- // — the point is that playlists.js gets its key the way it really does.
+ // A real master key over fixed bytes, the shape `deriveBundleSessionKey`
+ // produces — the point is that playlists.js gets its key the way it
+ // really does.
const raw = new Uint8Array(32).fill(5);
session.bundleKey = {
- v2: await crypto.subtle.importKey('raw', raw, { name: 'AES-GCM' }, false,
- ['encrypt', 'decrypt']),
- v2hkdf: await crypto.subtle.importKey('raw', raw, 'HKDF', false, ['deriveKey']),
+ v3: await crypto.subtle.importKey('raw', raw, 'HKDF', false, ['deriveKey', 'deriveBits']),
+ pepperVersion: 1,
};
- const key = await derivePlaylistKey(session.bundleKey.v2hkdf);
+ const key = await derivePlaylistKey(session.bundleKey.v3);
// ── local editing ──────────────────────────────────────────────────────
const eveningId = await P.createPlaylist(USER, 'Soirée');
@@ -364,6 +364,39 @@ function fakeNode() {
names: readBack ? Object.values(readBack.playlists).map((p) => p.name).sort() : [],
});
+ // ── a node sealed under the previous playlist key ─────────────────────
+ //
+ // The key changed, not the playlists: every row on such a node carries the
+ // revision the local copy has, so "push only when the node is behind"
+ // would leave all of it unreadable for ever. Built from what a clean sync
+ // stores, then every row resealed under another key, plus a body for a
+ // playlist this browser has never heard of.
+ const clean = fakeNode();
+ await P.syncWith(clean, USER);
+ const oldKeyNode = fakeNode();
+ for (const [kind, r] of clean.rows) {
+ oldKeyNode.rows.set(kind, { rev: r.rev, blob: await seal(
+ kind === MANIFEST_KIND ? { v: 1, rev: r.rev, playlists: {} }
+ : { id: kind, rev: r.rev, tracks: [] },
+ kind, USER, junkKey) });
+ }
+ oldKeyNode.rows.set(bodyKind('never-seen-here'), { rev: 3, blob: await seal(
+ { id: 'never-seen-here', rev: 3, tracks: [] }, bodyKind('never-seen-here'), USER, junkKey) });
+ const rekeyResult = await P.syncWith(oldKeyNode, USER);
+ const readable = [];
+ for (const [kind, r] of oldKeyNode.rows) {
+ try { await open(r.blob, kind, USER, key); readable.push(kind); } catch { /* not */ }
+ }
+ steps.push({
+ step: 'a node sealed under the previous key',
+ result: rekeyResult,
+ kinds: [...clean.rows.keys()].sort(),
+ readable: readable.sort(),
+ remaining: [...oldKeyNode.rows.keys()].sort(),
+ revsNotLowered: [...clean.rows].every(([k, r]) =>
+ (oldKeyNode.rows.get(k) || { rev: -1 }).rev >= r.rev),
+ });
+
// ── what a playlist costs, sealed ─────────────────────────────────────
//
// The cap below is in bytes, but the only number a reader can act on is a
@@ -449,12 +482,12 @@ function fakeNode() {
// playlist is not a broken sync.
stored: bigNode.stored.map((e) => ({ kind: e.kind, bytes: e.bytes })) });
- // ── a session with no HKDF handle degrades rather than failing ─────────
+ // ── a session with no bundle key degrades rather than failing ──────────
P.setPlaylistTransport(null);
P.forgetPlaylistKey();
- session.bundleKey = { v2: session.bundleKey.v2 }; // pre-change session
+ session.bundleKey = { v2: 'a key from before the pepper' }; // pre-change session
const r3 = await P.syncWith(fakeNode(), USER);
- steps.push({ step: 'a session from before the HKDF handle', result: r3 });
+ steps.push({ step: 'a session from before the pepper', result: r3 });
parent.postMessage({ steps, logs: LOGS.slice(0, 8) }, '*');
} catch (err) {