From 7cae27cca26b973e64e7698415ec821df76a4d0e Mon Sep 17 00:00:00 2001 From: linus-sun Date: Thu, 14 Nov 2024 19:07:28 +0000 Subject: [PATCH] add matched indices Signed-off-by: linus-sun --- pkg/ct/monitor.go | 43 +++++++++++-- pkg/ct/monitor_test.go | 142 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 173 insertions(+), 12 deletions(-) diff --git a/pkg/ct/monitor.go b/pkg/ct/monitor.go index 2c84563a..71b6ccf4 100644 --- a/pkg/ct/monitor.go +++ b/pkg/ct/monitor.go @@ -32,14 +32,14 @@ func GetCTLogEntries(logClient *ctclient.LogClient, startIndex int, endIndex int return entries, nil } -func ScanEntryCertSubject(logEntry ct.LogEntry, monitoredCertIDs []identity.CertificateIdentity) ([]*identity.LogEntry, error) { - matchedEntries := []*identity.LogEntry{} +func ScanEntryCertSubject(logEntry ct.LogEntry, monitoredCertIDs []identity.CertificateIdentity) ([]identity.LogEntry, error) { + matchedEntries := []identity.LogEntry{} for _, monitoredCertID := range monitoredCertIDs { match, sub, iss, err := identity.CertMatchesPolicy(logEntry.X509Cert, monitoredCertID.CertSubject, monitoredCertID.Issuers) if err != nil { return nil, fmt.Errorf("error with policy matching at index %d: %w", logEntry.Index, err) } else if match { - matchedEntries = append(matchedEntries, &identity.LogEntry{ + matchedEntries = append(matchedEntries, identity.LogEntry{ CertSubject: sub, Issuer: iss, Index: logEntry.Index, @@ -49,8 +49,8 @@ func ScanEntryCertSubject(logEntry ct.LogEntry, monitoredCertIDs []identity.Cert return matchedEntries, nil } -func ScanEntryOIDExtensions(logEntry ct.LogEntry, monitoredOIDMatchers []extensions.OIDExtension) ([]*identity.LogEntry, error) { - matchedEntries := []*identity.LogEntry{} +func ScanEntryOIDExtensions(logEntry ct.LogEntry, monitoredOIDMatchers []extensions.OIDExtension) ([]identity.LogEntry, error) { + matchedEntries := []identity.LogEntry{} cert := logEntry.X509Cert for _, monitoredOID := range monitoredOIDMatchers { match, _, extValue, err := identity.OIDMatchesPolicy(cert, monitoredOID.ObjectIdentifier, monitoredOID.ExtensionValues) @@ -58,7 +58,7 @@ func ScanEntryOIDExtensions(logEntry ct.LogEntry, monitoredOIDMatchers []extensi return nil, fmt.Errorf("error with policy matching at index %d: %w", logEntry.Index, err) } if match { - matchedEntries = append(matchedEntries, &identity.LogEntry{ + matchedEntries = append(matchedEntries, identity.LogEntry{ Index: logEntry.Index, OIDExtension: monitoredOID.ObjectIdentifier, ExtensionValue: extValue, @@ -67,3 +67,34 @@ func ScanEntryOIDExtensions(logEntry ct.LogEntry, monitoredOIDMatchers []extensi } return matchedEntries, nil } + +func MatchedIndices(logEntries []ct.LogEntry, mvs identity.MonitoredValues) ([]identity.LogEntry, error) { + matchedEntries := []identity.LogEntry{} + for _, entry := range logEntries { + matchedCertSubjectEntries, err := ScanEntryCertSubject(entry, mvs.CertificateIdentities) + if err != nil { + return nil, err + } + matchedEntries = append(matchedEntries, matchedCertSubjectEntries...) + + matchedOIDEntries, err := ScanEntryOIDExtensions(entry, mvs.OIDMatchers) + if err != nil { + return nil, err + } + matchedEntries = append(matchedEntries, matchedOIDEntries...) + } + + return matchedEntries, nil +} + +func IdentitySearch(client *ctclient.LogClient, startIndex int, endIndex int, mvs identity.MonitoredValues) ([]identity.LogEntry, error) { + retrievedEntries, err := GetCTLogEntries(client, startIndex, endIndex) + if err != nil { + return nil, err + } + matchedEntries, err := MatchedIndices(retrievedEntries, mvs) + if err != nil { + return nil, err + } + return matchedEntries, nil +} diff --git a/pkg/ct/monitor_test.go b/pkg/ct/monitor_test.go index 2511d523..eef54163 100644 --- a/pkg/ct/monitor_test.go +++ b/pkg/ct/monitor_test.go @@ -38,7 +38,7 @@ func TestScanEntryCertSubject(t *testing.T) { testCases := map[string]struct { inputEntry ct.LogEntry inputSubjects []identity.CertificateIdentity - expected []*identity.LogEntry + expected []identity.LogEntry }{ "no matching subject": { inputEntry: ct.LogEntry{ @@ -50,7 +50,7 @@ func TestScanEntryCertSubject(t *testing.T) { }, }, inputSubjects: []identity.CertificateIdentity{}, - expected: []*identity.LogEntry{}, + expected: []identity.LogEntry{}, }, "matching subject": { inputEntry: ct.LogEntry{ @@ -79,7 +79,7 @@ func TestScanEntryCertSubject(t *testing.T) { Issuers: []string{}, }, }, - expected: []*identity.LogEntry{ + expected: []identity.LogEntry{ {Index: 1, CertSubject: subjectName, Issuer: issuerName}, @@ -119,7 +119,7 @@ func TestScanEntryOIDExtensions(t *testing.T) { testCases := map[string]struct { inputEntry ct.LogEntry inputOIDExtensions []extensions.OIDExtension - expected []*identity.LogEntry + expected []identity.LogEntry }{ "no matching subject": { inputEntry: ct.LogEntry{ @@ -132,7 +132,7 @@ func TestScanEntryOIDExtensions(t *testing.T) { ExtensionValues: []string{extValueString}, }, }, - expected: []*identity.LogEntry{}, + expected: []identity.LogEntry{}, }, "matching subject": { inputEntry: ct.LogEntry{ @@ -145,7 +145,7 @@ func TestScanEntryOIDExtensions(t *testing.T) { ExtensionValues: []string{extValueString}, }, }, - expected: []*identity.LogEntry{ + expected: []identity.LogEntry{ { Index: 1, OIDExtension: matchedAsn1OID, @@ -172,3 +172,133 @@ func TestScanEntryOIDExtensions(t *testing.T) { } } } + +func TestMatchedIndices(t *testing.T) { + extCert, err := mockCertificateWithExtension(google_asn1.ObjectIdentifier{2, 5, 29, 17}, "test cert value") + if err != nil { + t.Errorf("Expected nil got %v", err) + } + unmatchedAsn1OID := asn1.ObjectIdentifier{2} + matchedAsn1OID := asn1.ObjectIdentifier{2, 5, 29, 17} + extValueString := "test cert value" + extCert.Extensions = append(extCert.Extensions, pkix.Extension{ + Id: google_asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 1}, + Value: []byte(issuerName), + }) + inputEntries := []ct.LogEntry{ + {Index: 1, + X509Cert: &x509.Certificate{ + DNSNames: []string{subjectName}, + EmailAddresses: []string{organizationName}, + Extensions: extCert.Extensions, + Issuer: pkix.Name{ + CommonName: issuerName, + }, + }, + }, + } + testCases := map[string]struct { + inputEntries []ct.LogEntry + inputMonitoredValues identity.MonitoredValues + expected []identity.LogEntry + }{ + "empty case": { + inputEntries: []ct.LogEntry{}, + inputMonitoredValues: identity.MonitoredValues{}, + expected: []identity.LogEntry{}, + }, + "no matching entries": { + inputEntries: inputEntries, + inputMonitoredValues: identity.MonitoredValues{ + CertificateIdentities: []identity.CertificateIdentity{ + { + CertSubject: "non-matched-subject", + }, + }, + OIDMatchers: []extensions.OIDExtension{ + { + ObjectIdentifier: unmatchedAsn1OID, + ExtensionValues: []string{"unmatched extension value"}, + }, + }, + }, + expected: []identity.LogEntry{}, + }, + "matching certificate identity and issuer": { + inputEntries: inputEntries, + inputMonitoredValues: identity.MonitoredValues{ + CertificateIdentities: []identity.CertificateIdentity{ + { + CertSubject: subjectName, + Issuers: []string{issuerName}, + }, + }, + }, + expected: []identity.LogEntry{ + { + Index: 1, + CertSubject: subjectName, + Issuer: issuerName, + }, + }, + }, + "matching OID extension": { + inputEntries: inputEntries, + inputMonitoredValues: identity.MonitoredValues{ + OIDMatchers: []extensions.OIDExtension{ + { + ObjectIdentifier: matchedAsn1OID, + ExtensionValues: []string{extValueString}, + }, + }, + }, + expected: []identity.LogEntry{ + { + Index: 1, + OIDExtension: matchedAsn1OID, + ExtensionValue: extValueString, + }, + }, + }, + "matching certificate subject and issuer and OID extension": { + inputEntries: inputEntries, + inputMonitoredValues: identity.MonitoredValues{ + CertificateIdentities: []identity.CertificateIdentity{ + { + CertSubject: subjectName, + Issuers: []string{issuerName}, + }, + }, + OIDMatchers: []extensions.OIDExtension{ + { + ObjectIdentifier: matchedAsn1OID, + ExtensionValues: []string{extValueString}, + }, + }, + }, + expected: []identity.LogEntry{ + { + Index: 1, + CertSubject: subjectName, + Issuer: issuerName, + }, + { + Index: 1, + OIDExtension: matchedAsn1OID, + ExtensionValue: extValueString, + }, + }, + }, + } + + for _, tc := range testCases { + matchedEntries, err := MatchedIndices(tc.inputEntries, tc.inputMonitoredValues) + if err != nil { + t.Errorf("error matching indices: %v", err) + } + expected := tc.expected + if !reflect.DeepEqual(matchedEntries, expected) { + t.Errorf("received %v, expected %v", matchedEntries, expected) + } + } +}