aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/app.js
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/app.js')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/app.js91
1 files changed, 29 insertions, 62 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/app.js b/packages/meshbay-hub/src/meshbay_hub/static/app.js
index 94bf09e..300059d 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/app.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/app.js
@@ -144,24 +144,24 @@ function passwordBits(pw) {
const PASSWORD_MIN_BITS = 60; // refuse below this
const PASSWORD_MIN_LEN = 12;
-// Whether this account backs its encrypted keys up to the nodes it joins.
-//
-// On: any browser recovers the same identity with the password — the ordinary
-// multi-device expectation. Off: the keys exist only where they were generated,
-// nothing is left on anyone's disk, and losing this browser's storage loses the
-// account's content for good. That is a real choice, so it is the user's.
-const KEY_BACKUP_PREFIX = 'mb_key_backup_';
-
-function keyBackupEnabled(username) {
- try {
- return localStorage.getItem(KEY_BACKUP_PREFIX + username) !== '0';
- } catch { return true; }
-}
-
-function setKeyBackupEnabled(username, on) {
+/**
+ * Re-encrypt a bundle written under the old KDF before it is stored again.
+ *
+ * PBKDF2 bundles are still readable, but leaving one on a node keeps the weak
+ * protection alive for as long as it sits there. Any backup is an opportunity to
+ * replace it with the Argon2id form, and it costs nothing the user notices.
+ */
+async function _upgradedBundle(bundleEnc) {
try {
- localStorage.setItem(KEY_BACKUP_PREFIX + username, on ? '1' : '0');
- } catch {}
+ if (!window.MeshBayKeys || !_sessionKeys || !_bundleKey) return bundleEnc;
+ if (window.MeshBayKeys.bundleVersion(bundleEnc) === 2) return bundleEnc;
+ const b64 = (s) => Uint8Array.from(atob(s), c => c.charCodeAt(0));
+ return await window.MeshBayKeys.encryptBundleWithKey(
+ b64(_sessionKeys.skEdB64), b64(_sessionKeys.skXB64), _bundleKey.v2);
+ } catch (e) {
+ console.warn('[MeshBay] bundle upgrade skipped:', e.message);
+ return bundleEnc;
+ }
}
function _restoreSessionKeys() {
@@ -979,26 +979,18 @@ function GroupPage({ groupId, group, token, username, userId, onRefreshAuth }) {
}
}
- // Back the encrypted keys up to the node, or withdraw them — whichever
- // this account asked for. The local copy is only dropped once the node
- // holds one, so turning backup off never strands anybody.
- if (transport.connected) {
- if (keyBackupEnabled(username)) {
- if (_pendingBundlePush) {
- try {
- await transport.storeKeypairBundle(_pendingBundlePush);
- try { localStorage.removeItem(`meshbay_kp_${username}`); } catch {}
- _pendingBundlePush = null;
- } catch (e) {
- console.warn('[MeshBay] Bundle push to node deferred:', e.message);
- }
- }
- } else {
- try {
- await transport.deleteKeypairBundle();
- } catch (e) {
- console.warn('[MeshBay] Could not withdraw key backup:', e.message);
- }
+ // Back the encrypted keys up to the node. This is what lets any other
+ // browser recover them with the passphrase, which is the ordinary
+ // expectation; the protection that matters is the KDF guarding the
+ // bundle, not withholding the bundle.
+ if (transport.connected && _pendingBundlePush) {
+ try {
+ await transport.storeKeypairBundle(
+ await _upgradedBundle(_pendingBundlePush));
+ try { localStorage.removeItem(`meshbay_kp_${username}`); } catch {}
+ _pendingBundlePush = null;
+ } catch (e) {
+ console.warn('[MeshBay] Bundle push to node deferred:', e.message);
}
}
@@ -2206,15 +2198,6 @@ function SettingsPage({ user, theme, onThemeChange, groups }) {
const [nodeKeyLoading, setNodeKeyLoading] = useState(false);
const [pinCount, setPinCount] = useState(
() => (window.MeshBayTransport?.pinnedNodeCount?.() ?? 0));
- const [backup, setBackup] = useState(() => keyBackupEnabled(user.username));
-
- // Takes effect on the next connection to each node: enabling uploads the
- // encrypted bundle, disabling withdraws whatever that node already holds.
- const toggleBackup = useCallback(() => {
- const next = !backup;
- setKeyBackupEnabled(user.username, next);
- setBackup(next);
- }, [backup, user.username]);
// 11.5.8: node identity pins are refused strictly on change, so users need a
// deliberate way to accept a legitimate rotation (operator reinstalled a node).
@@ -2313,22 +2296,6 @@ function SettingsPage({ user, theme, onThemeChange, groups }) {
</div>
<div class="settings-section">
- <h3 class="settings-heading">${t('settings.key_backup')}</h3>
- <p class="settings-hint">${t('settings.key_backup_hint')}</p>
- <div class="settings-row">
- <span class="settings-label">
- ${backup ? t('settings.key_backup_on') : t('settings.key_backup_off')}
- </span>
- <button class="btn-secondary" onClick=${toggleBackup}>
- ${backup ? t('settings.key_backup_disable') : t('settings.key_backup_enable')}
- </button>
- </div>
- ${!backup && html`
- <p class="error-msg" style="margin-top:8px">${t('settings.key_backup_warning')}</p>
- `}
- </div>
-
- <div class="settings-section">
<h3 class="settings-heading">${t('settings.node_pins')}</h3>
<p class="settings-hint">${t('settings.node_pins_hint')}</p>
<div class="settings-row">