Key Management
Proper key management is essential for maintaining the security of encrypted data. This guide covers key generation, storage approaches, and best practices for managing cryptographic keys in Skiff.Key Generation
Symmetric Key Generation
Generate a 256-bit (32-byte) symmetric key for use with ChaCha20Poly1305 encryption:libs/skiff-crypto/src/keys.ts:14-17
Usage:
- Generates a random 32-byte key using
nacl.randomBytes() - Returns the key as a base64-encoded string
- Uses cryptographically secure random number generation
- Key length matches
nacl.secretbox.keyLength(32 bytes)
Public/Private Key Pair Generation
Generate encryption and signing key pairs:libs/skiff-crypto/src/keys.ts:82-94
Usage:
- Encryption keys (
publicKey,privateKey): Used withnacl.boxfor asymmetric encryption (Curve25519) - Signing keys (
signingPublicKey,signingPrivateKey): Used withnacl.signfor digital signatures (Ed25519)
Derived Key Generation
Skiff provides functions to derive keys from master secrets using HKDF (HMAC-based Key Derivation Function):Password-Derived Secret
Generate a symmetric key from a master secret for encrypting user private keys:libs/skiff-crypto/src/keys.ts:102-110
Parameters:
masterSecret: User’s master secret for HKDF inputsalt: Salt to use in HKDF for key derivation
SRP Authentication Key
Generate a key for Secure Remote Password (SRP) authentication:libs/skiff-crypto/src/keys.ts:59-66
Parameters:
masterSecret: Master secret used for HKDFsalt: Salt for SRP key derivation
Argon2 Key Derivation
Generate a key from a secret using Argon2id (password hashing):libs/skiff-crypto/src/keys.ts:27-35
Parameters:
secret: Secret value (typically a password)argonSalt: Salt for Argon2 hashing
- Winner of the Password Hashing Competition
- Resistant to GPU/ASIC attacks
- Provides both memory-hardness and GPU resistance
- Recommended for password-based key derivation
HKDF Info Parameters
The HKDF function uses different info strings for different key types:libs/skiff-crypto/src/keys.ts:45-49
These ensure that keys derived from the same master secret for different purposes are cryptographically independent.
Key Storage
Client-Side Storage
For browser applications, Skiff uses IndexedDB to store encrypted search indices and keys:libs/skiff-front-search/src/searchIndex.ts:38
Best practices:
- Never store private keys in plain text
- Encrypt private keys with a password-derived key before storage
- Use secure storage mechanisms (IndexedDB, not localStorage)
- Clear keys from memory when no longer needed
Encrypted Key Storage Pattern
Skiff uses a layered encryption approach for storing user keys:- Private keys are never stored in plain text
- Keys can only be decrypted with the user’s password
- Password changes only require re-encrypting the private key
Search Index Encryption
Search indices are encrypted using a hybrid approach:libs/skiff-front-search/src/encryption.ts:35-46
Decryption:
libs/skiff-front-search/src/encryption.ts:16-31
Key Verification
Signing Key Verification
Generate a human-readable verification phrase from a signing key:libs/skiff-crypto/src/keys.ts:120-129
Usage:
- Takes the public signing key
- Generates a checksum using HKDF
- Concatenates key and checksum
- Converts to a mnemonic phrase
Security Best Practices
Key Generation
- Use cryptographically secure random sources: Always use
nacl.randomBytes()or equivalent - Generate keys on the client: Never send private keys over the network
- Use sufficient key lengths: 256-bit (32-byte) keys for symmetric encryption
- Generate fresh keys: Don’t reuse keys across different contexts
Key Storage
- Encrypt private keys at rest: Use password-derived keys to encrypt private keys
- Use secure storage: IndexedDB for browsers, secure enclaves for mobile
- Never log keys: Exclude keys from error messages and logs
- Clear sensitive data: Zero out key material when no longer needed
- Protect against XSS: Use Content Security Policy and input validation
Key Distribution
- Verify public keys: Use out-of-band verification (safety numbers)
- Use authenticated channels: Ensure public keys aren’t tampered with
- Implement key transparency: Log public keys in a verifiable data structure
- Support key rotation: Allow users to update keys periodically
Key Lifecycle
- Generation: Use secure random sources
- Distribution: Protect public keys from tampering
- Storage: Encrypt private keys with password-derived keys
- Usage: Follow proper encryption/decryption protocols
- Rotation: Periodically generate new keys
- Destruction: Securely delete old keys
Password-Based Keys
- Use strong KDFs: Argon2id for passwords, HKDF for key derivation
- Use unique salts: Generate a random salt for each user
- Sufficient iterations: Configure Argon2 with appropriate parameters
- Don’t store passwords: Only store salts and encrypted keys
- Support key derivation: Allow re-deriving keys from passwords
Common Patterns
User Registration
User Login
Sharing Encrypted Data
Troubleshooting
Key Format Issues
Problem: Keys are the wrong format (hex vs base64) Solution:- Most Skiff functions expect base64-encoded keys
- SRP functions require hex-encoded keys
- Use
fromByteArray()for base64,.toString('hex')for hex
Decryption Failures
Problem: “Could not decrypt message” or “invalid key” errors Solutions:- Verify the key is correct and properly formatted
- Check that encryption and decryption use matching key pairs
- Ensure the data hasn’t been corrupted
- Verify datagram types and versions match
Key Storage Issues
Problem: Keys not persisting or being lost Solutions:- Use IndexedDB, not localStorage (more reliable for binary data)
- Always encrypt private keys before storage
- Implement proper error handling for storage operations
- Test key recovery flows thoroughly
Next Steps
Encryption
Learn about encryption methods and APIs
Private Search
Implement encrypted search functionality