Merge pull request #24 from outis1one/claude/read-repo-wA3y1

fix: cross-browser sync decryption + WebAuthn on extension pages
This commit is contained in:
Outis
2026-03-27 00:39:03 -04:00
committed by GitHub
2 changed files with 30 additions and 8 deletions
+3
View File
@@ -374,6 +374,9 @@ const SilentSendCrypto = {
* Check if WebAuthn is available in this browser. * Check if WebAuthn is available in this browser.
*/ */
isWebAuthnAvailable() { isWebAuthnAvailable() {
// WebAuthn doesn't work on extension pages (chrome-extension:// origin)
if (typeof location !== 'undefined' && location.protocol === 'chrome-extension:') return false;
if (typeof location !== 'undefined' && location.protocol === 'moz-extension:') return false;
return !!(window.PublicKeyCredential && navigator.credentials); return !!(window.PublicKeyCredential && navigator.credentials);
}, },
+27 -8
View File
@@ -464,23 +464,42 @@ const SilentSendSync = {
async _decryptFromSync(data) { async _decryptFromSync(data) {
if (!data?._ssEncrypted) return { data, decrypted: false }; if (!data?._ssEncrypted) return { data, decrypted: false };
// If this device has no encryption config yet, bootstrap from the sync envelope // Always use the sync envelope's salt/verificationBlob for decryption,
// not the local config. Different devices have different salts, so the
// local key won't decrypt data encrypted with another device's salt.
let config = await this._getSyncEncryption(); let config = await this._getSyncEncryption();
if (!config?.enabled && data._encConfig) {
// Save minimal config so authenticate() can work if (data._encConfig) {
// Use the source's salt for this decryption, but don't overwrite
// the local config permanently yet — only if decryption succeeds
config = { config = {
...(config || {}),
enabled: true, enabled: true,
salt: data._encConfig.salt, salt: data._encConfig.salt,
verificationBlob: data._encConfig.verificationBlob, verificationBlob: data._encConfig.verificationBlob,
authMethod: 'password', // will be updated from _encMeta after decryption
ttlDays: 90,
webauthn: false,
}; };
await this._saveSyncEncryption(config);
} }
const keyInfo = await this._getEncryptionKey(); if (!config?.enabled) {
return { data: null, decrypted: false, needsAuth: true };
}
// Try to get a key using the sync envelope's salt
// First check if we have a cached key that matches
let keyInfo = await SilentSendCrypto.getCachedKey();
// If the cached key's salt doesn't match the sync data's salt,
// we need to re-derive from the password
if (keyInfo && data._encConfig && keyInfo.salt !== data._encConfig.salt) {
keyInfo = null; // force re-auth with the correct salt
}
if (!keyInfo) { if (!keyInfo) {
// Need the user to enter the password — save the sync salt temporarily
// so authenticate() uses it to derive the correct key
if (data._encConfig) {
await this._saveSyncEncryption(config);
}
return { data: null, decrypted: false, needsAuth: true }; return { data: null, decrypted: false, needsAuth: true };
} }