webassembly/implementation_plan.md
2025-04-19 19:16:58 +02:00

377 lines
10 KiB
Markdown

# Implementation Plan: Multi-Keypair Management with Encrypted Spaces
## Overview
This plan outlines the implementation of a multi-keypair management system with encrypted spaces for the WebAssembly crypto module. The system will allow users to:
1. Create and access different "spaces" using different passwords
2. Manage multiple named keypairs within each space
3. Select which keypair to use for cryptographic operations
4. Automatically log out after 15 minutes of inactivity
## Architecture
```mermaid
graph TD
A[User Interface] --> B[WebAssembly API]
B --> C[Rust Core Implementation]
C --> D[LocalStorage]
subgraph "Frontend (JavaScript)"
A
E[Session Management]
F[UI Components]
G[Event Handlers]
end
subgraph "WebAssembly Bridge"
B
end
subgraph "Backend (Rust)"
C
H[KeySpace Management]
I[KeyPair Management]
J[Encryption/Decryption]
end
E --> A
F --> A
G --> A
H --> C
I --> C
J --> C
```
## Data Model
```mermaid
classDiagram
class KeySpace {
+String name
+Map~String, KeyPair~ keypairs
+encrypt(password)
+decrypt(password)
}
class KeyPair {
+String name
+VerifyingKey publicKey
+SigningKey privateKey
+sign(message)
+verify(message, signature)
}
class LocalStorage {
+Map~String, EncryptedKeySpace~ spaces
}
class Session {
+KeySpace currentSpace
+KeyPair activeKeyPair
+DateTime lastActivity
+Boolean isLoggedIn
}
KeySpace "1" --> "*" KeyPair: contains
LocalStorage "1" --> "*" KeySpace: stores
Session "1" --> "0..1" KeySpace: references
Session "1" --> "0..1" KeyPair: references
```
## Implementation Steps
### 1. Backend (Rust) Changes
#### 1.1 Create New Data Structures
- Create a `KeyPair` struct that includes a name and the existing keypair functionality
- Create a `KeySpace` struct to manage a collection of named keypairs
- Implement serialization/deserialization for these structures
#### 1.2 Update Core Functionality
- Remove the global `KEYPAIR` static variable
- Implement functions to create, retrieve, and manage keypairs by name
- Implement functions to encrypt/decrypt a `KeySpace` using a password
- Add error types for the new functionality
#### 1.3 Update API Layer
- Update the keypair API to work with named keypairs
- Add functions for:
- Creating/opening a space with a password
- Listing available spaces
- Creating a new keypair in the current space
- Listing keypairs in the current space
- Selecting a keypair for operations
- Signing/verifying with the selected keypair
#### 1.4 Update WebAssembly Bindings
- Expose the new API functions to JavaScript
- Ensure proper error handling and type conversions
### 2. Frontend (JavaScript/HTML) Changes
#### 2.1 Update JavaScript Interface
- Create a session management module to track:
- Current space
- Selected keypair
- Login status
- Last activity timestamp
- Implement auto-logout after 15 minutes of inactivity
- Update the existing functions to work with the selected keypair
#### 2.2 Update HTML/UI
- Add a login section with:
- Password input
- Space name input (for creating new spaces)
- Login/Create buttons
- Add space management UI:
- Dropdown to select current space
- Button to create a new space
- Add keypair management UI:
- Dropdown to select current keypair
- Input for new keypair name
- Button to create a new keypair
- Update the existing UI sections to work with the selected keypair
## Detailed Technical Approach
### Backend Implementation Details
1. **KeyPair Structure**:
```rust
pub struct KeyPair {
pub name: String,
pub verifying_key: VerifyingKey,
pub signing_key: SigningKey,
}
```
2. **KeySpace Structure**:
```rust
pub struct KeySpace {
pub name: String,
pub keypairs: HashMap<String, KeyPair>,
}
impl KeySpace {
pub fn new(name: &str) -> Self { ... }
pub fn add_keypair(&mut self, name: &str) -> Result<(), CryptoError> { ... }
pub fn get_keypair(&self, name: &str) -> Option<&KeyPair> { ... }
pub fn list_keypairs(&self) -> Vec<String> { ... }
pub fn encrypt(&self, password: &str) -> Result<Vec<u8>, CryptoError> { ... }
pub fn decrypt(encrypted: &[u8], password: &str) -> Result<Self, CryptoError> { ... }
}
```
3. **Session Management**:
```rust
pub struct Session {
pub current_space: Option<KeySpace>,
pub selected_keypair: Option<String>,
}
static SESSION: OnceCell<Mutex<Session>> = OnceCell::new();
pub fn initialize_session() { ... }
pub fn login(space_name: &str, password: &str) -> Result<(), CryptoError> { ... }
pub fn create_space(space_name: &str, password: &str) -> Result<(), CryptoError> { ... }
pub fn logout() { ... }
pub fn is_logged_in() -> bool { ... }
```
### Frontend Implementation Details
1. **Session Management**:
```javascript
// Session state
let currentSpace = null;
let selectedKeypair = null;
let lastActivity = Date.now();
let logoutTimer = null;
// Activity tracking
function updateActivity() {
lastActivity = Date.now();
}
// Auto-logout
function setupAutoLogout() {
logoutTimer = setInterval(() => {
const inactiveTime = Date.now() - lastActivity;
if (inactiveTime > 15 * 60 * 1000) { // 15 minutes
logout();
}
}, 60000); // Check every minute
}
// Login/logout functions
async function login(spaceName, password) { ... }
async function createSpace(spaceName, password) { ... }
async function logout() { ... }
```
2. **UI Updates**:
```javascript
// Space management
function updateSpacesList() { ... }
function selectSpace(spaceName) { ... }
// Keypair management
function updateKeypairsList() { ... }
function selectKeypair(keypairName) { ... }
function createKeypair(keypairName) { ... }
// Update existing functionality
function signMessage() {
// Check if logged in and keypair selected
if (!selectedKeypair) {
alert("Please select a keypair first");
return;
}
// Get message and sign with selected keypair
const message = document.getElementById('sign-message').value;
const messageBytes = new TextEncoder().encode(message);
try {
const signature = keypair_sign_with_selected(messageBytes);
// ...rest of the function
} catch (e) {
// ...error handling
}
}
```
### LocalStorage Structure
The system will use localStorage to persist encrypted key spaces. The structure will be:
```javascript
// localStorage key format: "crypto_space_{spaceName}"
// localStorage value: JSON string of encrypted space data
// Example localStorage entry:
// Key: "crypto_space_personal"
// Value: {
// "name": "personal",
// "encryptedData": "base64encodedencrypteddata...",
// "createdAt": "2023-04-19T12:00:00Z",
// "lastAccessed": "2023-04-19T15:30:00Z"
// }
// Helper functions for localStorage access
function saveSpaceToStorage(spaceName, encryptedData) {
const storageKey = `crypto_space_${spaceName}`;
const storageData = {
name: spaceName,
encryptedData: btoa(String.fromCharCode.apply(null, new Uint8Array(encryptedData))),
createdAt: new Date().toISOString(),
lastAccessed: new Date().toISOString()
};
localStorage.setItem(storageKey, JSON.stringify(storageData));
}
function getSpaceFromStorage(spaceName) {
const storageKey = `crypto_space_${spaceName}`;
const storageData = JSON.parse(localStorage.getItem(storageKey));
if (!storageData) return null;
// Update last accessed time
storageData.lastAccessed = new Date().toISOString();
localStorage.setItem(storageKey, JSON.stringify(storageData));
// Return the encrypted data as Uint8Array
return new Uint8Array(
atob(storageData.encryptedData)
.split('')
.map(c => c.charCodeAt(0))
);
}
function listSpacesFromStorage() {
const spaces = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key.startsWith('crypto_space_')) {
const spaceName = key.substring('crypto_space_'.length);
spaces.push(spaceName);
}
}
return spaces;
}
function removeSpaceFromStorage(spaceName) {
const storageKey = `crypto_space_${spaceName}`;
localStorage.removeItem(storageKey);
}
```
## UI Mockup
```
+------------------------------------------+
| Rust WebAssembly Crypto Example |
+------------------------------------------+
+------------------------------------------+
| Space Management |
| |
| Password: [________] Space: [________] |
| |
| [Login] [Create Space] [Logout] |
| |
| Current Space: [Dropdown▼] |
+------------------------------------------+
+------------------------------------------+
| Keypair Management |
| |
| Keypair Name: [________] |
| |
| [Create Keypair] |
| |
| Selected Keypair: [Dropdown▼] |
+------------------------------------------+
... (existing UI sections, updated to work with
the selected keypair) ...
```
## Testing Strategy
1. **Unit Tests**:
- Test KeyPair and KeySpace functionality
- Test encryption/decryption of spaces
- Test session management
2. **Integration Tests**:
- Test the full flow from login to using keypairs
- Test auto-logout functionality
- Test persistence to localStorage
3. **UI Testing**:
- Test the UI components for managing spaces and keypairs
- Test error handling and user feedback
## Implementation Timeline
1. **Phase 1: Backend Implementation**
- Implement the core data structures and functionality
- Update the API layer
- Update the WebAssembly bindings
2. **Phase 2: Frontend Implementation**
- Implement session management
- Update the UI for space and keypair management
- Update the existing functionality to work with selected keypairs
3. **Phase 3: Testing and Refinement**
- Test the implementation
- Fix any issues
- Refine the UI based on feedback