Fix Safari doesn't store CryptoKey in IndexedDB

This commit is contained in:
artur 2023-12-01 16:38:11 +03:00
parent 6dde554865
commit 3552447383
4 changed files with 35 additions and 9 deletions

View File

@ -201,7 +201,7 @@ export class NoauthBackend {
})
if (r.status !== 200 && r.status != 201) {
console.log("Fetch error", url, method, r.status)
throw new Error("Failed to fetch"+url)
throw new Error("Failed to fetch" + url)
}
return await r.json();
@ -367,6 +367,7 @@ export class NoauthBackend {
const npub = nip19.npubEncode(pubkey)
const localKey = await this.keysModule.generateLocalKey()
const enckey = await this.keysModule.encryptKeyLocal(sk, localKey)
// @ts-ignore
const dbKey: DbKey = { npub, enckey, localKey }
await dbi.addKey(dbKey)
this.enckeys.push(dbKey)
@ -560,7 +561,11 @@ export class NoauthBackend {
if (!info) throw new Error(`Key ${npub} not found`)
const { type } = nip19.decode(npub)
if (type !== "npub") throw new Error(`Invalid npub ${npub}`)
const sk = await this.keysModule.decryptKeyLocal({ enckey: info.enckey, localKey: info.localKey })
const sk = await this.keysModule.decryptKeyLocal({
enckey: info.enckey,
// @ts-ignore
localKey: info.localKey
})
await this.startKey({ npub, sk })
}
@ -573,7 +578,11 @@ export class NoauthBackend {
private async saveKey(npub: string, passphrase: string) {
const info = this.enckeys.find(k => k.npub === npub)
if (!info) throw new Error(`Key ${npub} not found`)
const sk = await this.keysModule.decryptKeyLocal({ enckey: info.enckey, localKey: info.localKey })
const sk = await this.keysModule.decryptKeyLocal({
enckey: info.enckey,
// @ts-ignore
localKey: info.localKey
})
const { enckey, pwh } = await this.keysModule.encryptKeyPass({ key: sk, passphrase })
await this.sendKeyToServer(npub, enckey, pwh)
}
@ -582,7 +591,7 @@ export class NoauthBackend {
const { type, data: pubkey } = nip19.decode(npub)
if (type !== "npub") throw new Error(`Invalid npub ${npub}`)
const { pwh } = await this.keysModule.generatePassKey(pubkey, passphrase)
const { data: enckey } = await this.fetchKeyFromServer(npub, pwh);
const { data: enckey } = await this.fetchKeyFromServer(npub, pwh);
// key already exists?
const key = this.enckeys.find(k => k.npub === npub)

View File

@ -7,7 +7,6 @@ export interface DbKey {
avatar?: string
relays?: string[]
enckey: string
localKey: CryptoKey
}
export interface DbApp {
@ -57,7 +56,7 @@ export interface DbSchema extends Dexie {
export const db = new Dexie('noauthdb') as DbSchema
db.version(5).stores({
db.version(7).stores({
keys: 'npub',
apps: 'appNpub,npub,name,timestamp',
perms: 'id,npub,appNpub,perm,value,timestamp',

View File

@ -64,7 +64,19 @@ export class Keys {
})
}
public async generateLocalKey(): Promise<CryptoKey> {
private isSafari() {
const chrome = navigator.userAgent.indexOf("Chrome") > -1;
const safari = navigator.userAgent.indexOf("Safari") > -1;
return safari && !chrome
}
public async generateLocalKey(): Promise<CryptoKey | {}> {
// https://github.com/dexie/Dexie.js/issues/585
// Those lazy-asses from Safari still don't allow one
// to store keys in IndexedDB, so for them we have to
// store nsecs in plaintext
if (this.isSafari()) return {}
return await this.subtle.generateKey(
{ name: ALGO_LOCAL, length: KEY_SIZE_LOCAL },
// NOTE: important to make sure it's not visible in
@ -74,14 +86,16 @@ export class Keys {
)
}
public async encryptKeyLocal(key: string, localKey: CryptoKey): Promise<string> {
public async encryptKeyLocal(key: string, localKey: CryptoKey | {}): Promise<string> {
if (this.isSafari()) return key
const nsec = nip19.nsecEncode(key)
const iv = crypto.randomBytes(IV_SIZE)
const encrypted = await this.subtle.encrypt({ name: ALGO_LOCAL, iv }, localKey, Buffer.from(nsec))
return `${PREFIX_LOCAL}:${VERSION_LOCAL}:${iv.toString('hex')}:${Buffer.from(encrypted).toString('hex')}}`
}
public async decryptKeyLocal({ enckey, localKey }: { enckey: string, localKey: CryptoKey }): Promise<string> {
public async decryptKeyLocal({ enckey, localKey }: { enckey: string, localKey: CryptoKey | {} }): Promise<string> {
if (this.isSafari()) return enckey
const parts = enckey.split(':')
if (parts.length !== 4) throw new Error("Bad encrypted key")
if (parts[0] !== PREFIX_LOCAL) throw new Error("Bad encrypted key prefix")

View File

@ -58,6 +58,10 @@ export function register(config?: Config) {
registerValidSW(swUrl, config);
}
});
} else {
if (config && config.onError) {
config.onError(new Error("No service worker"));
}
}
}