/** * Crypto tests */ const fs = require('fs').promises; const path = require('path'); const { assert } = require('chai'); const spec = require('./spec'); const { crypto } = require('./../'); const emptyBodyChain1 = ` -----BEGIN TEST----- a -----END TEST----- -----BEGIN TEST----- b -----END TEST----- -----BEGIN TEST----- -----END TEST----- -----BEGIN TEST----- c -----END TEST----- `; const emptyBodyChain2 = ` -----BEGIN TEST----- -----END TEST----- -----BEGIN TEST----- -----END TEST----- -----BEGIN TEST----- a -----END TEST----- -----BEGIN TEST----- b -----END TEST----- -----BEGIN TEST----- c -----END TEST----- `; describe('crypto', () => { const testCsrDomain = 'example.com'; const testSanCsrDomains = ['example.com', 'test.example.com', 'abc.example.com']; const testKeyPath = path.join(__dirname, 'fixtures', 'private.key'); const testCertPath = path.join(__dirname, 'fixtures', 'certificate.crt'); const testSanCertPath = path.join(__dirname, 'fixtures', 'san-certificate.crt'); /** * Key types */ Object.entries({ rsa: { createKeyFns: { s1024: () => crypto.createPrivateRsaKey(1024), s2048: () => crypto.createPrivateRsaKey(), s4096: () => crypto.createPrivateRsaKey(4096) }, jwkSpecFn: spec.jwk.rsa }, ecdsa: { createKeyFns: { p256: () => crypto.createPrivateEcdsaKey(), p384: () => crypto.createPrivateEcdsaKey('P-384'), p521: () => crypto.createPrivateEcdsaKey('P-521') }, jwkSpecFn: spec.jwk.ecdsa } }).forEach(([name, { createKeyFns, jwkSpecFn }]) => { describe(name, () => { const testPrivateKeys = {}; const testPublicKeys = {}; /** * Iterate through all generator variations */ Object.entries(createKeyFns).forEach(([n, createFn]) => { let testCsr; let testSanCsr; let testNonCnCsr; let testNonAsciiCsr; /** * Keys and JWK */ it(`${n}/should generate private key`, async () => { testPrivateKeys[n] = await createFn(); assert.isTrue(Buffer.isBuffer(testPrivateKeys[n])); }); it(`${n}/should get public key`, () => { testPublicKeys[n] = crypto.getPublicKey(testPrivateKeys[n]); assert.isTrue(Buffer.isBuffer(testPublicKeys[n])); }); it(`${n}/should get jwk from private key`, () => { const jwk = crypto.getJwk(testPrivateKeys[n]); jwkSpecFn(jwk); }); it(`${n}/should get jwk from public key`, () => { const jwk = crypto.getJwk(testPublicKeys[n]); jwkSpecFn(jwk); }); /** * Certificate Signing Request */ it(`${n}/should generate a csr`, async () => { const [key, csr] = await crypto.createCsr({ commonName: testCsrDomain }, testPrivateKeys[n]); assert.isTrue(Buffer.isBuffer(key)); assert.isTrue(Buffer.isBuffer(csr)); testCsr = csr; }); it(`${n}/should generate a san csr`, async () => { const [key, csr] = await crypto.createCsr({ commonName: testSanCsrDomains[0], altNames: testSanCsrDomains.slice(1, testSanCsrDomains.length) }, testPrivateKeys[n]); assert.isTrue(Buffer.isBuffer(key)); assert.isTrue(Buffer.isBuffer(csr)); testSanCsr = csr; }); it(`${n}/should generate a csr without common name`, async () => { const [key, csr] = await crypto.createCsr({ altNames: testSanCsrDomains }, testPrivateKeys[n]); assert.isTrue(Buffer.isBuffer(key)); assert.isTrue(Buffer.isBuffer(csr)); testNonCnCsr = csr; }); it(`${n}/should generate a non-ascii csr`, async () => { const [key, csr] = await crypto.createCsr({ commonName: testCsrDomain, organization: '大安區', organizationUnit: '中文部門' }, testPrivateKeys[n]); assert.isTrue(Buffer.isBuffer(key)); assert.isTrue(Buffer.isBuffer(csr)); testNonAsciiCsr = csr; }); it(`${n}/should throw with invalid key`, async () => { await assert.isRejected(crypto.createCsr({ commonName: testCsrDomain }, testPublicKeys[n])); }); /** * Domain and info resolver */ it(`${n}/should resolve domains from csr`, () => { const result = crypto.readCsrDomains(testCsr); spec.crypto.csrDomains(result); assert.strictEqual(result.commonName, testCsrDomain); assert.deepStrictEqual(result.altNames, [testCsrDomain]); }); it(`${n}/should resolve domains from san csr`, () => { const result = crypto.readCsrDomains(testSanCsr); spec.crypto.csrDomains(result); assert.strictEqual(result.commonName, testSanCsrDomains[0]); assert.deepStrictEqual(result.altNames, testSanCsrDomains); }); it(`${n}/should resolve domains from csr without common name`, () => { const result = crypto.readCsrDomains(testNonCnCsr); spec.crypto.csrDomains(result); assert.isNull(result.commonName); assert.deepStrictEqual(result.altNames, testSanCsrDomains); }); it(`${n}/should resolve domains from non-ascii csr`, () => { const result = crypto.readCsrDomains(testNonAsciiCsr); spec.crypto.csrDomains(result); assert.strictEqual(result.commonName, testCsrDomain); assert.deepStrictEqual(result.altNames, [testCsrDomain]); }); }); }); }); /** * Common functionality */ describe('common', () => { let testPemKey; let testCert; let testSanCert; it('should read private key fixture', async () => { testPemKey = await fs.readFile(testKeyPath); assert.isTrue(Buffer.isBuffer(testPemKey)); }); it('should read certificate fixture', async () => { testCert = await fs.readFile(testCertPath); assert.isTrue(Buffer.isBuffer(testCert)); }); it('should read san certificate fixture', async () => { testSanCert = await fs.readFile(testSanCertPath); assert.isTrue(Buffer.isBuffer(testSanCert)); }); /** * CSR with auto-generated key */ it('should generate a csr with auto-generated key', async () => { const [key, csr] = await crypto.createCsr({ commonName: testCsrDomain }); assert.isTrue(Buffer.isBuffer(key)); assert.isTrue(Buffer.isBuffer(csr)); }); /** * Certificate */ it('should read certificate info', () => { const info = crypto.readCertificateInfo(testCert); spec.crypto.certificateInfo(info); assert.strictEqual(info.domains.commonName, testCsrDomain); assert.strictEqual(info.domains.altNames.length, 0); }); it('should read certificate info with san', () => { const info = crypto.readCertificateInfo(testSanCert); spec.crypto.certificateInfo(info); assert.strictEqual(info.domains.commonName, testSanCsrDomains[0]); assert.deepEqual(info.domains.altNames, testSanCsrDomains.slice(1, testSanCsrDomains.length)); }); /** * PEM utils */ it('should get pem body as b64u', () => { [testPemKey, testCert, testSanCert].forEach((pem) => { const body = crypto.getPemBodyAsB64u(pem); assert.isString(body); assert.notInclude(body, '\r'); assert.notInclude(body, '\n'); assert.notInclude(body, '\r\n'); }); }); it('should split pem chain', () => { [testPemKey, testCert, testSanCert].forEach((pem) => { const chain = crypto.splitPemChain(pem); assert.isArray(chain); assert.isNotEmpty(chain); chain.forEach((c) => assert.isString(c)); }); }); it('should split pem chain with empty bodies', () => { const c1 = crypto.splitPemChain(emptyBodyChain1); const c2 = crypto.splitPemChain(emptyBodyChain2); assert.strictEqual(c1.length, 3); assert.strictEqual(c2.length, 3); }); }); });