From 972eb54e9abaa8ca1caaef67811dd778ea4b002d Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Wed, 8 Apr 2026 00:52:42 +0200 Subject: [PATCH] refactor(tests): harmonize crypto test data and improve test quality MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit - Reorganize OCPP20CertificateTestData: move CA/leaf certificates into Real X.509 section, simplify JSDoc to consistent one-line format - Add round-trip tests (derLength → readDerLength) replacing duplicate raw byte assertions - Add semantic test for extractDerIssuer (DER hash vs string DN hash) - Replace eslint-disable || workaround with assert.match regex - Update Asn1DerUtils @description to cover parsing functions --- .../ocpp/2.0/Asn1DerUtils.test.ts | 36 +++++++----- .../ocpp/2.0/OCPP20CertificateManager.test.ts | 6 +- .../ocpp/2.0/OCPP20CertificateTestData.ts | 56 +++++++++---------- 3 files changed, 50 insertions(+), 48 deletions(-) diff --git a/tests/charging-station/ocpp/2.0/Asn1DerUtils.test.ts b/tests/charging-station/ocpp/2.0/Asn1DerUtils.test.ts index cdb6ab7a..25231379 100644 --- a/tests/charging-station/ocpp/2.0/Asn1DerUtils.test.ts +++ b/tests/charging-station/ocpp/2.0/Asn1DerUtils.test.ts @@ -1,10 +1,10 @@ /** * @file Tests for Asn1DerUtils - * @description Unit tests for ASN.1 DER encoding primitives and PKCS#10 CSR generation + * @description Unit tests for ASN.1 DER encoding, parsing, and PKCS#10 CSR generation */ import assert from 'node:assert/strict' -import { X509Certificate } from 'node:crypto' +import { hash, X509Certificate } from 'node:crypto' import { afterEach, describe, it } from 'node:test' import { @@ -118,25 +118,25 @@ await describe('ASN.1 DER encoding utilities', async () => { }) await describe('readDerLength', async () => { - await it('should parse short form length', () => { - const buf = Buffer.from([42]) - const result = readDerLength(buf, 0) + await it('should round-trip with derLength for short form', () => { + const encoded = derLength(42) + const result = readDerLength(encoded, 0) assert.strictEqual(result.length, 42) - assert.strictEqual(result.end, 1) + assert.strictEqual(result.end, encoded.length) }) - await it('should parse single-byte long form (0x81)', () => { - const buf = Buffer.from([0x81, 200]) - const result = readDerLength(buf, 0) + await it('should round-trip with derLength for single-byte long form', () => { + const encoded = derLength(200) + const result = readDerLength(encoded, 0) assert.strictEqual(result.length, 200) - assert.strictEqual(result.end, 2) + assert.strictEqual(result.end, encoded.length) }) - await it('should parse two-byte long form (0x82)', () => { - const buf = Buffer.from([0x82, 0x01, 0x2c]) - const result = readDerLength(buf, 0) + await it('should round-trip with derLength for two-byte long form', () => { + const encoded = derLength(300) + const result = readDerLength(encoded, 0) assert.strictEqual(result.length, 300) - assert.strictEqual(result.end, 3) + assert.strictEqual(result.end, encoded.length) }) await it('should throw RangeError for empty buffer', () => { @@ -177,6 +177,14 @@ await describe('ASN.1 DER encoding utilities', async () => { assert.strictEqual(issuerDer[0], 0x30) }) + await it('should extract issuer that differs from hashing the string DN', () => { + const x509 = new X509Certificate(VALID_X509_PEM_CERTIFICATE) + const issuerDer = extractDerIssuer(x509.raw) + const derHash = hash('sha256', issuerDer, 'hex') + const stringHash = hash('sha256', x509.issuer, 'hex') + assert.notStrictEqual(derHash, stringHash) + }) + await it('should produce consistent output for the same certificate', () => { const x509 = new X509Certificate(VALID_X509_PEM_CERTIFICATE) const first = extractDerIssuer(x509.raw) diff --git a/tests/charging-station/ocpp/2.0/OCPP20CertificateManager.test.ts b/tests/charging-station/ocpp/2.0/OCPP20CertificateManager.test.ts index c29251c8..b6a0aad9 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20CertificateManager.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20CertificateManager.test.ts @@ -490,11 +490,7 @@ await describe('I02-I04 - ISO15118 Certificate Management', async () => { const result = manager.validateCertificateX509(brokenChain) assert.strictEqual(result.valid, false) - assert.ok( - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - result.reason?.includes('issuer mismatch') || - result.reason?.includes('signature verification failed') - ) + assert.match(result.reason ?? '', /issuer mismatch|signature verification failed/) }) await it('should return invalid when chain has expired intermediate', () => { diff --git a/tests/charging-station/ocpp/2.0/OCPP20CertificateTestData.ts b/tests/charging-station/ocpp/2.0/OCPP20CertificateTestData.ts index 2164717a..a833226f 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20CertificateTestData.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20CertificateTestData.ts @@ -97,39 +97,12 @@ SIb3DQEBCwUAA0EAexpired== */ export const EMPTY_PEM_CERTIFICATE = '' -/** - * Real EC P-256 CA certificate (CN=TestRootCA, valid 2026-2036). - * Used as issuer for VALID_X509_LEAF_CERTIFICATE. - */ -export const VALID_X509_CA_CERTIFICATE = `-----BEGIN CERTIFICATE----- -MIIBGDCBwAIJAMY5KBDzNkHGMAoGCCqGSM49BAMCMBUxEzARBgNVBAMMClRlc3RS -b290Q0EwHhcNMjYwNDA3MjIxMjU1WhcNMzYwNDA0MjIxMjU1WjAVMRMwEQYDVQQD -DApUZXN0Um9vdENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhOoqHSLrnwVr -0Nu78E9rGDQmhGK1qvr2W7ye7yXMHgfFGYMuVr7ejCj6TXk2YPSS8ADRwCRj8R6S -JFomGI2soDAKBggqhkjOPQQDAgNHADBEAiBDCuKOo0v3y/ZTGSf20GQyTtmfibV5 -5t1yXJkVTudOMgIgWqQXoyjI/k2s+T9U38X4S9yTeMkjeNb3FEYVq5kaA5w= ------END CERTIFICATE-----` - -/** - * Real EC P-256 leaf certificate (CN=TestLeaf, valid 2026-2036). - * Signed by VALID_X509_CA_CERTIFICATE. - */ -export const VALID_X509_LEAF_CERTIFICATE = `-----BEGIN CERTIFICATE----- -MIIBGDCBvgIJAJn8LXzPXkayMAoGCCqGSM49BAMCMBUxEzARBgNVBAMMClRlc3RS -b290Q0EwHhcNMjYwNDA3MjIxMjU1WhcNMzYwNDA0MjIxMjU1WjATMREwDwYDVQQD -DAhUZXN0TGVhZjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDMWqNhqXlWIksJi -fYrEQLdPlWG4zAfvc/1q7npD5d+OJu4uwbseA2uXf8/wHgQYrpD3jkXT4b/amhyv -W1dCq+0wCgYIKoZIzj0EAwIDSQAwRgIhAPnXKKIs+8sE+W+3AH9zE3Z51I1ndCks -wD0Gud+kCORgAiEArgyP/lVR0Vh9NWe8iTNVXOyc4s8Jn0J+CF9UsUIGuFA= ------END CERTIFICATE-----` - // ============================================================================ // Real X.509 Certificates (parseable by node:crypto X509Certificate) // ============================================================================ /** - * Valid self-signed X.509 certificate (EC P-256, CN=TestCA, valid 2026-2036). - * Parseable by node:crypto X509Certificate for X.509 structural validation tests. + * Self-signed X.509 certificate (EC P-256, CN=TestCA, valid 2026-2036). */ export const VALID_X509_PEM_CERTIFICATE = `-----BEGIN CERTIFICATE----- MIICBjCCAawCCQDuW/VTwcEHDTAKBggqhkjOPQQDAjARMQ8wDQYDVQQDDAZUZXN0 @@ -147,7 +120,6 @@ IHCV6XLaqZACIHEFQs2oVgHqMTxx22E7ZffFpA5hNP021SFwJ9ujSYQj /** * Expired self-signed X.509 certificate (EC P-256, CN=ExpiredTestCA, valid 2020-2021). - * Parseable by node:crypto X509Certificate; triggers expiration check. */ export const EXPIRED_X509_PEM_CERTIFICATE = `-----BEGIN CERTIFICATE----- MIIBLjCB1qADAgECAhRHkkXuRncB5xOMPnTt0pqA/uWOsTAKBggqhkjOPQQDAjAY @@ -158,3 +130,29 @@ nR2Dm+OL4pHpa31dWSPRPeAZYUlsz8zER/rARNUwCgYIKoZIzj0EAwIDRwAwRAIg XvHyZ5eCRgOTBCMBDXvxxZXGaWFsrhq066F0MKd6D1ICICrQl8LxyYh72Bc0gWBG 2LQtv+sPK1CmsMqp8G8DiFqY -----END CERTIFICATE-----` + +/** + * CA certificate (EC P-256, CN=TestRootCA, valid 2026-2036). + * Issuer for VALID_X509_LEAF_CERTIFICATE. + */ +export const VALID_X509_CA_CERTIFICATE = `-----BEGIN CERTIFICATE----- +MIIBGDCBwAIJAMY5KBDzNkHGMAoGCCqGSM49BAMCMBUxEzARBgNVBAMMClRlc3RS +b290Q0EwHhcNMjYwNDA3MjIxMjU1WhcNMzYwNDA0MjIxMjU1WjAVMRMwEQYDVQQD +DApUZXN0Um9vdENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhOoqHSLrnwVr +0Nu78E9rGDQmhGK1qvr2W7ye7yXMHgfFGYMuVr7ejCj6TXk2YPSS8ADRwCRj8R6S +JFomGI2soDAKBggqhkjOPQQDAgNHADBEAiBDCuKOo0v3y/ZTGSf20GQyTtmfibV5 +5t1yXJkVTudOMgIgWqQXoyjI/k2s+T9U38X4S9yTeMkjeNb3FEYVq5kaA5w= +-----END CERTIFICATE-----` + +/** + * Leaf certificate (EC P-256, CN=TestLeaf, valid 2026-2036). + * Signed by VALID_X509_CA_CERTIFICATE. + */ +export const VALID_X509_LEAF_CERTIFICATE = `-----BEGIN CERTIFICATE----- +MIIBGDCBvgIJAJn8LXzPXkayMAoGCCqGSM49BAMCMBUxEzARBgNVBAMMClRlc3RS +b290Q0EwHhcNMjYwNDA3MjIxMjU1WhcNMzYwNDA0MjIxMjU1WjATMREwDwYDVQQD +DAhUZXN0TGVhZjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDMWqNhqXlWIksJi +fYrEQLdPlWG4zAfvc/1q7npD5d+OJu4uwbseA2uXf8/wHgQYrpD3jkXT4b/amhyv +W1dCq+0wCgYIKoZIzj0EAwIDSQAwRgIhAPnXKKIs+8sE+W+3AH9zE3Z51I1ndCks +wD0Gud+kCORgAiEArgyP/lVR0Vh9NWe8iTNVXOyc4s8Jn0J+CF9UsUIGuFA= +-----END CERTIFICATE-----` -- 2.43.0