feat: implement CryptoModule with comprehensive cryptographic utilities and testing
Added CryptoModule providing services for Argon2 hashing, JWT handling, JWE encryption/decryption, JWS signing/verification, and post-quantum cryptography (ML-KEM). Includes extensive unit tests for all features.
This commit is contained in:
117
backend/src/crypto/crypto.service.spec.ts
Normal file
117
backend/src/crypto/crypto.service.spec.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import { CryptoService } from "./crypto.service";
|
||||
|
||||
describe("CryptoService", () => {
|
||||
let service: CryptoService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
CryptoService,
|
||||
{
|
||||
provide: ConfigService,
|
||||
useValue: {
|
||||
get: jest.fn().mockReturnValue("test-secret"),
|
||||
},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<CryptoService>(CryptoService);
|
||||
});
|
||||
|
||||
it("should be defined", () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
|
||||
describe("Argon2 Password Hashing", () => {
|
||||
it("should hash and verify a password", async () => {
|
||||
const password = "mySecurePassword123!";
|
||||
const hash = await service.hashPassword(password);
|
||||
expect(hash).toBeDefined();
|
||||
expect(hash).not.toBe(password);
|
||||
|
||||
const isValid = await service.verifyPassword(password, hash);
|
||||
expect(isValid).toBe(true);
|
||||
|
||||
const isInvalid = await service.verifyPassword("wrongPassword", hash);
|
||||
expect(isInvalid).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("JWT jose", () => {
|
||||
it("should generate and verify a JWT", async () => {
|
||||
const payload = { sub: "1234567890", name: "John Doe", admin: true };
|
||||
const token = await service.generateJwt(payload);
|
||||
expect(token).toBeDefined();
|
||||
|
||||
const verifiedPayload = await service.verifyJwt(token);
|
||||
expect(verifiedPayload.sub).toBe(payload.sub);
|
||||
expect(verifiedPayload.name).toBe(payload.name);
|
||||
expect(verifiedPayload.admin).toBe(payload.admin);
|
||||
});
|
||||
|
||||
it("should throw for invalid token", async () => {
|
||||
await expect(service.verifyJwt("invalid.token.here")).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Encryption/Decryption (JWE)", () => {
|
||||
it("should encrypt and decrypt content", async () => {
|
||||
const content = "This is a secret message 🤫";
|
||||
const jwe = await service.encryptContent(content);
|
||||
expect(jwe).toBeDefined();
|
||||
expect(typeof jwe).toBe("string");
|
||||
expect(jwe.split(".").length).toBe(5); // JWE compact serialization has 5 parts
|
||||
|
||||
const decrypted = await service.decryptContent(jwe);
|
||||
expect(decrypted).toBe(content);
|
||||
});
|
||||
|
||||
it("should fail to decrypt invalid content", async () => {
|
||||
await expect(
|
||||
service.decryptContent("invalid.jwe.content"),
|
||||
).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Signature (JWS)", () => {
|
||||
it("should sign and verify content signature", async () => {
|
||||
const content = "Important document content";
|
||||
const jws = await service.signContent(content);
|
||||
expect(jws).toBeDefined();
|
||||
expect(typeof jws).toBe("string");
|
||||
expect(jws.split(".").length).toBe(3); // JWS compact serialization has 3 parts
|
||||
|
||||
const verifiedContent = await service.verifyContentSignature(jws);
|
||||
expect(verifiedContent).toBe(content);
|
||||
});
|
||||
|
||||
it("should fail to verify tampered content", async () => {
|
||||
const content = "Original content";
|
||||
const jws = await service.signContent(content);
|
||||
const parts = jws.split(".");
|
||||
// Tamper with the payload (middle part)
|
||||
parts[1] = Buffer.from("Tampered content").toString("base64url");
|
||||
const tamperedJws = parts.join(".");
|
||||
|
||||
await expect(service.verifyContentSignature(tamperedJws)).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Post-Quantum @noble/post-quantum", () => {
|
||||
it("should generate keypair, encapsulate and decapsulate", () => {
|
||||
const { publicKey, secretKey } = service.generatePostQuantumKeyPair();
|
||||
expect(publicKey).toBeDefined();
|
||||
expect(secretKey).toBeDefined();
|
||||
|
||||
const { cipherText, sharedSecret } = service.encapsulate(publicKey);
|
||||
expect(cipherText).toBeDefined();
|
||||
expect(sharedSecret).toBeDefined();
|
||||
|
||||
const decapsulatedSecret = service.decapsulate(cipherText, secretKey);
|
||||
expect(decapsulatedSecret).toEqual(sharedSecret);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user