/** * **@noy-db/on-totp** — TOTP (RFC 6238) authenticator-app second factor. * * Generates TOTP secrets, produces the standard `otpauth://` provisioning * URI that authenticator apps parse from QR codes, and verifies * user-entered 6-digit codes in constant time. * * **Zero dependencies.** HMAC-SHA1 runs on the Web Crypto API * (`crypto.subtle`) — same ethos as the rest of noy-db. * * ## Security model * * TOTP is a **second factor**, not an independent strength multiplier. * The secret must live somewhere the verifier can read (otherwise no * one can validate codes), so a compromised verifier leaks the secret. * In noy-db the typical pattern is: * * 1. User sets a secret (primary factor). KEK derives via PBKDF2. * 2. On enroll, a TOTP secret is generated and stored **encrypted * under the KEK** in the user's keyring. * 3. On unlock, the user enters secret → unwraps the secret → * validates the entered code → proceeds only on match. * * This adds "something you have" on top of "something you know" but * does not defend against a secret-KEK compromise. For real * hardware-backed second factor, use `@noy-db/on-webauthn`. * * ## API * * ```ts * import { generateTotpSecret, totpProvisioningUri, verifyTotp, generateTotpCode } from '@noy-db/on-totp' * * // Enroll * const secret = generateTotpSecret() * const uri = totpProvisioningUri(secret, { account: 'alice@acme.com', issuer: 'Acme' }) * // Show QR code of `uri`. * * // Unlock * const ok = await verifyTotp(secret, userEnteredCode) * ``` * * @packageDocumentation */ /** Encode raw bytes as RFC 4648 Base32 (no padding — authenticator apps accept this). */ declare function encodeBase32(bytes: Uint8Array): string; /** Decode RFC 4648 Base32 (case-insensitive, padding-tolerant, whitespace-stripped). */ declare function decodeBase32(input: string): Uint8Array; /** Random 20-byte (160-bit) TOTP secret — RFC 4226 recommended minimum. */ declare function generateTotpSecret(): string; interface TotpProvisioningUriOptions { /** Account label shown in the authenticator — typically the user's email. */ readonly account: string; /** Issuer name shown in the authenticator — your product name. */ readonly issuer?: string; /** Code digits. Default 6. */ readonly digits?: 6 | 7 | 8; /** Step in seconds. Default 30. */ readonly period?: number; /** Hash algorithm. Default SHA1 (compatibility — Google Authenticator etc.). */ readonly algorithm?: 'SHA1' | 'SHA256' | 'SHA512'; } /** Build a standard `otpauth://totp/` URI that authenticator apps parse from QR codes. */ declare function totpProvisioningUri(secret: string, options: TotpProvisioningUriOptions): string; interface TotpOptions { readonly digits?: 6 | 7 | 8; readonly period?: number; readonly algorithm?: 'SHA1' | 'SHA256' | 'SHA512'; } interface VerifyTotpOptions extends TotpOptions { /** * Clock-drift tolerance window, in steps before/after the current * interval. `1` (default) accepts the current step ± 1. `0` is * strictly exact — authenticators rarely stay that precise. */ readonly window?: number; /** Override "now" for deterministic tests. */ readonly timestamp?: number; } /** Compute the TOTP code for the given secret at the current time. */ declare function generateTotpCode(secret: string, options?: TotpOptions): Promise; /** * Verify a user-entered code against the secret. Constant-time comparison; * accepts drift within `window` steps. */ declare function verifyTotp(secret: string, code: string, options?: VerifyTotpOptions): Promise; export { type TotpOptions, type TotpProvisioningUriOptions, type VerifyTotpOptions, decodeBase32, encodeBase32, generateTotpCode, generateTotpSecret, totpProvisioningUri, verifyTotp };