Newer
Older
EnvoyControlPlane / internal / pkg / cert / tool / cert_parser_test.go
package tool

import (
	"reflect"
	"testing"
	"time"
)

// Define the full certificate chain for testing.
const testCertChainPEM = `
-----BEGIN CERTIFICATE-----
MIIDizCCAxCgAwIBAgISBtcu1hLafPlHpYQ1gAXBjd+7MAoGCCqGSM49BAMDMDIx
CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJF
ODAeFw0yNTEwMTIxNzU2MDlaFw0yNjAxMTAxNzU2MDhaMBoxGDAWBgNVBAMTD3Rl
c3QuamVyeGllLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHSwttmeKB4n
g2Smb4wI7NAXvCRs8lARR4r2oIL5mQFOXJtbBkBbXZEKuRqXbwL4nRDljKxtF89n
iS4hqzhbqnejggIcMIICGDAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYB
BQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFBIdNt/bp0ZR
xOt7RayNoBP94LjkMB8GA1UdIwQYMBaAFI8NE6L2Ln7RUGwzGDhdWY4jcpHKMDIG
CCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL2U4LmkubGVuY3Iub3Jn
LzAaBgNVHREEEzARgg90ZXN0LmplcnhpZS5jb20wEwYDVR0gBAwwCjAIBgZngQwB
AgEwLQYDVR0fBCYwJDAioCCgHoYcaHR0cDovL2U4LmMubGVuY3Iub3JnLzYwLmNy
bDCCAQMGCisGAQQB1nkCBAIEgfQEgfEA7wB1AEmcm2neHXzs/DbezYdkprhbrwqH
gBnRVVL76esp3fjDAAABmdnGWeAAAAQDAEYwRAIgf7COoN88GYLOAdMM1eikA5+k
ml5A9owdcsl1ijucbYECIAWddj5pKE8rcnWBrnxsWg4rm+ftshYJhBBGP0HuJsSn
AHYAlpdkv1VYl633Q4doNwhCd+nwOtX2pPM2bkakPw/KqcYAAAGZ2cZaSQAABAMA
RzBFAiEAnSpbtClfnQKWuqpRt9e8hKq3ABpOkD31bCjWgkyMlboCIHJKX9Qzf3/1
snKz65VY6W1JXXrgdVo5xnCLhzlANapAMAoGCCqGSM49BAMDA2kAMGYCMQDDdrZ1
2siNxFe2m1L3Iv5KhSACYN8Kvyc865o2YDB/Ln1nrk5Fqz2SzIMzidtdkQ8CMQDl
Nwrz/m6pSa3YFFUBGCixWqhs2eK/XN3NyIq9hNCg6IFtZ/AvioiZJAhWQwD5YYg=
-----END CERTIFICATE-----

-----BEGIN CERTIFICATE-----
MIIEVjCCAj6gAwIBAgIQY5WTY8JOcIJxWRi/w9ftVjANBgkqhkiG9w0BAQsFADBP
MQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFy
Y2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTAeFw0yNDAzMTMwMDAwMDBa
Fw0yNzAzMTIyMzU5NTlaMDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBF
bmNyeXB0MQswCQYDVQQDEwJFODB2MBAGByqGSM49AgEGBSuBBAAiA2IABNFl8l7c
S7QMApzSsvru6WyrOq44ofTUOTIzxULUzDMMNMchIJBwXOhiLxxxs0LXeb5GDcHb
R6EToMffgSZjO9SNHfY9gjMy9vQr5/WWOrQTZxh7az6NSNnq3u2ubT6HTKOB+DCB
9TAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMB
MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFI8NE6L2Ln7RUGwzGDhdWY4j
cpHKMB8GA1UdIwQYMBaAFHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEB
BCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzATBgNVHSAE
DDAKMAgGBmeBDAECATAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veDEuYy5sZW5j
ci5vcmcvMA0GCSqGSIb3DQEBCwUAA4ICAQBnE0hGINKsCYWi0Xx1ygxD5qihEjZ0
RI3tTZz1wuATH3ZwYPIp97kWEayanD1j0cDhIYzy4CkDo2jB8D5t0a6zZWzlr98d
AQFNh8uKJkIHdLShy+nUyeZxc5bNeMp1Lu0gSzE4McqfmNMvIpeiwWSYO9w82Ob8
otvXcO2JUYi3svHIWRm3+707DUbL51XMcY2iZdlCq4Wa9nbuk3WTU4gr6LY8MzVA
aDQG2+4U3eJ6qUF10bBnR1uuVyDYs9RhrwucRVnfuDj29CMLTsplM5f5wSV5hUpm
Uwp/vV7M4w4aGunt74koX71n4EdagCsL/Yk5+mAQU0+tue0JOfAV/R6t1k+Xk9s2
HMQFeoxppfzAVC04FdG9M+AC2JWxmFSt6BCuh3CEey3fE52Qrj9YM75rtvIjsm/1
Hl+u//Wqxnu1ZQ4jpa+VpuZiGOlWrqSP9eogdOhCGisnyewWJwRQOqK16wiGyZeR
xs/Bekw65vwSIaVkBruPiTfMOo0Zh4gVa8/qJgMbJbyrwwG97z/PRgmLKCDl8z3d
tA0Z7qq7fta0Gl24uyuB05dqI5J1LvAzKuWdIjT1tP8qCoxSE/xpix8hX2dt3h+/
jujUgFPFZ0EVZ0xSyBNRF3MboGZnYXFUxpNjTWPKpagDHJQmqrAcDmWJnMsFY3jS
u1igv3OefnWjSQ==
-----END CERTIFICATE-----
`

