UNPKG

accounts

Version:

Tempo Accounts SDK

80 lines 3.78 kB
import { PublicKey, WebCryptoP256 } from 'ox'; import { Account as TempoAccount } from 'viem/tempo'; import { KeyUnavailableError } from '../Keystore.js'; /** * WebCrypto P-256 keystore. The built-in default (see `Keystore.defaults`). * * By default the key is generated non-extractable: the private key never has * a JS-visible encoding, and the handle holds the live `CryptoKey`, which * persists only through structured-clone storage (`Storage.idb`, * `Storage.memory`) — on string-based storage the key is session-only. * * Pass `extractable: true` for environments without structured-clone storage * where keys must survive restarts (e.g. React Native, Node, CLI): the * private key is exported once as a JWK (at-rest protection is the host * app's storage adapter) and re-imported non-extractable each session. In * browsers with IndexedDB storage, prefer the non-extractable default. * * @example * ```ts * import { Keystore, Provider } from 'accounts' * * const provider = Provider.create({ * accessKey: { keystores: { p256: Keystore.webCryptoP256({ extractable: true }) } }, * }) * ``` */ export function webCryptoP256(options = {}) { const { extractable = false } = options; return { requiresStructuredClone: !extractable, async createKey() { if (!globalThis.crypto?.subtle) throw new Error('`webCryptoP256` keystore requires WebCrypto (`crypto.subtle`) support.'); const keyPair = await WebCryptoP256.createKeyPair({ extractable }); const publicKey = PublicKey.toHex(keyPair.publicKey); if (!extractable) return { handle: { keyPair, kind: 'webcrypto-p256' }, publicKey, }; const jwk = await globalThis.crypto.subtle.exportKey('jwk', keyPair.privateKey); return { handle: { jwk, kind: 'webcrypto-p256' }, publicKey }; }, async toAccount(record, context) { const handle = record.handle; if (handle?.kind !== 'webcrypto-p256') throw new Error('Unrecognized `webCryptoP256` keystore handle.'); const account = { access: context.access, keyAuthorizationManager: context.keyAuthorizationManager, }; if (handle.keyPair) { // A live key pair serialized through non-structured-clone storage // arrives mangled — the key is permanently gone. if (!isCryptoKey(handle.keyPair.privateKey)) throw new KeyUnavailableError('`webCryptoP256` key material did not survive storage.'); return TempoAccount.fromWebCryptoP256(handle.keyPair, account); } if (handle.jwk) { const privateKey = await globalThis.crypto.subtle .importKey('jwk', handle.jwk, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['sign']) .catch((error) => { throw new KeyUnavailableError('`webCryptoP256` keystore handle holds unusable key material.', { cause: error }); }); return TempoAccount.fromWebCryptoP256({ privateKey, publicKey: PublicKey.fromHex(record.publicKey) }, account); } throw new Error('Unrecognized `webCryptoP256` keystore handle.'); }, }; } function isCryptoKey(value) { if (typeof CryptoKey !== 'undefined' && value instanceof CryptoKey) return true; // Cross-realm fallback (e.g. keys structured-cloned through IndexedDB). return (!!value && typeof value === 'object' && 'algorithm' in value && value.type === 'private'); } //# sourceMappingURL=webCryptoP256.js.map