2026-01-13 14:49:19 +01:00

95 lines
3.8 KiB
TypeScript

import { generateRecoveryPhrase } from '../keyManagementBIP39'
import type { EncryptedPayload } from '../keyManagementEncryption'
import { generateKEK } from './crypto'
import { decryptKEK, encryptKEK } from './kekEncryption'
import { storeEncryptedKEK, getEncryptedKEK } from './credentials'
import { decryptPrivateKeyWithKEK, encryptPrivateKeyWithKEK } from './privateKeyEncryption'
export interface CreateAccountResult {
recoveryPhrase: string[]
npub: string
publicKey: string
}
export interface UnlockAccountResult {
privateKey: string
publicKey: string
npub: string
}
export async function createAccountTwoLevel(
privateKeyHex: string,
getPublicKey: (secretKey: Uint8Array) => string,
encodeNpub: (publicKey: string) => string
): Promise<CreateAccountResult> {
const recoveryPhrase = generateRecoveryPhrase()
const kek = await generateKEK()
const encryptedPrivateKey = await encryptPrivateKeyWithKEK(privateKeyHex, kek)
const encryptedKEK = await encryptKEK(kek, recoveryPhrase)
await storeEncryptedKEK(encryptedKEK)
const { storageService } = await import('../storage/indexedDB')
await storageService.set('nostr_encrypted_key', encryptedPrivateKey, 'nostr_key_storage')
const { hexToBytes } = await import('nostr-tools/utils')
const secretKey = hexToBytes(privateKeyHex)
const publicKey = getPublicKey(secretKey)
const npub = encodeNpub(publicKey)
await storageService.set('nostr_public_key', { publicKey, npub }, 'nostr_key_storage')
await storageService.set('nostr_account_exists', true, 'nostr_key_storage')
return { recoveryPhrase, npub, publicKey }
}
export async function unlockAccountTwoLevel(
recoveryPhrase: string[],
getPublicKey: (secretKey: Uint8Array) => string,
encodeNpub: (publicKey: string) => string
): Promise<UnlockAccountResult> {
const encryptedKEK = await getEncryptedKEK()
if (!encryptedKEK) {
throw new Error('No encrypted KEK found in Credentials API')
}
const kek = await decryptKEK(encryptedKEK, recoveryPhrase)
recoveryPhrase.fill('')
const { storageService: indexedDBStorage } = await import('../storage/indexedDB')
const encryptedPrivateKey = await indexedDBStorage.get<EncryptedPayload>('nostr_encrypted_key', 'nostr_key_storage')
if (!encryptedPrivateKey) {
throw new Error('No encrypted private key found in IndexedDB')
}
const privateKeyHex = await decryptPrivateKeyWithKEK(encryptedPrivateKey, kek)
const { hexToBytes } = await import('nostr-tools/utils')
const secretKey = hexToBytes(privateKeyHex)
const publicKey = getPublicKey(secretKey)
const npub = encodeNpub(publicKey)
return { privateKey: privateKeyHex, publicKey, npub }
}
export async function accountExistsTwoLevel(): Promise<boolean> {
try {
const { storageService: indexedDBStorage } = await import('../storage/indexedDB')
const exists = await indexedDBStorage.get<boolean>('nostr_account_exists', 'nostr_key_storage')
return exists === true
} catch (e) {
console.error('[keyManagementTwoLevel] Error checking account existence:', e)
return false
}
}
export async function getPublicKeysTwoLevel(): Promise<{ publicKey: string; npub: string } | null> {
try {
const { storageService: indexedDBStorage } = await import('../storage/indexedDB')
return indexedDBStorage.get<{ publicKey: string; npub: string }>('nostr_public_key', 'nostr_key_storage')
} catch (e) {
console.error('[keyManagementTwoLevel] Error reading public keys:', e)
return null
}
}
export async function deleteAccountTwoLevel(): Promise<void> {
const { storageService: indexedDBStorage } = await import('../storage/indexedDB')
await indexedDBStorage.delete('nostr_encrypted_key')
await indexedDBStorage.delete('nostr_public_key')
await indexedDBStorage.delete('nostr_account_exists')
if (navigator.credentials?.preventSilentAccess) {
void navigator.credentials.preventSilentAccess()
}
}