-
Notifications
You must be signed in to change notification settings - Fork 0
Description
📌 Parent Epic
#143 (Client-Side File Encryption for Zero-Knowledge Architecture)
🎯 Goal
Implement core encryption/decryption utilities using Web Crypto API with AES-GCM-256, including key derivation (HKDF), checksum calculation (SHA-256), and comprehensive TDD test coverage with known test vectors.
📋 Implementation Tasks
1. Test Vectors Setup
File: src/lib/crypto/testVectors.ts
- Define known-answer tests (KAT) for AES-GCM-256
- NIST-validated test vectors
- Test data for key derivation (HKDF)
- Test data for SHA-256 checksums
2. Encryption/Decryption Functions (TDD)
File: src/lib/crypto/encryption.test.ts (Write FIRST)
- Test: Encrypt file with AES-GCM-256
- Test: Decrypt file correctly (roundtrip)
- Test: Fail to decrypt with wrong key
- Test: Detect tampering (auth tag verification)
- Test: Use known test vectors (NIST validation)
- Test: Handle edge cases (empty files, large files)
File: src/lib/crypto/encryption.ts (Implement AFTER tests)
-
encryptFile(file: File, key: CryptoKey): Promise<EncryptedFileBlob>- Generate random IV (12 bytes)
- Encrypt with AES-GCM (256-bit)
- Include auth tag (16 bytes)
-
decryptFile(blob: EncryptedFileBlob, key: CryptoKey): Promise<File>- Verify version and algorithm
- Decrypt with AES-GCM
- Automatic auth tag verification
- Throw on tampering/wrong key
3. Key Derivation (TDD)
File: src/lib/crypto/encryption.test.ts (extend)
- Test: Derive file key from secret key using HKDF
- Test: Different keys for different filenames
- Test: Same key for same filename (deterministic)
- Test: Keys are non-extractable
- Test: Keys have correct usage flags
File: src/lib/crypto/encryption.ts (extend)
-
deriveFileKey(secretKey: CryptoKey, filename: string): Promise<CryptoKey>- Hash filename to create salt (SHA-256)
- Derive key using HKDF
- Return non-extractable AES-GCM key
4. Checksum Utilities (TDD)
File: src/lib/crypto/checksum.test.ts (Write FIRST)
- Test: Calculate SHA-256 checksum of file
- Test: Detect file modifications
- Test: Verify checksum correctly
- Test: Handle empty files
- Test: Handle large files (>5MB)
File: src/lib/crypto/checksum.ts (Implement AFTER tests)
-
calculateChecksum(file: File | Blob): Promise<string>- SHA-256 hash
- Hex-encoded output
-
verifyChecksum(file: File | Blob, expectedChecksum: string): Promise<boolean>- Calculate checksum
- Compare with expected
5. TypeScript Types
File: src/lib/crypto/types.ts
-
EncryptedFileBlobinterface -
FileMetadatainterface - Export all types
✅ Acceptance Criteria
- All test files created BEFORE implementation (TDD mandatory)
- ≥15 tests for encryption/decryption
- ≥5 tests for checksum utilities
- All tests passing (100% green)
- Known test vectors validated (NIST)
- Roundtrip encryption/decryption works
- Tampering detection works (auth tag)
- Key derivation deterministic
- Checksum verification works
- Code coverage ≥80% for new code
- PHPStan/ESLint passing
- TypeScript strict mode clean
- REUSE compliant (SPDX headers)
- No security warnings (CodeQL)
🧪 Testing Strategy
// Example test structure
describe('File Encryption', () => {
it('should encrypt file with AES-GCM-256', async () => { ... });
it('should decrypt file correctly (roundtrip)', async () => { ... });
it('should fail to decrypt with wrong key', async () => { ... });
it('should detect tampering (auth tag verification)', async () => { ... });
it('should use known test vectors (NIST validation)', async () => { ... });
});
describe('Key Derivation', () => {
it('should derive file key from secret key using HKDF', async () => { ... });
it('should derive different keys for different filenames', async () => { ... });
it('should derive same key for same filename (deterministic)', async () => { ... });
});
describe('File Checksum', () => {
it('should calculate SHA-256 checksum of file', async () => { ... });
it('should detect file modifications', async () => { ... });
it('should verify checksum correctly', async () => { ... });
});🔗 Dependencies
- Depends on: None (foundation layer)
- Blocks: #TBD (Phase 2 - ShareTarget Integration)
- Part of: Epic [EPIC] Client-Side File Encryption for Zero-Knowledge Architecture #143
📝 Technical Notes
Web Crypto API Usage
// Generate key for testing
const key = await crypto.subtle.generateKey(
{ name: 'AES-GCM', length: 256 },
false, // Non-extractable
['encrypt', 'decrypt']
);
// Encrypt
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv, tagLength: 128 },
key,
data
);
// Decrypt (auth tag verified automatically)
const decrypted = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv, tagLength: 128 },
key,
encrypted
);HKDF Key Derivation
// Hash filename for salt
const salt = await crypto.subtle.digest(
'SHA-256',
new TextEncoder().encode(filename)
);
// Derive key
const derivedBits = await crypto.subtle.deriveBits(
{
name: 'HKDF',
hash: 'SHA-256',
salt,
info: new TextEncoder().encode('file-encryption')
},
secretKey,
256 // 256 bits for AES-256
);🚀 Branch Strategy
# Create feature branch
git checkout -b feat/crypto-utilities-phase1
# Follow TDD cycle
# 1. Write tests FIRST
# 2. Run tests (they MUST fail initially)
# 3. Implement code (make tests pass)
# 4. Refactor (keep tests green)
# 5. Commit with signed commits
# Example commits
git commit -S -m "test: add encryption test vectors (Phase 1.1)"
git commit -S -m "test: add encryption/decryption tests (Phase 1.2)"
git commit -S -m "feat: implement AES-GCM file encryption (Phase 1.3)"
git commit -S -m "test: add key derivation tests (Phase 1.4)"
git commit -S -m "feat: implement HKDF key derivation (Phase 1.5)"📏 PR Linking Instructions
When creating the PR for this sub-issue, use this in your PR description:
Fixes #TBD
Part of: #143- Do NOT use
Fixes #143- this is not the last sub-issue - This is Phase 1 of 5 - foundation layer only
- PR size: ~400-500 LOC (tests + implementation)
Type: Sub-Issue (Phase 1/5)
Priority: High
Estimated Effort: 2 days
Sprint: Encryption Foundation
Metadata
Metadata
Assignees
Labels
Type
Projects
Status