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