# EncryptID: Unified Identity System for the r-Ecosystem *Version 0.1 - February 2026* --- ## Executive Summary **EncryptID** is a unified, self-sovereign identity system designed to work across all r-ecosystem applications (rspace.online, rwallet, rvote, rmaps, rfiles). It combines the security of hardware-backed authentication with the usability of passkeys, the flexibility of derived cryptographic keys, and the power of account abstraction smart wallets. **Core Principles:** - **No seed phrases** - Social recovery replaces mnemonic backup - **Hardware-backed security** - WebAuthn/passkeys as the root of trust - **Client-side encryption** - Keys never leave the user's device - **Cross-app identity** - One login for all r-ecosystem apps - **Web3 native** - Integrated smart wallet for on-chain operations --- ## Architecture Overview ``` +============================================================================+ || ENCRYPTID ARCHITECTURE || +============================================================================+ +----------------------------------------------------------------------------+ | LAYER 5: APPLICATION LAYER | | | | +-------------+ +-------------+ +-------------+ +-------------+ | | | rspace | | rwallet | | rvote | | rfiles | | | | (canvas) | | (treasury) | | (voting) | | (storage) | | | +------+------+ +------+------+ +------+------+ +------+------+ | | | | | | | +----------|----------------|----------------|----------------|-------------+ | | | | v v v v +----------------------------------------------------------------------------+ | LAYER 4: SESSION & SSO LAYER | | | | +--------------------------------------------------------------------+ | | | EncryptID Session Service | | | | | | | | - JWT tokens (short-lived, refresh rotation) | | | | - Cross-app SSO via Related Origin Requests | | | | - Per-app capability scoping | | | | - Session key caching for UX | | | +--------------------------------------------------------------------+ | | | +--------------------------------+-------------------------------------------+ | v +----------------------------------------------------------------------------+ | LAYER 3: SMART WALLET LAYER (AA) | | | | +--------------------------------------------------------------------+ | | | Account Abstraction Smart Wallet (ERC-4337) | | | | | | | | +------------------+ +------------------+ +------------------+ | | | | | Passkey Signer | | Session Keys | | Social Recovery | | | | | | (WebAuthn P256) | | (time-limited) | | (Guardian Module)| | | | | +------------------+ +------------------+ +------------------+ | | | | | | | | Features: | | | | - Gasless transactions (paymaster) | | | | - Batched operations | | | | - Spending limits & policies | | | | - No seed phrase needed | | | +--------------------------------------------------------------------+ | | | +--------------------------------+-------------------------------------------+ | v +----------------------------------------------------------------------------+ | LAYER 2: DERIVED KEYS LAYER | | | | +--------------------------------------------------------------------+ | | | WebCrypto Key Derivation (HKDF) | | | | | | | | Source: WebAuthn PRF output OR passphrase-derived master key | | | | | | | | +---------------+ +---------------+ +---------------+ | | | | | Encryption | | Signing | | DID | | | | | | Key (AES-256) | | Key (P-256) | | Key (Ed25519) | | | | | +-------+-------+ +-------+-------+ +-------+-------+ | | | | | | | | | | | v v v | | | | +---------------+ +---------------+ +---------------+ | | | | | rfiles E2E | | rvote ballot | | did:key:z6Mk | | | | | | rspace boards | | signatures | | identity | | | | | +---------------+ +---------------+ +---------------+ | | | +--------------------------------------------------------------------+ | | | +--------------------------------+-------------------------------------------+ | v +----------------------------------------------------------------------------+ | LAYER 1: PRIMARY AUTHENTICATION | | | | +--------------------------------------------------------------------+ | | | WebAuthn / Passkeys | | | | | | | | +------------------+ +------------------+ +------------------+ | | | | | Platform Passkey | | Security Key | | Cross-Device | | | | | | (iCloud/Google) | | (YubiKey/Titan) | | (QR/Bluetooth) | | | | | +------------------+ +------------------+ +------------------+ | | | | | | | | Properties: | | | | - Phishing-resistant (origin-bound) | | | | - Hardware-backed (TPM/Secure Enclave) | | | | - Biometric (fingerprint/face) | | | | - Syncs across devices (platform providers) | | | +--------------------------------------------------------------------+ | | | +============================================================================+ ``` --- ## Layer 1: Primary Authentication (WebAuthn/Passkeys) ### Why WebAuthn as the Foundation | Property | Benefit | |----------|---------| | **Origin-bound** | Credentials only work on the registered domain - phishing-resistant | | **Hardware-backed** | Private key stored in TPM/Secure Enclave - can't be extracted | | **Biometric** | Face/fingerprint verification - something you *are* | | **Synced** | Platform passkeys sync via iCloud Keychain, Google Password Manager | | **No passwords** | Nothing to remember, nothing to steal | ### Implementation ```typescript // EncryptID Registration Flow async function registerEncryptID(username: string): Promise { const challenge = await fetchChallenge('/api/encryptid/register/challenge'); const credential = await navigator.credentials.create({ publicKey: { challenge: base64ToBuffer(challenge), rp: { name: "EncryptID", id: "encryptid.online" // Shared RP ID for all r-apps }, user: { id: crypto.getRandomValues(new Uint8Array(32)), name: username, displayName: username }, pubKeyCredParams: [ { alg: -7, type: "public-key" }, // ES256 (P-256) { alg: -257, type: "public-key" } // RS256 (fallback) ], authenticatorSelection: { residentKey: "required", // Discoverable credential userVerification: "required", // Biometric/PIN required authenticatorAttachment: "platform" // Prefer built-in authenticator }, attestation: "none", // Privacy-preserving extensions: { prf: { // PRF extension for key derivation eval: { first: base64ToBuffer(await generateSalt("encryptid-master")) } }, credProps: true // Get credential properties } } }); return processRegistration(credential); } ``` ### PRF Extension Support Status (2026) | Platform | Browser | PRF Support | Notes | |----------|---------|-------------|-------| | **Windows** | Chrome/Edge | Full | TPM-backed | | **macOS** | Chrome/Safari | Full | Secure Enclave | | **Android** | Chrome | Full | TEE/StrongBox | | **iOS/iPadOS** | Safari | Partial | Works with iCloud Keychain passkeys | | **Linux** | Chrome | Varies | Depends on FIDO2 library | **Fallback Strategy:** For authenticators without PRF support, derive master key from a user-provided passphrase using Argon2id + HKDF. ### Related Origin Requests for Cross-App SSO Since r-apps span multiple domains, we use [Related Origin Requests](https://passkeys.dev/docs/advanced/related-origins/): ``` // File: https://encryptid.online/.well-known/webauthn { "origins": [ "https://rspace.online", "https://rwallet.online", "https://rvote.online", "https://rmaps.online", "https://rfiles.online", "https://app.rspace.online", "https://dev.rspace.online" ] } ``` This allows a passkey registered with RP ID `encryptid.online` to authenticate on any of these origins. **Browser Support:** - Chrome: Full support - Safari: Full support - Firefox: Under consideration (use iframe fallback) --- ## Layer 2: Derived Keys (WebCrypto API) ### Key Derivation Hierarchy ``` +---------------------------+ | WebAuthn PRF Output | | (32 bytes from HMAC) | +-------------+-------------+ | v +---------------------------+ | Master Key | | (HKDF extract step) | +-------------+-------------+ | +-----------------------+-----------------------+ | | | v v v +-------------------+ +-------------------+ +-------------------+ | Encryption Key | | Signing Key | | DID Key | | (AES-256-GCM) | | (ECDSA P-256) | | (Ed25519) | +-------------------+ +-------------------+ +-------------------+ | | | v v v +-------------------+ +-------------------+ +-------------------+ | rfiles encryption | | rvote signatures | | did:key identity | | rspace E2E boards | | rspace authorship | | verifiable creds | +-------------------+ +-------------------+ +-------------------+ ``` ### Implementation ```typescript // Key Derivation from PRF output class EncryptIDKeyDerivation { private masterKey: CryptoKey; async initFromPRF(prfOutput: ArrayBuffer): Promise { // Import PRF output as HKDF key material this.masterKey = await crypto.subtle.importKey( "raw", prfOutput, { name: "HKDF" }, false, ["deriveKey", "deriveBits"] ); } async initFromPassphrase(passphrase: string, salt: Uint8Array): Promise { // Fallback: derive from passphrase using PBKDF2 const encoder = new TextEncoder(); const passphraseKey = await crypto.subtle.importKey( "raw", encoder.encode(passphrase), { name: "PBKDF2" }, false, ["deriveBits", "deriveKey"] ); const masterBits = await crypto.subtle.deriveBits( { name: "PBKDF2", salt: salt, iterations: 600000, // OWASP 2023 recommendation hash: "SHA-256" }, passphraseKey, 256 ); this.masterKey = await crypto.subtle.importKey( "raw", masterBits, { name: "HKDF" }, false, ["deriveKey", "deriveBits"] ); } async deriveEncryptionKey(): Promise { return crypto.subtle.deriveKey( { name: "HKDF", hash: "SHA-256", salt: new TextEncoder().encode("encryptid-encryption-v1"), info: new TextEncoder().encode("AES-256-GCM") }, this.masterKey, { name: "AES-GCM", length: 256 }, false, // Not extractable ["encrypt", "decrypt", "wrapKey", "unwrapKey"] ); } async deriveSigningKeyPair(): Promise { // Derive deterministic seed for signing key const seed = await crypto.subtle.deriveBits( { name: "HKDF", hash: "SHA-256", salt: new TextEncoder().encode("encryptid-signing-v1"), info: new TextEncoder().encode("ECDSA-P256") }, this.masterKey, 256 ); // Use seed to generate deterministic P-256 key // (Implementation uses noble-curves or similar) return generateP256FromSeed(seed); } async deriveDIDKey(): Promise<{ did: string; privateKey: Uint8Array }> { // Derive Ed25519 key for DID const seed = await crypto.subtle.deriveBits( { name: "HKDF", hash: "SHA-256", salt: new TextEncoder().encode("encryptid-did-v1"), info: new TextEncoder().encode("Ed25519") }, this.masterKey, 256 ); // Generate Ed25519 keypair from seed const keyPair = ed25519.generateKeyPairFromSeed(new Uint8Array(seed)); // Format as did:key const did = `did:key:${base58btc.encode( new Uint8Array([0xed, 0x01, ...keyPair.publicKey]) )}`; return { did, privateKey: keyPair.privateKey }; } } ``` ### Key Usage by Application | Application | Encryption Key | Signing Key | DID Key | Wallet | |-------------|----------------|-------------|---------|--------| | **rspace** | Private boards, E2E content | Document signatures, authorship | Cross-app identity | Premium features | | **rwallet** | - | Transaction signing | Identity verification | Treasury ops | | **rvote** | Encrypted ballot metadata | Ballot signatures | Voter identity | On-chain voting | | **rmaps** | Private location data | Contribution signatures | Contributor identity | - | | **rfiles** | E2E file encryption | File signatures | Sharing identity | Storage payments | --- ## Layer 3: Smart Wallet (Account Abstraction) ### Why Account Abstraction? Traditional EOA (Externally Owned Account) wallets have critical UX problems: - Seed phrases are confusing and easily lost - No built-in recovery mechanism - Gas required for every transaction - Single point of failure (one key = full access) Account Abstraction (ERC-4337) smart wallets solve these: | Feature | EOA Wallet | AA Smart Wallet | |---------|-----------|-----------------| | **Key management** | Seed phrase | Passkey + social recovery | | **Gas** | User pays ETH | Paymaster can sponsor | | **Recovery** | Seed phrase or nothing | Guardian-based recovery | | **Policies** | None | Spending limits, time locks | | **Batching** | One tx at a time | Multiple ops in one tx | ### Provider Comparison | Provider | Passkey Support | Social Recovery | Pricing | Best For | |----------|-----------------|-----------------|---------|----------| | **[ZeroDev](https://zerodev.app/)** | Native (Turnkey) | Guardian module | Usage-based | Full control, custom flows | | **[Safe](https://safe.global/)** | Passkey module | Multi-sig native | Free (gas only) | DAOs, shared treasuries | | **[Privy](https://privy.io/)** | Native | Managed recovery | Enterprise pricing | Quick integration | | **[Turnkey](https://turnkey.com/)** | Native (TEE) | Custom | Volume-based | Infrastructure layer | ### Recommended Architecture: ZeroDev + Safe Hybrid ``` +============================================================================+ | ENCRYPTID SMART WALLET ARCHITECTURE | +============================================================================+ +---------------------------+ | User's Passkey | | (WebAuthn P-256 on device)| +-------------+-------------+ | v +---------------------------+ | Turnkey TEE Signer | | (Secure enclave signing) | +-------------+-------------+ | +------------------+------------------+ | | v v +-------------------------------+ +-------------------------------+ | Personal Smart Wallet | | Community Treasury (Safe) | | (ZeroDev Kernel) | | Multi-sig Wallet | +-------------------------------+ +-------------------------------+ | | | | | Owner: Passkey Validator | | Signers: | | | | - User's EncryptID | | Modules: | | - Other community members | | - Session Key (daily ops) | | - Guardian (recovery) | | - Recovery (guardians) | | | | - Spending Limits | | Threshold: 2 of 3 | | | | | +-------------------------------+ +-------------------------------+ | | v v +-------------------------------+ +-------------------------------+ | Use Cases: | | Use Cases: | | - Personal rwallet | | - Community rwallet | | - rvote governance | | - Shared treasury | | - rfiles payments | | - Grant disbursement | +-------------------------------+ +-------------------------------+ ``` ### Implementation ```typescript import { createKernelAccount, createKernelAccountClient } from "@zerodev/sdk"; import { toWebAuthnKey, toPasskeyValidator } from "@zerodev/passkey-validator"; import { toECDSASigner } from "@zerodev/permissions"; class EncryptIDWallet { private kernelAccount: KernelAccount; private kernelClient: KernelAccountClient; async initialize(webAuthnCredential: PublicKeyCredential): Promise { // Create WebAuthn-based validator const webAuthnKey = await toWebAuthnKey({ passkeyName: "EncryptID", passkeyServerUrl: "https://encryptid.online/passkey", credential: webAuthnCredential }); const passkeyValidator = await toPasskeyValidator(publicClient, { webAuthnKey, entryPoint: ENTRYPOINT_ADDRESS_V07, kernelVersion: KERNEL_V3_1 }); // Create kernel account with passkey as owner this.kernelAccount = await createKernelAccount(publicClient, { plugins: { sudo: passkeyValidator // Passkey has full control }, entryPoint: ENTRYPOINT_ADDRESS_V07, kernelVersion: KERNEL_V3_1 }); // Create client with paymaster for gasless UX this.kernelClient = createKernelAccountClient({ account: this.kernelAccount, chain: base, // or arbitrum, optimism, etc. bundlerTransport: http(BUNDLER_URL), paymaster: createZeroDevPaymasterClient({ chain: base, transport: http(PAYMASTER_URL) }) }); return this.kernelAccount.address; } // Create session key for frictionless UX async createSessionKey(permissions: Permission[], duration: number): Promise { const sessionKey = generatePrivateKey(); const sessionSigner = privateKeyToAccount(sessionKey); const sessionKeyValidator = await toPermissionValidator(publicClient, { entryPoint: ENTRYPOINT_ADDRESS_V07, kernelVersion: KERNEL_V3_1, signer: toECDSASigner({ signer: sessionSigner }), policies: [ toCallPolicy({ permissions: permissions // e.g., only call specific contracts }), toTimestampPolicy({ validUntil: Math.floor(Date.now() / 1000) + duration }), toSpendingLimitPolicy({ limits: [{ token: ETH_ADDRESS, limit: parseEther("0.1") }] }) ] }); // User approves session key with passkey (one biometric prompt) await this.kernelClient.installModule({ module: sessionKeyValidator.address, data: sessionKeyValidator.initData }); return { privateKey: sessionKey, validator: sessionKeyValidator }; } } ``` --- ## Layer 4: Social Recovery (No Seed Phrases!) ### Design Philosophy > "The goal of social recovery is to avoid a single point of failure. If you lose your device, you should not lose your assets. If your guardians conspire against you, they should not be able to steal your assets." > — [Vitalik Buterin](https://vitalik.eth.limo/general/2021/01/11/recovery.html) ### Guardian Configuration ``` +============================================================================+ | ENCRYPTID SOCIAL RECOVERY MODEL | +============================================================================+ +---------------------------+ | Recovery Threshold | | 3 of 5 guardians | +---------------------------+ | +------------+------------+------------+------------+ | | | | | v v v v v +-------------+ +-------------+ +-------------+ +-------------+ +-------------+ | Guardian 1 | | Guardian 2 | | Guardian 3 | | Guardian 4 | | Guardian 5 | +-------------+ +-------------+ +-------------+ +-------------+ +-------------+ | | | | | | | | | | | Secondary | | Trusted | | Trusted | | Hardware | | Institutional| | Passkey | | Friend A | | Friend B | | Key | | Guardian | | (YubiKey) | | (their | | (their | | (offline | | (service) | | | | EncryptID) | | EncryptID) | | backup) | | | +-------------+ +-------------+ +-------------+ +-------------+ +-------------+ Recovery Rules: - Time-lock: 48-hour delay before recovery completes - Notification: User notified immediately when recovery initiated - Cancellation: User can cancel with any valid authenticator - Guardian privacy: Guardians don't know each other's identities ``` ### Guardian Types | Type | Description | Pros | Cons | |------|-------------|------|------| | **Secondary Passkey** | Another device you own (YubiKey, second phone) | Always available, you control | Can lose both devices | | **Trusted Contact** | Friend/family with EncryptID | Human judgment, diverse access | Relationship changes | | **Hardware Key** | Offline YubiKey stored securely | Immune to remote attacks | Physical access needed | | **Institutional** | Service provider (e.g., rspace.online) | Professional, always available | Trust in service | | **Time-delayed Self** | Your own auth after N days | No external trust needed | Attacker has same delay | ### Implementation ```typescript // ZeroDev Recovery Module class EncryptIDRecovery { async setupGuardians(guardians: Guardian[]): Promise { const recoveryConfig = { threshold: 3, // 3 of 5 required delay: 48 * 60 * 60, // 48 hours guardians: guardians.map(g => ({ address: g.address, weight: g.weight || 1 })) }; const recoveryPlugin = await createRecoveryPlugin(recoveryConfig); await this.kernelClient.installModule({ module: recoveryPlugin.address, data: recoveryPlugin.initData }); } async initiateRecovery( newOwner: Address, guardianSignatures: Signature[] ): Promise { // Guardian signs recovery request // This starts the time-lock countdown const recoveryId = await this.kernelClient.sendUserOperation({ userOperation: { callData: encodeFunctionData({ abi: recoveryAbi, functionName: 'initiateRecovery', args: [newOwner, guardianSignatures] }) } }); // Notify user through all channels await notifyUserOfRecovery(this.kernelAccount.address, recoveryId); return { recoveryId, completesAt: Date.now() + 48 * 60 * 60 * 1000 }; } async completeRecovery(recoveryId: string): Promise { // After time-lock, anyone can complete await this.kernelClient.sendUserOperation({ userOperation: { callData: encodeFunctionData({ abi: recoveryAbi, functionName: 'completeRecovery', args: [recoveryId] }) } }); } async cancelRecovery(recoveryId: string): Promise { // User can cancel with any valid auth method await this.kernelClient.sendUserOperation({ userOperation: { callData: encodeFunctionData({ abi: recoveryAbi, functionName: 'cancelRecovery', args: [recoveryId] }) } }); } } ``` ### Guardian Privacy Guardians don't need to know each other - this prevents collusion: ```typescript // Store guardian list as hash on-chain const guardianListHash = keccak256( encodePacked(['address[]'], [guardianAddresses.sort()]) ); // During recovery, reveal full list function verifyGuardians(addresses: Address[], hash: bytes32): boolean { return keccak256(encodePacked(['address[]'], [addresses.sort()])) === hash; } ``` ### Best Practices (per [Vitalik](https://vitalik.eth.limo/general/2021/01/11/recovery.html)) 1. **High guardian count (5-7)** for better security 2. **Diverse social circles** - family, friends, colleagues, institutions 3. **Privacy-preserving** - guardians don't know each other 4. **Time-lock** - gives user window to cancel malicious recovery 5. **Regular verification** - periodically confirm guardians are reachable --- ## Layer 5: Session & Cross-App SSO ### Session Architecture ``` +============================================================================+ | ENCRYPTID SESSION FLOW | +============================================================================+ User encryptid.online r-app (e.g., rspace) | | | | 1. Visit rspace.online | | |------------------------>| | | | 2. Check for EncryptID | | | session cookie | | |----------------------------->| | | | | 3. No session, redirect to encryptid.online/auth | |<-------------------------------------------------------| | | | | 4. WebAuthn authenticate | |------------------------>| | | | | | 5. Derive session keys (if needed) | |<------------------------| | | | | | 6. Issue session token | | | (JWT + refresh) | | |<------------------------| | | | | | 7. Redirect back with auth code | |------------------------------------------------------>| | | | | | 8. Exchange code for tokens | | |<-----------------------------| | | | | 9. Session established, user authenticated | |<-------------------------------------------------------| ``` ### Token Structure ```typescript interface EncryptIDSession { // Standard JWT claims iss: "https://encryptid.online"; sub: string; // DID (did:key:z6Mk...) aud: string[]; // Authorized apps ["rspace.online", "rwallet.online"] iat: number; exp: number; // Short-lived (15 min) // EncryptID-specific claims eid: { walletAddress?: string; // AA wallet if deployed credentialId: string; // WebAuthn credential ID authLevel: 1 | 2 | 3 | 4; // Security level of this session capabilities: { encrypt: boolean; // Has derived encryption key sign: boolean; // Has derived signing key wallet: boolean; // Can authorize wallet ops }; recoveryConfigured: boolean; // Has social recovery set up }; } ``` ### Security Levels ```typescript enum AuthLevel { // Level 1: Session token only (cookie) // Can: View public content, read-only operations // Cannot: Modify data, sign anything BASIC = 1, // Level 2: Recent WebAuthn (within 15 min) // Can: Create/edit content, standard operations // Cannot: High-value transactions, key operations STANDARD = 2, // Level 3: Fresh WebAuthn (just authenticated) // Can: Sign votes, approve transactions // Cannot: Recovery operations, export keys ELEVATED = 3, // Level 4: Fresh WebAuthn + explicit consent // Can: Everything including recovery, key export CRITICAL = 4 } // Operation requirements const operationLevels = { 'rspace:view-public': AuthLevel.BASIC, 'rspace:edit-board': AuthLevel.STANDARD, 'rspace:encrypt-board': AuthLevel.ELEVATED, 'rwallet:view-balance': AuthLevel.BASIC, 'rwallet:send-small': AuthLevel.STANDARD, 'rwallet:send-large': AuthLevel.ELEVATED, 'rwallet:add-guardian': AuthLevel.CRITICAL, 'rvote:view-proposals': AuthLevel.BASIC, 'rvote:cast-vote': AuthLevel.ELEVATED, 'rfiles:download-own': AuthLevel.STANDARD, 'rfiles:upload': AuthLevel.STANDARD, 'rfiles:share': AuthLevel.ELEVATED, 'rfiles:export-keys': AuthLevel.CRITICAL }; ``` --- ## Migration Path from CryptID ### Phase 1: Parallel Systems (Months 1-2) ``` +----------------------------------+ | Current CryptID | | (WebCrypto keypairs in IDB) | +----------------------------------+ || || Users can link || EncryptID passkey vv +----------------------------------+ | EncryptID | | (WebAuthn + derived keys) | +----------------------------------+ - Existing CryptID users continue working - Optional: Link EncryptID passkey to existing account - New users default to EncryptID - Both auth methods valid during transition ``` ### Phase 2: Key Migration (Months 2-3) ```typescript async function migrateCryptIDToEncryptID( cryptidKeyPair: CryptoKeyPair, encryptidMasterKey: CryptoKey ): Promise { // 1. Export CryptID private key (if exportable) // or re-encrypt data with new keys // 2. For each encrypted item: // - Decrypt with CryptID key // - Re-encrypt with EncryptID-derived key // 3. Update identity mappings // - Link old CryptID public key to new DID // - Maintain backward compatibility // 4. Mark migration complete } ``` ### Phase 3: CryptID Sunset (Months 3-6) - New accounts: EncryptID only - Existing accounts: Prompted to upgrade - CryptID becomes legacy fallback - Eventually: Remove CryptID support --- ## Security Considerations ### Threat Model | Threat | Mitigation | |--------|------------| | **Phishing** | WebAuthn is origin-bound; passkeys can't be used on fake sites | | **Device theft** | Biometric required; derived keys not extractable | | **Server compromise** | Keys derived client-side; server never sees private keys | | **Guardian collusion** | High threshold (3/5), diverse selection, time-lock | | **Malicious recovery** | 48h delay, user notification, cancellation window | | **Key extraction** | WebCrypto keys marked non-extractable | | **PRF unavailable** | Fallback to passphrase-derived with strong KDF | ### PRF Key Binding Warning > "Data encrypted with PRF-derived keys is bound exclusively to the specific passkey. Losing the passkey will permanently render the encrypted data inaccessible." > — [Corbado](https://www.corbado.com/blog/passkeys-prf-webauthn) **Mitigation:** 1. Social recovery enables passkey rotation 2. Multiple passkeys (backup hardware key) 3. Encrypted key escrow (wrapped with recovery keys) ### Browser/Authenticator Compatibility Test matrix for WebAuthn features: | Feature | Chrome | Safari | Firefox | Edge | |---------|--------|--------|---------|------| | Basic WebAuthn | Yes | Yes | Yes | Yes | | Discoverable Credentials | Yes | Yes | Yes | Yes | | PRF Extension | Yes | Yes | No | Yes | | Related Origins | Yes | Yes | No | Yes | | Cross-device (Hybrid) | Yes | Yes | Partial | Yes | --- ## Implementation Roadmap ### Sprint 1: Foundation (2 weeks) - [ ] Set up encryptid.online domain - [ ] Implement WebAuthn registration/authentication - [ ] Create Related Origins configuration - [ ] Basic session token issuance ### Sprint 2: Key Derivation (2 weeks) - [ ] Implement PRF-based key derivation - [ ] Passphrase fallback for non-PRF authenticators - [ ] Encryption key integration with rfiles - [ ] Signing key integration with rvote ### Sprint 3: Smart Wallet (3 weeks) - [ ] ZeroDev integration - [ ] Passkey validator deployment - [ ] Session key module - [ ] Paymaster setup for gasless UX ### Sprint 4: Social Recovery (2 weeks) - [ ] Guardian configuration UI - [ ] Recovery initiation flow - [ ] Time-lock and notification system - [ ] Recovery completion and cancellation ### Sprint 5: Cross-App Integration (2 weeks) - [ ] rspace.online integration - [ ] rwallet.online integration - [ ] rvote.online integration - [ ] rfiles.online integration - [ ] rmaps.online integration ### Sprint 6: Migration & Polish (2 weeks) - [ ] CryptID migration tools - [ ] User onboarding flow - [ ] Documentation - [ ] Security audit --- ## References ### WebAuthn & Passkeys - [WebAuthn PRF Extension Guide (Yubico)](https://developers.yubico.com/WebAuthn/Concepts/PRF_Extension/Developers_Guide_to_PRF.html) - [Passkeys & WebAuthn PRF for E2E Encryption (Corbado)](https://www.corbado.com/blog/passkeys-prf-webauthn) - [Related Origin Requests (passkeys.dev)](https://passkeys.dev/docs/advanced/related-origins/) - [Cross-Domain Passkeys (web.dev)](https://web.dev/articles/webauthn-related-origin-requests) ### Account Abstraction - [ZeroDev Documentation](https://docs.zerodev.app/) - [ZeroDev Recovery](https://docs-v4.zerodev.app/use-wallets/recovery) - [Safe Passkey Module](https://docs.safe.global/advanced/passkeys/passkeys-safe) - [Safe Passkey Signer](https://docs.safe.global/sdk/signers/passkeys) ### Social Recovery - [Why We Need Social Recovery Wallets (Vitalik Buterin)](https://vitalik.eth.limo/general/2021/01/11/recovery.html) - [Social Recovery Best Practices](https://yellow.com/learn/social-recovery-wallets-can-they-solve-the-seed-phrase-problem-complete-2025-guide) ### Infrastructure - [Turnkey Whitepaper](https://whitepaper.turnkey.com/architecture) - [Turnkey Embedded Wallets](https://www.turnkey.com/embedded-wallets) - [Privy Documentation](https://docs.privy.io/) ### Decentralized Identity - [DID Core Specification (W3C)](https://www.w3.org/TR/did-core/) - [did:key Method](https://w3c-ccg.github.io/did-method-key/) - [Decentralized Identity Guide 2025 (Dock)](https://www.dock.io/post/decentralized-identity) --- *Document version: 0.1* *Last updated: February 5, 2026* *Author: Jeff Emmett with Claude*