95 lines
3.8 KiB
TypeScript
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()
|
|
}
|
|
}
|