Skip to content

Commit

Permalink
yaml tag oid extensions
Browse files Browse the repository at this point in the history
Signed-off-by: linus-sun <[email protected]>
  • Loading branch information
linus-sun committed Nov 19, 2024
1 parent 8c71153 commit ed21107
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 128 deletions.
36 changes: 8 additions & 28 deletions pkg/ct/monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,40 +33,20 @@ func GetCTLogEntries(logClient *ctclient.LogClient, startIndex int, endIndex int
return entries, nil
}

func ScanEntryCertSubject(logEntry ct.LogEntry, monitoredCertIDs []identity.CertificateIdentity) ([]*identity.LogEntry, error) {
func ScanEntrySubject(logEntry ct.LogEntry, monitoredSubjects []string) ([]*identity.LogEntry, error) {
subject := logEntry.X509Cert.Subject.String()
certIssuer := logEntry.X509Cert.Issuer.String()
matchedEntries := []*identity.LogEntry{}
for _, monitoredCertID := range monitoredCertIDs {
regex, err := regexp.Compile(monitoredCertID.CertSubject)
for _, monitoredSub := range monitoredSubjects {
regex, err := regexp.Compile(monitoredSub)
if err != nil {
return nil, fmt.Errorf("error compiling regex for subject: %v", err)
return nil, fmt.Errorf("error compiling regex: %v", err)
}

expectedIssuers := monitoredCertID.Issuers

matches := regex.FindAllString(subject, -1)
for _, match := range matches {
if len(expectedIssuers) > 0 {
for _, expectedIssuer := range expectedIssuers {
regex, err := regexp.Compile(expectedIssuer)
if err != nil {
return nil, fmt.Errorf("error compiling regex for issuer: %v", err)
}
if regex.MatchString(certIssuer) {
matchedEntries = append(matchedEntries, &identity.LogEntry{
Index: logEntry.Index,
CertSubject: match,
Issuer: expectedIssuer,
})
}
}
} else {
matchedEntries = append(matchedEntries, &identity.LogEntry{
Index: logEntry.Index,
CertSubject: match,
})
}
matchedEntries = append(matchedEntries, &identity.LogEntry{
Index: logEntry.Index,
CertSubject: match,
})
}
}

Expand Down
26 changes: 6 additions & 20 deletions pkg/ct/monitor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,13 @@ import (

const (
subjectName = "test-subject"
issuerName = "test-issuer"
organizationName = "test-org"
)

func TestScanEntryCertSubject(t *testing.T) {
func TestScanEntrySubject(t *testing.T) {
testCases := map[string]struct {
inputEntry ct.LogEntry
inputSubjects []identity.CertificateIdentity
inputSubjects []string
expected []*identity.LogEntry
}{
"no matching subject": {
Expand All @@ -49,7 +48,7 @@ func TestScanEntryCertSubject(t *testing.T) {
},
},
},
inputSubjects: []identity.CertificateIdentity{},
inputSubjects: []string{},
expected: []*identity.LogEntry{},
},
"matching subject": {
Expand All @@ -60,33 +59,20 @@ func TestScanEntryCertSubject(t *testing.T) {
CommonName: subjectName,
Organization: []string{organizationName},
},
Issuer: pkix.Name{
CommonName: issuerName,
},
},
},
inputSubjects: []identity.CertificateIdentity{
{
CertSubject: subjectName,
Issuers: []string{issuerName},
},
{
CertSubject: organizationName,
Issuers: []string{},
},
},
inputSubjects: []string{subjectName, organizationName},
expected: []*identity.LogEntry{
{Index: 1,
CertSubject: subjectName,
Issuer: issuerName},
CertSubject: subjectName},
{Index: 1,
CertSubject: organizationName},
},
},
}

for _, tc := range testCases {
logEntries, err := ScanEntryCertSubject(tc.inputEntry, tc.inputSubjects)
logEntries, err := ScanEntrySubject(tc.inputEntry, tc.inputSubjects)
if err != nil {
t.Errorf("received error scanning entry for subjects: %v", err)
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/fulcio/extensions/extensions.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,9 @@ type CustomExtension struct {

// OIDMatchers holds all FulcioExtensions, OIDMatchers, and CustomExtensions
type OIDMatchers struct {
OIDExtensions []OIDExtension
FulcioExtensions FulcioExtensions
CustomExtensions []CustomExtension
OIDExtensions []OIDExtension `yaml:"oidExtensions"`
FulcioExtensions FulcioExtensions `yaml:"fulcioExtensions"`
CustomExtensions []CustomExtension `yaml:"customExtensions"`
}

func (e FulcioExtensions) RenderFulcioOIDMatchers() []OIDExtension {
Expand Down
62 changes: 6 additions & 56 deletions pkg/identity/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package identity

import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/json"
"errors"
Expand Down Expand Up @@ -46,21 +45,21 @@ type CertificateIdentity struct {
// MonitoredValues holds a set of values to compare against a given entry
type MonitoredValues struct {
// CertificateIdentities contains a list of subjects and issuers
CertificateIdentities []CertificateIdentity `yaml:"certIdentities"`
CertificateIdentities []CertificateIdentity
// Fingerprints contains a list of key fingerprints. Values are as follows:
// For keys, certificates, and minisign, hex-encoded SHA-256 digest
// of the DER-encoded PKIX public key or certificate
// For SSH and PGP, the standard for each ecosystem:
// For SSH, unpadded base-64 encoded SHA-256 digest of the key
// For PGP, hex-encoded SHA-1 digest of a key, which can be either
// a primary key or subkey
Fingerprints []string `yaml:"fingerprints"`
Fingerprints []string
// Subjects contains a list of subjects that are not specified in a
// certificate, such as a SSH key or PGP key email address
Subjects []string `yaml:"subjects"`
Subjects []string
// OIDMatchers represents a list of OID extension fields and associated values,
// which includes those constructed directly, those supported by Fulcio, and any constructed via dot notation.
OIDMatchers []extensions.OIDExtension `yaml:"oidMatchers"`
OIDMatchers []extensions.OIDExtension
}

// LogEntry holds a certificate subject, issuer, OID extension and associated value, and log entry metadata
Expand Down Expand Up @@ -275,60 +274,11 @@ func OIDMatchesPolicy[Certificate *x509.Certificate | *google_x509.Certificate](
return false, nil, "", nil
}

// getSubjectAlternateNames extracts all subject alternative names from
// the certificate, including email addresses, DNS, IP addresses, URIs, and OtherName SANs
// duplicate of cryptoutils function GetSubjectAlternateNames to match in case of google_x509 fork certificate
func getSubjectAlternateNames[Certificate *x509.Certificate | *google_x509.Certificate](certificate Certificate) []string {
sans := []string{}
switch cert := any(certificate).(type) {
case *x509.Certificate:
sans = append(sans, cert.DNSNames...)
sans = append(sans, cert.EmailAddresses...)
for _, ip := range cert.IPAddresses {
sans = append(sans, ip.String())
}
for _, uri := range cert.URIs {
sans = append(sans, uri.String())
}
// ignore error if there's no OtherName SAN
otherName, _ := cryptoutils.UnmarshalOtherNameSAN(cert.Extensions)
if len(otherName) > 0 {
sans = append(sans, otherName)
}
return sans
case *google_x509.Certificate:
sans = append(sans, cert.DNSNames...)
sans = append(sans, cert.EmailAddresses...)
for _, ip := range cert.IPAddresses {
sans = append(sans, ip.String())
}
for _, uri := range cert.URIs {
sans = append(sans, uri.String())
}
// ignore error if there's no OtherName SAN
pkixExts := []pkix.Extension{}
for _, googleExt := range cert.Extensions {
pkixExt := pkix.Extension{
Id: (asn1.ObjectIdentifier)(googleExt.Id),
Critical: googleExt.Critical,
Value: googleExt.Value,
}
pkixExts = append(pkixExts, pkixExt)
}
otherName, _ := cryptoutils.UnmarshalOtherNameSAN(pkixExts)
if len(otherName) > 0 {
sans = append(sans, otherName)
}
return sans
}
return sans
}

// CertMatchesPolicy returns true if a certificate contains a given subject and optionally a given issuer
// expectedSub and expectedIssuers can be regular expressions
// CertMatchesPolicy also returns the matched subject and issuer on success
func CertMatchesPolicy[Certificate *x509.Certificate | *google_x509.Certificate](cert Certificate, expectedSub string, expectedIssuers []string) (bool, string, string, error) {
sans := getSubjectAlternateNames(cert)
func CertMatchesPolicy(cert *x509.Certificate, expectedSub string, expectedIssuers []string) (bool, string, string, error) {
sans := cryptoutils.GetSubjectAlternateNames(cert)
var issuer string
var err error
issuer, err = getExtension(cert, certExtensionOIDCIssuerV2)
Expand Down
21 changes: 0 additions & 21 deletions pkg/identity/identity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -447,24 +447,3 @@ func TestCertMatches(t *testing.T) {
t.Errorf("expected subject %s and issuer %s, received subject %s and issuer %s", emailAddr, issuer, receivedSub, receivedIssuer)
}
}

func TestGoogleCertMatches(t *testing.T) {
emailAddr := "[email protected]"
issuer := "test-issuer"
cert := &google_x509.Certificate{
EmailAddresses: []string{emailAddr},
Extensions: []google_pkix.Extension{
{
Id: (google_asn1.ObjectIdentifier)(certExtensionOIDCIssuer),
Value: []byte(issuer),
},
},
}
matches, receivedSub, receivedIssuer, err := CertMatchesPolicy(cert, emailAddr, []string{issuer})
if !matches || err != nil {
t.Errorf("Expected true without error, got %v, error %v", matches, err)
}
if receivedSub != emailAddr || receivedIssuer != issuer {
t.Errorf("expected subject %s and issuer %s, received subject %s and issuer %s", emailAddr, issuer, receivedSub, receivedIssuer)
}
}

0 comments on commit ed21107

Please sign in to comment.