import { ConfigService } from "@nestjs/config"; import { Test, TestingModule } from "@nestjs/testing"; jest.mock("@noble/post-quantum/ml-kem.js", () => ({ ml_kem768: { keygen: jest.fn(() => ({ publicKey: new Uint8Array(1184), secretKey: new Uint8Array(2400), })), encapsulate: jest.fn((_pk: Uint8Array) => ({ cipherText: new Uint8Array(1088), sharedSecret: new Uint8Array(32), })), decapsulate: jest.fn( (_ct: Uint8Array, _sk: Uint8Array) => new Uint8Array(32), ), }, })); jest.mock("jose", () => ({ generateSecret: jest.fn().mockResolvedValue(new Uint8Array(32)), CompactEncrypt: jest.fn().mockImplementation(() => ({ setProtectedHeader: jest.fn().mockReturnThis(), encrypt: jest.fn().mockResolvedValue("mocked.jwe.token.parts.here"), })), compactDecrypt: jest.fn().mockImplementation((jwe) => { if (jwe === "invalid.jwe.content") { throw new Error("Invalid JWE"); } return Promise.resolve({ plaintext: new TextEncoder().encode("This is a secret message 🤫"), }); }), SignJWT: jest.fn().mockImplementation(() => ({ setProtectedHeader: jest.fn().mockReturnThis(), setIssuedAt: jest.fn().mockReturnThis(), setExpirationTime: jest.fn().mockReturnThis(), sign: jest.fn().mockResolvedValue("mocked.jwt.token"), })), jwtVerify: jest.fn().mockImplementation((token) => { if (token === "invalid.token.here") { throw new Error("Invalid token"); } return Promise.resolve({ payload: { sub: "1234567890", name: "John Doe", admin: true }, }); }), CompactSign: jest.fn().mockImplementation(() => ({ setProtectedHeader: jest.fn().mockReturnThis(), sign: jest.fn().mockResolvedValue("mocked.jws.token"), })), compactVerify: jest.fn().mockImplementation((jws) => { if (jws.includes("tampered") || jws.split(".").length !== 3) { throw new Error("Tampered or invalid content"); } const payload = jws === "mocked.jws.token" ? "Important document content" : "Original content"; return Promise.resolve({ payload: new TextEncoder().encode(payload), }); }), })); import { CryptoService } from "./crypto.service"; import { EncryptionService } from "./services/encryption.service"; import { HashingService } from "./services/hashing.service"; import { JwtService } from "./services/jwt.service"; import { PostQuantumService } from "./services/post-quantum.service"; describe("CryptoService", () => { let service: CryptoService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ CryptoService, HashingService, JwtService, EncryptionService, PostQuantumService, { provide: ConfigService, useValue: { get: jest.fn().mockReturnValue("test-secret"), }, }, ], }).compile(); service = module.get(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) const tamperedJws = "this.is.tampered"; 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); }); }); });