docs: add crypto compatibility design document
This commit is contained in:
199
docs/plans/2025-10-26-crypto-compatibility-design.md
Normal file
199
docs/plans/2025-10-26-crypto-compatibility-design.md
Normal 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
|
||||
Reference in New Issue
Block a user