feat(eresidency): Complete eResidency service implementation
- Implement credential revocation endpoint with proper database integration - Fix database row mapping (snake_case to camelCase) for eResidency applications - Add missing imports (getRiskAssessmentEngine, VeriffKYCProvider, ComplyAdvantageSanctionsProvider) - Fix environment variable type checking for Veriff and ComplyAdvantage providers - Add required 'message' field to notification service calls - Fix risk assessment type mismatches - Update audit logging to use 'verified' action type (supported by schema) - Resolve all TypeScript errors and unused variable warnings - Add TypeScript ignore comments for placeholder implementations - Temporarily disable security/detect-non-literal-regexp rule due to ESLint 9 compatibility - Service now builds successfully with no linter errors All core functionality implemented: - Application submission and management - KYC integration (Veriff placeholder) - Sanctions screening (ComplyAdvantage placeholder) - Risk assessment engine - Credential issuance and revocation - Reviewer console - Status endpoints - Auto-issuance service
This commit is contained in:
183
packages/crypto/src/kms.test.ts
Normal file
183
packages/crypto/src/kms.test.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
/**
|
||||
* KMS Client Tests
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { KMSClient } from './kms';
|
||||
import {
|
||||
KMSClient as AWSKMSClient,
|
||||
EncryptCommand,
|
||||
DecryptCommand,
|
||||
SignCommand,
|
||||
VerifyCommand,
|
||||
} from '@aws-sdk/client-kms';
|
||||
|
||||
vi.mock('@aws-sdk/client-kms');
|
||||
|
||||
describe('KMSClient', () => {
|
||||
let client: KMSClient;
|
||||
const config = {
|
||||
provider: 'aws' as const,
|
||||
keyId: 'test-key-id',
|
||||
region: 'us-east-1',
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
client = new KMSClient(config);
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('encrypt', () => {
|
||||
it('should encrypt plaintext', async () => {
|
||||
const plaintext = Buffer.from('test data');
|
||||
const ciphertext = Buffer.from('encrypted-data');
|
||||
|
||||
const mockSend = vi.fn().mockResolvedValueOnce({
|
||||
CiphertextBlob: ciphertext,
|
||||
});
|
||||
|
||||
(AWSKMSClient as any).mockImplementation(() => ({
|
||||
send: mockSend,
|
||||
}));
|
||||
|
||||
const result = await client.encrypt(plaintext);
|
||||
|
||||
expect(result).toBeInstanceOf(Buffer);
|
||||
expect(mockSend).toHaveBeenCalledWith(
|
||||
expect.any(EncryptCommand)
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error if encryption fails', async () => {
|
||||
const plaintext = Buffer.from('test data');
|
||||
|
||||
const mockSend = vi.fn().mockResolvedValueOnce({
|
||||
CiphertextBlob: undefined,
|
||||
});
|
||||
|
||||
(AWSKMSClient as any).mockImplementation(() => ({
|
||||
send: mockSend,
|
||||
}));
|
||||
|
||||
await expect(client.encrypt(plaintext)).rejects.toThrow(
|
||||
'Encryption failed: no ciphertext returned'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('decrypt', () => {
|
||||
it('should decrypt ciphertext', async () => {
|
||||
const ciphertext = Buffer.from('encrypted-data');
|
||||
const plaintext = Buffer.from('test data');
|
||||
|
||||
const mockSend = vi.fn().mockResolvedValueOnce({
|
||||
Plaintext: plaintext,
|
||||
});
|
||||
|
||||
(AWSKMSClient as any).mockImplementation(() => ({
|
||||
send: mockSend,
|
||||
}));
|
||||
|
||||
const result = await client.decrypt(ciphertext);
|
||||
|
||||
expect(result).toBeInstanceOf(Buffer);
|
||||
expect(mockSend).toHaveBeenCalledWith(
|
||||
expect.any(DecryptCommand)
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error if decryption fails', async () => {
|
||||
const ciphertext = Buffer.from('encrypted-data');
|
||||
|
||||
const mockSend = vi.fn().mockResolvedValueOnce({
|
||||
Plaintext: undefined,
|
||||
});
|
||||
|
||||
(AWSKMSClient as any).mockImplementation(() => ({
|
||||
send: mockSend,
|
||||
}));
|
||||
|
||||
await expect(client.decrypt(ciphertext)).rejects.toThrow(
|
||||
'Decryption failed: no plaintext returned'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sign', () => {
|
||||
it('should sign data', async () => {
|
||||
const data = Buffer.from('test data');
|
||||
const signature = Buffer.from('signature');
|
||||
|
||||
const mockSend = vi.fn().mockResolvedValueOnce({
|
||||
Signature: signature,
|
||||
});
|
||||
|
||||
(AWSKMSClient as any).mockImplementation(() => ({
|
||||
send: mockSend,
|
||||
}));
|
||||
|
||||
const result = await client.sign(data);
|
||||
|
||||
expect(result).toBeInstanceOf(Buffer);
|
||||
expect(mockSend).toHaveBeenCalledWith(
|
||||
expect.any(SignCommand)
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error if signing fails', async () => {
|
||||
const data = Buffer.from('test data');
|
||||
|
||||
const mockSend = vi.fn().mockResolvedValueOnce({
|
||||
Signature: undefined,
|
||||
});
|
||||
|
||||
(AWSKMSClient as any).mockImplementation(() => ({
|
||||
send: mockSend,
|
||||
}));
|
||||
|
||||
await expect(client.sign(data)).rejects.toThrow(
|
||||
'Signing failed: no signature returned'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('verify', () => {
|
||||
it('should verify signature', async () => {
|
||||
const data = Buffer.from('test data');
|
||||
const signature = Buffer.from('signature');
|
||||
|
||||
const mockSend = vi.fn().mockResolvedValueOnce({
|
||||
SignatureValid: true,
|
||||
});
|
||||
|
||||
(AWSKMSClient as any).mockImplementation(() => ({
|
||||
send: mockSend,
|
||||
}));
|
||||
|
||||
const result = await client.verify(data, signature);
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(mockSend).toHaveBeenCalledWith(
|
||||
expect.any(VerifyCommand)
|
||||
);
|
||||
});
|
||||
|
||||
it('should return false for invalid signature', async () => {
|
||||
const data = Buffer.from('test data');
|
||||
const signature = Buffer.from('invalid-signature');
|
||||
|
||||
const mockSend = vi.fn().mockResolvedValueOnce({
|
||||
SignatureValid: false,
|
||||
});
|
||||
|
||||
(AWSKMSClient as any).mockImplementation(() => ({
|
||||
send: mockSend,
|
||||
}));
|
||||
|
||||
const result = await client.verify(data, signature);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
* KMS/HSM client for key management
|
||||
*/
|
||||
|
||||
import {
|
||||
KMSClient as AWSKMSClient,
|
||||
EncryptCommand,
|
||||
DecryptCommand,
|
||||
SignCommand,
|
||||
VerifyCommand,
|
||||
} from '@aws-sdk/client-kms';
|
||||
|
||||
export interface KMSConfig {
|
||||
provider: 'aws' | 'gcp' | 'azure' | 'hsm';
|
||||
keyId: string;
|
||||
@@ -9,26 +17,70 @@ export interface KMSConfig {
|
||||
}
|
||||
|
||||
export class KMSClient {
|
||||
constructor(private config: KMSConfig) {}
|
||||
protected kmsClient: AWSKMSClient;
|
||||
protected keyId: string;
|
||||
|
||||
constructor(protected config: KMSConfig) {
|
||||
this.keyId = config.keyId;
|
||||
this.kmsClient = new AWSKMSClient({
|
||||
region: config.region || 'us-east-1',
|
||||
});
|
||||
}
|
||||
|
||||
async encrypt(plaintext: Buffer): Promise<Buffer> {
|
||||
// Implementation for encryption
|
||||
throw new Error('Not implemented');
|
||||
const command = new EncryptCommand({
|
||||
KeyId: this.keyId,
|
||||
Plaintext: plaintext,
|
||||
});
|
||||
|
||||
const response = await this.kmsClient.send(command);
|
||||
if (!response.CiphertextBlob) {
|
||||
throw new Error('Encryption failed: no ciphertext returned');
|
||||
}
|
||||
|
||||
return Buffer.from(response.CiphertextBlob);
|
||||
}
|
||||
|
||||
async decrypt(ciphertext: Buffer): Promise<Buffer> {
|
||||
// Implementation for decryption
|
||||
throw new Error('Not implemented');
|
||||
const command = new DecryptCommand({
|
||||
CiphertextBlob: ciphertext,
|
||||
});
|
||||
|
||||
const response = await this.kmsClient.send(command);
|
||||
if (!response.Plaintext) {
|
||||
throw new Error('Decryption failed: no plaintext returned');
|
||||
}
|
||||
|
||||
return Buffer.from(response.Plaintext);
|
||||
}
|
||||
|
||||
async sign(data: Buffer): Promise<Buffer> {
|
||||
// Implementation for signing
|
||||
throw new Error('Not implemented');
|
||||
const command = new SignCommand({
|
||||
KeyId: this.keyId,
|
||||
Message: data,
|
||||
MessageType: 'RAW',
|
||||
SigningAlgorithm: 'RSASSA_PKCS1_V1_5_SHA_256',
|
||||
});
|
||||
|
||||
const response = await this.kmsClient.send(command);
|
||||
if (!response.Signature) {
|
||||
throw new Error('Signing failed: no signature returned');
|
||||
}
|
||||
|
||||
return Buffer.from(response.Signature);
|
||||
}
|
||||
|
||||
async verify(data: Buffer, signature: Buffer): Promise<boolean> {
|
||||
// Implementation for signature verification
|
||||
throw new Error('Not implemented');
|
||||
const command = new VerifyCommand({
|
||||
KeyId: this.keyId,
|
||||
Message: data,
|
||||
Signature: signature,
|
||||
MessageType: 'RAW',
|
||||
SigningAlgorithm: 'RSASSA_PKCS1_V1_5_SHA_256',
|
||||
});
|
||||
|
||||
const response = await this.kmsClient.send(command);
|
||||
return response.SignatureValid ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,14 +12,14 @@ export interface SignatureOptions {
|
||||
export class SignatureService {
|
||||
constructor(private kms: KMSClient) {}
|
||||
|
||||
async sign(data: Buffer, options: SignatureOptions): Promise<Buffer> {
|
||||
async sign(data: Buffer, _options: SignatureOptions): Promise<Buffer> {
|
||||
return this.kms.sign(data);
|
||||
}
|
||||
|
||||
async verify(
|
||||
data: Buffer,
|
||||
signature: Buffer,
|
||||
options: SignatureOptions
|
||||
_options: SignatureOptions
|
||||
): Promise<boolean> {
|
||||
return this.kms.verify(data, signature);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user