From 8957f852b82ea5bf5d785e22f0b1cde19aa2e71f Mon Sep 17 00:00:00 2001 From: Bill Date: Sun, 26 Oct 2025 12:32:51 -0400 Subject: [PATCH] docs: add crypto compatibility design document --- .../2025-10-26-crypto-compatibility-design.md | 199 ++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 docs/plans/2025-10-26-crypto-compatibility-design.md diff --git a/docs/plans/2025-10-26-crypto-compatibility-design.md b/docs/plans/2025-10-26-crypto-compatibility-design.md new file mode 100644 index 0000000..520ff8d --- /dev/null +++ b/docs/plans/2025-10-26-crypto-compatibility-design.md @@ -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(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