import { afterEach, describe, expect, it, vi } from 'vitest' import { formatBytes, parseHex } from '../src/lib/bytes' import { base64UrlToHex, formatPem, generateRsaKeyPair } from '../src/lib/rsa' describe('RSA key generation', () => { afterEach(() => { vi.unstubAllGlobals() }) it('formats DER and JWK integer values', () => { expect(formatPem(Uint8Array.from([0, 1, 2, 3]), 'PUBLIC KEY')).toBe('-----BEGIN PUBLIC KEY-----\nAAECAw==\n-----END PUBLIC KEY-----\n') expect(base64UrlToHex('AQAB')).toBe('010001') }) it('generates an exportable 2048-bit key pair using a cryptographic provider', async () => { const result = await generateRsaKeyPair(2048) expect(result.publicKeyPem).toMatch(/^-----BEGIN PUBLIC KEY-----/) expect(result.privateKeyPem).toMatch(/^-----BEGIN PRIVATE KEY-----/) expect(result.modulusHex).toHaveLength(512) expect(result.exponentHex).toBe('010001') expect(formatBytes(parseHex(result.exponentHex), { format: 'byte-array' })).toBe('0x01, 0x00, 0x01') expect(['Web Crypto', 'node-forge']).toContain(result.source) }, 20_000) it('uses the node-forge fallback with a cryptographic random source when SubtleCrypto is unavailable', async () => { const getRandomValues = globalThis.crypto.getRandomValues.bind(globalThis.crypto) vi.stubGlobal('crypto', { getRandomValues }) const result = await generateRsaKeyPair(2048) expect(result.source).toBe('node-forge') expect(result.publicKeyPem).toMatch(/^-----BEGIN PUBLIC KEY-----/) expect(result.privateKeyPem).toMatch(/^-----BEGIN PRIVATE KEY-----/) expect(result.modulusHex).toHaveLength(512) expect(result.exponentHex).toBe('010001') }, 30_000) })