docs: add crypto compatibility design document

This commit is contained in:
2025-10-26 12:32:51 -04:00
parent 7122d66e1c
commit 8957f852b8

View File

@@ -0,0 +1,199 @@
# Cross-Environment Crypto Compatibility Design
**Date:** 2025-10-26
**Status:** Approved
**Author:** Design session with user
## Problem Statement
The `generateApiKey()` function in `src/utils/auth-utils.ts` uses `crypto.getRandomValues()` which works in Electron/browser environments but fails in Node.js test environment with "ReferenceError: crypto is not defined". This causes test failures during CI/CD builds.
## Goals
1. Make code work cleanly in both browser/Electron and Node.js environments
2. Use only built-in APIs (no additional npm dependencies)
3. Maintain cryptographic security guarantees
4. Keep production runtime behavior unchanged
5. Enable tests to pass without mocking
## Constraints
- Must use only built-in APIs (no third-party packages)
- Must maintain existing API surface of `generateApiKey()`
- Must preserve cryptographic security in both environments
- Must work with current Node.js version in project
## Architecture
### Component Overview
The solution uses an abstraction layer pattern with environment detection:
1. **crypto-adapter.ts** - New utility that provides unified crypto access
2. **auth-utils.ts** - Modified to use the adapter
3. **crypto-adapter.test.ts** - New test file for adapter verification
### Design Decisions
**Why abstraction layer over other approaches:**
- **vs Runtime detection in auth-utils:** Separates concerns, makes crypto access reusable
- **vs Jest polyfill:** Makes production code environment-aware instead of test-specific workarounds
- **vs Dynamic require():** Cleaner than inline environment detection, easier to test
**Why Web Crypto API standard:**
- Node.js 15+ includes `crypto.webcrypto` implementing the same Web Crypto API as browsers
- Allows using identical API (`getRandomValues()`) in both environments
- Standards-based approach, future-proof
## Implementation
### File: `src/utils/crypto-adapter.ts` (new)
```typescript
/**
* Cross-environment crypto adapter
* Provides unified access to cryptographically secure random number generation
* Works in both browser/Electron (window.crypto) and Node.js (crypto.webcrypto)
*/
/**
* Gets the appropriate Crypto interface for the current environment
* @returns Crypto interface with getRandomValues method
* @throws Error if no crypto API is available
*/
function getCrypto(): Crypto {
// Browser/Electron environment
if (typeof window !== 'undefined' && window.crypto) {
return window.crypto;
}
// Node.js environment (15+) - uses Web Crypto API standard
if (typeof global !== 'undefined') {
const nodeCrypto = require('crypto');
if (nodeCrypto.webcrypto) {
return nodeCrypto.webcrypto;
}
}
throw new Error('No Web Crypto API available in this environment');
}
/**
* Fills a typed array with cryptographically secure random values
* @param array TypedArray to fill with random values
*/
export function getCryptoRandomValues<T extends ArrayBufferView>(array: T): T {
return getCrypto().getRandomValues(array);
}
```
### File: `src/utils/auth-utils.ts` (modified)
```typescript
import { getCryptoRandomValues } from './crypto-adapter';
/**
* Generates a cryptographically secure random API key
* @param length Length of the API key (default: 32 characters)
* @returns A random API key string
*/
export function generateApiKey(length: number = 32): string {
const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
const values = new Uint8Array(length);
// Use cross-environment crypto adapter
getCryptoRandomValues(values);
let result = '';
for (let i = 0; i < length; i++) {
result += charset[values[i] % charset.length];
}
return result;
}
// validateApiKey() remains unchanged
```
### File: `tests/crypto-adapter.test.ts` (new)
Test coverage for the adapter:
- Verify `getCryptoRandomValues()` returns filled array with correct length
- Verify randomness (different calls produce different results)
- Verify it works in Node.js test environment
- Verify type preservation (Uint8Array in = Uint8Array out)
## Error Handling
### Scenarios Covered
1. **Missing crypto API** - Throws descriptive error if neither environment has crypto
2. **Node.js version incompatibility** - Error message guides developers to upgrade
3. **Type safety** - TypeScript ensures correct typed array usage
### Error Messages
- "No Web Crypto API available in this environment" - Clear indication of what's missing
## Testing Strategy
### Existing Tests
- `tests/main-migration.test.ts` - Will now pass without modification
- Uses real Node.js `crypto.webcrypto` instead of mocks
- No change to test assertions needed
### New Tests
- `tests/crypto-adapter.test.ts` - Verifies adapter functionality
- Tests environment detection logic
- Tests randomness properties
- Tests type preservation
### Coverage Impact
- New file adds to overall coverage
- No reduction in existing coverage
- All code paths in adapter are testable
## Production Behavior
### Obsidian/Electron Environment
- Always uses `window.crypto` (first check in getCrypto)
- Zero change to existing runtime behavior
- Same cryptographic guarantees as before
### Node.js Test Environment
- Uses `crypto.webcrypto` (Node.js 15+)
- Provides identical Web Crypto API
- Real cryptographic functions (not mocked)
## Migration Path
### Changes Required
1. Create `src/utils/crypto-adapter.ts`
2. Modify `src/utils/auth-utils.ts` to import and use adapter
3. Create `tests/crypto-adapter.test.ts`
4. Run tests to verify fix
### Backward Compatibility
- No breaking changes to public API
- `generateApiKey()` signature unchanged
- No settings or configuration changes needed
### Rollback Plan
- Single commit contains all changes
- Can revert commit if issues found
- Original implementation preserved in git history
## Benefits
1. **Clean separation of concerns** - Crypto access logic isolated
2. **Standards-based** - Uses Web Crypto API in both environments
3. **Reusable** - Other code can use crypto-adapter for crypto needs
4. **Type-safe** - TypeScript ensures correct usage
5. **Testable** - Each component can be tested independently
6. **No mocking needed** - Tests use real crypto functions
## Future Considerations
- If other utilities need crypto, they can import crypto-adapter
- Could extend adapter with other crypto operations (hashing, etc.)
- Could add feature detection for specific crypto capabilities