10 KiB
10 KiB
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:
- Create and access different "spaces" using different passwords
- Manage multiple named keypairs within each space
- Select which keypair to use for cryptographic operations
- Automatically log out after 15 minutes of inactivity
Architecture
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
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
- KeyPair Structure:
pub struct KeyPair {
pub name: String,
pub verifying_key: VerifyingKey,
pub signing_key: SigningKey,
}
- KeySpace Structure:
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> { ... }
}
- Session Management:
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
- Session Management:
// 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() { ... }
- UI Updates:
// 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:
// 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
-
Unit Tests:
- Test KeyPair and KeySpace functionality
- Test encryption/decryption of spaces
- Test session management
-
Integration Tests:
- Test the full flow from login to using keypairs
- Test auto-logout functionality
- Test persistence to localStorage
-
UI Testing:
- Test the UI components for managing spaces and keypairs
- Test error handling and user feedback
Implementation Timeline
-
Phase 1: Backend Implementation
- Implement the core data structures and functionality
- Update the API layer
- Update the WebAssembly bindings
-
Phase 2: Frontend Implementation
- Implement session management
- Update the UI for space and keypair management
- Update the existing functionality to work with selected keypairs
-
Phase 3: Testing and Refinement
- Test the implementation
- Fix any issues
- Refine the UI based on feedback