sharenet_passport_creator/docs/implementation_plan.md
2025-10-03 23:50:15 -04:00

390 lines
No EOL
12 KiB
Markdown

# Sharenet Passport Creator Implementation Plan
## Overview
A Rust command-line application for creating and managing Sharenet Passports - cryptographic identities for the Sharenet protocol. The tool generates secure identities that can be used to join nodes, sign Cards, and encrypt communications.
## Core Components
### 1. Passport Structure
**Based on Sharenet Spec Sections 11, 27:**
- 24-word recovery phrase (BIP-39)
- Derived Ed25519 keypair for signing
- DID constructed from public key
- Encrypted export format (.spf files)
- Cross-platform compatibility
### 2. Cryptographic Requirements
**Algorithms (Spec Section 15):**
- **Mnemonic**: BIP-39 with 256-bit entropy
- **Key Derivation**: Ed25519 from seed
- **File Encryption**: XChaCha20-Poly1305
- **Key Derivation**: HKDF-SHA256
- **Hashing**: SHA-256
**Rust Crates:**
- `bip39` - BIP-39 mnemonic generation
- `ed25519-dalek` - Ed25519 signatures
- `chacha20poly1305` - XChaCha20-Poly1305 encryption
- `hkdf` - HKDF key derivation
- `sha2` - SHA-256 hashing
- `rand` - CSPRNG for key generation
- `serde_cbor` - CBOR serialization
- `clap` - CLI argument parsing
### 3. File Format (.spf)
**CBOR Structure (before encryption):**
```rust
struct PassportFile {
enc_seed: Vec<u8>, // seed encrypted under KEK
kdf: String, // "HKDF-SHA256"
cipher: String, // "XChaCha20-Poly1305"
salt: Vec<u8>, // 32-byte salt for KEK derivation
nonce: Vec<u8>, // 24-byte nonce for encryption
public_key: Vec<u8>, // Ed25519 public key
did: String, // Generated DID
created_at: u64, // Creation timestamp
version: String, // File format version
}
```
**Storage:**
- File extension: `.spf` (Sharenet Passport File)
- Default location: `~/.sharenet/passports/`
- Secure file permissions (600)
## CLI Interface
### Commands
1. **`create`** - Generate new Passport
```bash
sharenet-passport create [--output FILE] [--security-level LEVEL]
```
2. **`import-recovery`** - Import from recovery phrase
```bash
sharenet-passport import-recovery [--output FILE]
```
3. **`import-file`** - Import from .spf file
```bash
sharenet-passport import-file <FILE> [--security-level LEVEL]
```
4. **`export`** - Export Passport to .spf file
```bash
sharenet-passport export <FILE> [--output FILE]
```
5. **`info`** - Display Passport details
```bash
sharenet-passport info [FILE]
```
6. **`sign`** - Sign a message (testing)
```bash
sharenet-passport sign <FILE> <MESSAGE>
```
### Security Levels
Users can choose their preferred security/convenience trade-off:
1. **`maximum`** - Password required for every operation
2. **`session`** - Password on app start, keys in memory until close (default)
3. **`timeout=Xh`** - Password required every X hours
4. **`keychain`** - Password once, keys stored in system keychain (desktop only)
## Implementation Phases
### Phase 1: Core Cryptographic Library
- [ ] BIP-39 mnemonic generation and validation
- [ ] Ed25519 key derivation from seed
- [ ] XChaCha20-Poly1305 encryption/decryption
- [ ] HKDF key derivation
- [ ] CBOR serialization/deserialization
### Phase 2: Passport Data Structures
- [ ] Passport struct with recovery phrase, keys, DID
- [ ] .spf file format implementation
- [ ] File I/O operations with error handling
- [ ] Memory zeroization for sensitive data
### Phase 3: CLI Implementation
- [ ] Command parsing with clap
- [ ] Secure password input (no echo)
- [ ] File permission enforcement
- [ ] User-friendly output formatting
### Phase 4: Security Features
- [ ] Multiple security level implementations
- [ ] System keychain integration (desktop)
- [ ] Session management
- [ ] Error handling and validation
### Phase 5: Testing & Documentation
- [ ] Unit tests for cryptographic operations
- [ ] Integration tests for CLI workflows
- [ ] Security testing and audit
- [ ] User documentation and examples
## Project Structure
```
sharenet-passport-cli/
├── src/
│ ├── main.rs # CLI entry point
│ ├── cli/
│ │ ├── mod.rs # Command definitions
│ │ ├── create.rs # Create command
│ │ ├── import.rs # Import commands
│ │ └── export.rs # Export command
│ ├── crypto/
│ │ ├── mod.rs # Cryptographic operations
│ │ ├── bip39.rs # BIP-39 implementation
│ │ ├── encryption.rs # File encryption
│ │ └── keys.rs # Key generation
│ ├── passport/
│ │ ├── mod.rs # Passport data structures
│ │ ├── file_format.rs # .spf file handling
│ │ └── did.rs # DID generation
│ ├── storage/
│ │ ├── mod.rs # Storage abstractions
│ │ ├── file_system.rs # File I/O
│ │ └── keychain.rs # System keychain (desktop)
│ └── error.rs # Error types
├── Cargo.toml
├── tests/
│ ├── unit/
│ └── integration/
└── docs/
└── implementation_plan.md
```
## Security Considerations
- Zeroize sensitive memory after use
- Secure password input handling
- File permission enforcement
- Cryptographic randomness verification
- Recovery phrase validation
- Error handling without information leakage
## Testing Strategy
**Unit Tests:**
- Mnemonic generation and validation
- Key derivation consistency
- Encryption/decryption round-trip
- File format serialization
**Integration Tests:**
- Full CLI workflow (create → export → import → sign)
- Cross-platform file handling
- Password recovery scenarios
**Security Tests:**
- Memory zeroization verification
- File permission validation
- Cryptographic randomness testing
---
# .spf File Import and Usage in Applications
*This section details how .spf files would be imported and used in various application types, beyond the scope of the Passport Creator CLI itself.*
## Web Applications
### Security Limitations
- Cannot access system keychains or secure storage
- Limited to browser storage (IndexedDB, localStorage)
- Private keys stored in potentially extractable formats
### Implementation Options
#### Option 1: Session-Based (Recommended)
```javascript
// On app start
async function loadPassport(spfFile, password) {
const passport = await decryptSPF(spfFile, password);
// Keep private key in memory only
sessionStorage.setItem('passport_loaded', 'true');
return passport;
}
// On app close or browser refresh
function cleanup() {
// Clear keys from memory
sessionStorage.removeItem('passport_loaded');
}
```
#### Option 2: Encrypted Browser Storage
```javascript
// With user consent and security warning
async function storePassport(spfFile, password, storagePassword) {
const passport = await decryptSPF(spfFile, password);
const encryptedKey = await encryptForStorage(
passport.privateKey,
storagePassword
);
localStorage.setItem('encrypted_private_key', encryptedKey);
}
// Requires storage password on each use
async function loadFromStorage(storagePassword) {
const encrypted = localStorage.getItem('encrypted_private_key');
return await decryptFromStorage(encrypted, storagePassword);
}
```
#### Option 3: Per-Operation Password
```javascript
// Maximum security, maximum inconvenience
async function signMessage(spfFile, password, message) {
const passport = await decryptSPF(spfFile, password);
const signature = await passport.sign(message);
// Immediately clear from memory
return signature;
}
```
### Web Security Trade-offs
- **Session-based**: Good balance, but keys lost on browser close
- **Encrypted storage**: Convenient but relies on user-chosen password strength
- **Per-operation**: Most secure but poor user experience
## Native Mobile Applications
### Android Implementation
#### Using Android Keystore
```kotlin
class PassportManager {
private val keyStore = KeyStore.getInstance("AndroidKeyStore")
fun importSPF(spfFile: File, password: String) {
// Decrypt .spf file
val passport = decryptSPF(spfFile, password)
// Generate new keypair in Android Keystore
val keyPair = generateKeyPairInKeystore("sharenet_passport")
// The private key never leaves secure hardware
// Future operations use Keystore signing
}
fun signData(data: ByteArray): ByteArray {
// Sign directly using Keystore
return keyStore.getKey("sharenet_passport", null).sign(data)
}
}
```
#### Alternative: Encrypted SharedPreferences
```kotlin
fun storeInEncryptedPrefs(passport: Passport, password: String) {
val encryptedPrefs = EncryptedSharedPreferences.create(
"sharenet_passport",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
)
// Store encrypted private key
encryptedPrefs.edit()
.putString("encrypted_private_key", encryptKey(passport.privateKey, password))
.apply()
}
```
### iOS Implementation
#### Using iOS Keychain
```swift
class PassportManager {
func importSPF(spfFile: URL, password: String) throws {
// Decrypt .spf file
let passport = try decryptSPF(spfFile, password: password)
// Store private key in Keychain
let query: [String: Any] = [
kSecClass as String: kSecClassKey,
kSecAttrApplicationTag as String: "sharenet.passport.private",
kSecValueRef as String: passport.privateKey,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemAdd(query as CFDictionary, nil)
}
func signData(_ data: Data) throws -> Data {
// Retrieve from Keychain and sign
let privateKey = try retrievePrivateKey()
return try privateKey.sign(data: data)
}
}
```
## Desktop Applications
### Cross-Platform Secure Storage
#### Using system keychains:
- **macOS**: Keychain Services
- **Linux**: libsecret / GNOME Keyring
- **Windows**: Credential Manager
#### Implementation Pattern:
```rust
// After importing .spf file once
fn store_in_keychain(passport: &Passport, password: &str) -> Result<()> {
let encrypted_key = encrypt_for_storage(&passport.private_key, password)?;
#[cfg(target_os = "macos")]
keychain::macos::store("sharenet_passport", &encrypted_key)?;
#[cfg(target_os = "linux")]
keychain::linux::store("sharenet_passport", &encrypted_key)?;
#[cfg(target_os = "windows")]
keychain::windows::store("sharenet_passport", &encrypted_key)?;
Ok(())
}
```
## Password Decryption Scheme
### .spf File Decryption Process
1. **Read .spf file** and parse CBOR structure
2. **Derive KEK** from user password using HKDF:
```
KEK = HKDF-SHA256(salt, password, info="sharenet-passport-kek")
```
3. **Decrypt seed** using XChaCha20-Poly1305:
```
seed = XChaCha20-Poly1305-Decrypt(KEK, nonce, enc_seed)
```
4. **Regenerate keys** from seed using BIP-39 derivation
5. **Verify integrity** by comparing generated public key with stored public key
### Application-Specific Storage
After initial .spf decryption, applications can choose their storage strategy:
- **Web**: Keep in memory or encrypt with separate password for browser storage
- **Mobile**: Store in platform secure storage (Keystore/Keychain)
- **Desktop**: Store in system keychain or keep in memory
### Security Considerations for Each Platform
- **Web**: Highest risk - recommend session-based or per-operation passwords
- **Mobile**: Good security through platform mechanisms
- **Desktop**: Excellent security through system keychains
This approach allows users to maintain the same identity across different application types while each platform implements appropriate security measures for its environment.