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

12 KiB

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):

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

    sharenet-passport create [--output FILE] [--security-level LEVEL]
    
  2. import-recovery - Import from recovery phrase

    sharenet-passport import-recovery [--output FILE]
    
  3. import-file - Import from .spf file

    sharenet-passport import-file <FILE> [--security-level LEVEL]
    
  4. export - Export Passport to .spf file

    sharenet-passport export <FILE> [--output FILE]
    
  5. info - Display Passport details

    sharenet-passport info [FILE]
    
  6. sign - Sign a message (testing)

    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

// 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

// 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

// 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

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

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

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:

// 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.