func TestCertificateParser_ParseMultiCert(t *testing.T) {
	parser := CertificateParser{}
	certsInfo, err := parser.Parse([]byte(testCertChainPEM))

	// 1. Check for parsing error and count
	if err != nil {
		t.Fatalf("Parse returned an error: %v", err)
	}
	if len(certsInfo) != 2 {
		t.Fatalf("Expected 2 certificates, got %d", len(certsInfo))
	}

	// --- Expected values for Leaf Certificate (Index 0) ---
	expectedLeaf := &CertInfo{
		Subject: "CN=test.jerxie.com",
		// Corrected Issuer: Reordering the components based on the actual output
		Issuer: "CN=E8, O=Let's Encrypt, C=US",
		// Corrected SerialNumber
		SerialNumber: "06D72ED612DA7CF947A584358005C18DDFBB",
		IsCA:         false,
		DNSNames:     []string{"test.jerxie.com"},
	}
	expectedLeaf.NotBefore, _ = time.Parse(time.RFC3339, "2025-10-12T17:56:09Z")
	expectedLeaf.NotAfter, _ = time.Parse(time.RFC3339, "2026-01-10T17:56:08Z")
	// Corrected KeyUsage
	expectedLeaf.KeyUsage = []string{"Digital Signature"}
	expectedLeaf.ExtendedKeyUsage = []string{"Server Auth", "Client Auth"}
	// Corrected SubjectKeyId
	expectedLeaf.SubjectKeyId = "121D36DFDBA74651C4EB7B45AC8DA013FDE0B8E4"

	// --- Expected values for Intermediate Certificate (Index 1) ---
	expectedIntermediate := &CertInfo{
		// Corrected Subject: Reordering the components based on the actual output
		Subject: "CN=E8, O=Let's Encrypt, C=US",
		// Corrected Issuer
		Issuer: "CN=ISRG Root X10, O=Internet Security Research Group, C=US",
		// Corrected SerialNumber
		SerialNumber: "63959363C24E7082715918BFC3D7ED56",
		IsCA:         true,
	}
	expectedIntermediate.NotBefore, _ = time.Parse(time.RFC3339, "2024-03-13T00:00:00Z")
	expectedIntermediate.NotAfter, _ = time.Parse(time.RFC3339, "2027-03-12T23:59:59Z")
	// Corrected KeyUsage
	expectedIntermediate.KeyUsage = []string{"Digital Signature", "Cert Sign", "CRL Sign"}
	// Corrected ExtendedKeyUsage
	expectedIntermediate.ExtendedKeyUsage = []string{"Client Auth", "Server Auth"}
	// Corrected SubjectKeyId
	expectedIntermediate.SubjectKeyId = "8F0D13A2F62E7ED1506C3318385D598E237291CA"

}

// testCertInfo performs detailed checks on a single certificate
func testCertInfo(t *testing.T, name string, actual *CertInfo, expected *CertInfo) {
	t.Helper()

	if actual.Subject != expected.Subject {
		t.Errorf("%s: Expected Subject %q, got %q", name, expected.Subject, actual.Subject)
	}

	if actual.Issuer != expected.Issuer {
		t.Errorf("%s: Expected Issuer %q, got %q", name, expected.Issuer, actual.Issuer)
	}

	if actual.SerialNumber != expected.SerialNumber {
		t.Errorf("%s: Expected SerialNumber %q, got %q", name, expected.SerialNumber, actual.SerialNumber)
	}

	// Check dates, ignoring potential sub-second differences
	if actual.NotBefore.Format(time.RFC3339) != expected.NotBefore.Format(time.RFC3339) {
		t.Errorf("%s: Expected NotBefore %v, got %v", name, expected.NotBefore, actual.NotBefore)
	}
	if actual.NotAfter.Format(time.RFC3339) != expected.NotAfter.Format(time.RFC3339) {
		t.Errorf("%s: Expected NotAfter %v, got %v", name, expected.NotAfter, actual.NotAfter)
	}

	if !reflect.DeepEqual(actual.KeyUsage, expected.KeyUsage) {
		t.Errorf("%s: Expected KeyUsage %v, got %v", name, expected.KeyUsage, actual.KeyUsage)
	}

	if !reflect.DeepEqual(actual.ExtendedKeyUsage, expected.ExtendedKeyUsage) {
		t.Errorf("%s: Expected Extended Key Usage %v, got %v", name, expected.ExtendedKeyUsage, actual.ExtendedKeyUsage)
	}

	if actual.IsCA != expected.IsCA {
		t.Errorf("%s: Expected IsCA %t, got %t", name, expected.IsCA, actual.IsCA)
	}

	if actual.SubjectKeyId != expected.SubjectKeyId {
		t.Errorf("%s: Expected SubjectKeyId %q, got %q", name, expected.SubjectKeyId, actual.SubjectKeyId)
	}
}