diff --git a/pkg/fulcio/certificate/summarize.go b/pkg/fulcio/certificate/summarize.go index 3370cfa3..78b7fee0 100644 --- a/pkg/fulcio/certificate/summarize.go +++ b/pkg/fulcio/certificate/summarize.go @@ -19,6 +19,8 @@ import ( "errors" "fmt" "reflect" + + "github.com/sigstore/sigstore/pkg/cryptoutils" ) type Summary struct { @@ -51,8 +53,11 @@ func SummarizeCertificate(cert *x509.Certificate) (Summary, error) { san = cert.URIs[0].String() case len(cert.EmailAddresses) > 0: san = cert.EmailAddresses[0] - default: - // TODO: Support OtherName SANs i.e. https://github.com/sigstore/fulcio/blob/main/docs/oid-info.md#1361415726417--othername-san + } + if san == "" { + san, _ = cryptoutils.UnmarshalOtherNameSAN(cert.Extensions) + } + if san == "" { return Summary{}, errors.New("No Subject Alternative Name found") } diff --git a/pkg/fulcio/certificate/summarize_test.go b/pkg/fulcio/certificate/summarize_test.go index f0f5742f..a891f514 100644 --- a/pkg/fulcio/certificate/summarize_test.go +++ b/pkg/fulcio/certificate/summarize_test.go @@ -101,6 +101,30 @@ func TestSummarizeCertificateWithOauthBundle(t *testing.T) { assert.Equal(t, expected, cs) } +func TestSummarizeCertificateWithOtherNameSAN(t *testing.T) { + entity := data.OthernameBundle(t) + vc, err := entity.VerificationContent() + if err != nil { + t.Fatalf("failed to get verification content: %v", err) + } + + leaf := vc.GetCertificate() + + if leaf == nil { + t.Fatalf("expected verification content to be a certificate chain") + } + cs, err := certificate.SummarizeCertificate(leaf) + assert.NoError(t, err) + expected := certificate.Summary{ + CertificateIssuer: "O=Linux Foundation,POSTALCODE=57274,STREET=548 Market St,L=San Francisco,ST=California,C=USA", + SubjectAlternativeName: "foo!oidc.local", + Extensions: certificate.Extensions{ + Issuer: "http://oidc.local:8080", + }, + } + assert.Equal(t, expected, cs) +} + func TestCompareExtensions(t *testing.T) { // Test that the extensions are equal actualExt := certificate.Extensions{ diff --git a/pkg/testing/data/data.go b/pkg/testing/data/data.go index 18084b3e..3e284df3 100644 --- a/pkg/testing/data/data.go +++ b/pkg/testing/data/data.go @@ -43,6 +43,9 @@ var SigstoreBundleRaw []byte //go:embed sigstore.js@2.0.0-provenanceBundle.json var SigstoreJS200ProvenanceBundleRaw []byte +//go:embed othernameBundle.json +var OthernameBundleRaw []byte + func TestBundle(t *testing.T, raw []byte) *bundle.ProtobufBundle { var b protobundle.Bundle err := protojson.Unmarshal(raw, &b) @@ -65,6 +68,10 @@ func SigstoreJS200ProvenanceBundle(t *testing.T) *bundle.ProtobufBundle { return TestBundle(t, SigstoreJS200ProvenanceBundleRaw) } +func OthernameBundle(t *testing.T) *bundle.ProtobufBundle { + return TestBundle(t, OthernameBundleRaw) +} + func PublicGoodTrustedMaterialRoot(t *testing.T) *root.TrustedRoot { trustedrootJSON, _ := os.ReadFile("../../examples/trusted-root-public-good.json") trustedRoot, _ := root.NewTrustedRootFromJSON(trustedrootJSON) @@ -73,3 +80,12 @@ func PublicGoodTrustedMaterialRoot(t *testing.T) *root.TrustedRoot { return trustedRoot } + +func ScaffoldingTrustedMaterialRoot(t *testing.T) *root.TrustedRoot { + trustedrootJSON, _ := os.ReadFile("../testing/data/trusted-root-scaffolding.json") + trustedRoot, _ := root.NewTrustedRootFromJSON(trustedrootJSON) + + assert.NotNil(t, trustedRoot) + + return trustedRoot +} diff --git a/pkg/testing/data/othernameBundle.json b/pkg/testing/data/othernameBundle.json new file mode 100644 index 00000000..d5810fcf --- /dev/null +++ b/pkg/testing/data/othernameBundle.json @@ -0,0 +1,44 @@ +{ + "mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json", + "verificationMaterial": { + "certificate": { + "rawBytes": "MIIEtTCCAp2gAwIBAgIUQo007zs0OhGOK8/Acik+axa7ve0wDQYJKoZIhvcNAQELBQAwfjEMMAoGA1UEBhMDVVNBMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRYwFAYDVQQJEw01NDggTWFya2V0IFN0MQ4wDAYDVQQREwU1NzI3NDEZMBcGA1UEChMQTGludXggRm91bmRhdGlvbjAeFw0yNDA3MTIxOTA2MjhaFw0yNDA3MTIxOTE2MjhaMAAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQ2fasaLzAQ6NW1DeN47ahLQ+4B/yykTNrlPN1L4/Fd2n7+Khk2Np0sCOzn1q1J3A9ctTaLwhmaWx98VXVax9uNo4IBcjCCAW4wDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBQav7zimj6IhRI/bEru7UNoUd2MMDAfBgNVHSMEGDAWgBSPD5vlHaXVMRD4Ul0X+y/OAJEl7TAsBgNVHREBAf8EIjAgoB4GCisGAQQBg78wAQegEAwOZm9vIW9pZGMubG9jYWwwJAYKKwYBBAGDvzABAQQWaHR0cDovL29pZGMubG9jYWw6ODA4MDAmBgorBgEEAYO/MAEIBBgMFmh0dHA6Ly9vaWRjLmxvY2FsOjgwODAwgYoGCisGAQQB1nkCBAIEfAR6AHgAdgDesHDYHzkyPSGM4zeGpsPji0+Fkuo5K601DwRJUWQDXAAAAZCoVvGxAAAEAwBHMEUCIF8KATnGR/A0M00weGYISnKlMHu+/PQPLXu7yO0G2itfAiEA2k2BG9Hzdp2AcgverhnsegnXxjKNO5FNtnwW/jnOIo4wDQYJKoZIhvcNAQELBQADggIBAGODe/vPPzDxaroHlIm/2uGoAl7a/aWJZvjobg7a9QqSM43nFhprRF3C518jATPxmzr0xzmDMOcI6+aT1ezK6pBRK5U/vY+mLzYHxBg9CcBDd6A8mOl89Qn1x6awSXoq+3D950Eww3vHfEJUS5gAFfD0SE91Y9L6fN1u9VzfcB27sTHfnfCk78iQf+sA0KWaTFgekCTkWetP9839efcQo5xY5JkxHzCWxKDsZrZqH3goGHCqdIL93g06QLJIHqOH3ztMvfkYbLmVuTV2RiysdYVhD6sJRlEKyiXtaXwthqdbsgbiKD8gRmQRJir961PoxTKkSvHhdafVmVUYtkWO6wQ98PwmOY0Poj+3zWoOAsnzqr0jwFn8QVNdeWKlDmzXqdXn5aBoXBphlQy/j2u1TWsl8Hc7JL+HhmV3GhqRbhD31WxVAQqi0poK7ig3ZB+q36TXvesmLEWenICplXscUy2Lr39C5sBeiLwLse3aaXse95YHqJkYgP44cS33/mmTmy2C1Fc4Pu01akUhLx69/sgLHS/3G2+UqgG8nslz2N7l7SUXat4Djqec1XQvoWG/f7kUbn3+dt0N8vv4YHVqVyaW7QkXcP6hyjnT8chmjsqCSCy8KWsgxr0pqpLCrrumlSke1BJGL4EZm0hSDvrh0dhqTgros8GZsYq8AJBAAmqj" + }, + "tlogEntries": [ + { + "logIndex": "3", + "logId": { + "keyId": "9vs1fkgdlblPyMuWiLRAQbEg0hmDHE6UwC92VxyLS8g=" + }, + "kindVersion": { + "kind": "hashedrekord", + "version": "0.0.1" + }, + "integratedTime": "1720811189", + "inclusionPromise": { + "signedEntryTimestamp": "MEUCIQDlRe4vCqGTap9Bko4TN9scDU7E7ideUfC51cEwxJJVJwIgBhimuSEUEUTuJ8rISl9UyMZvZp2hi1m7SSDIZM/ZkAA=" + }, + "inclusionProof": { + "logIndex": "3", + "rootHash": "uZYUY33ENx3NVSOphL2yVZLM+fjGXvOvRoQ15T82jp8=", + "treeSize": "4", + "hashes": [ + "7KJPHdqkyM0JutlXYl4X0P0KU4VrWQKzjU6khYDdypw=", + "t2F/5pUpEDAGCLrNbBywFrpk6eTM03yRmqxCkwO8nd0=" + ], + "checkpoint": { + "envelope": "rekor-00001-deployment-56bf7777c9-jds5x - 6364419738405537866\n4\nuZYUY33ENx3NVSOphL2yVZLM+fjGXvOvRoQ15T82jp8=\n\n— rekor-00001-deployment-56bf7777c9-jds5x 9vs1fjBFAiBU8kwsoJjjEntsK485B35Sa4xhVryfMnnsv+V3fjujFgIhAOe8Okg1uwIH0no5NG3YvR57Fq0rwdxTxLqrsj2Ox1aj\n" + } + }, + "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJiYzEwM2I0YTg0OTcxZWY2NDU5YjI5NGEyYjk4NTY4YTJiZmI3MmNkZWQwOWQ0YWNkMWUxNjM2NmE0MDFmOTViIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJQ2pKYmY1ZXZRRzBjZUN1SHEvZ1VWeWI4dFU5OHBaaVFudTcxYkRuT2drbUFpRUF0bzZLeTJYQjhPeitab1NQRzRQSjg3cnNUejFkR1h0V3V5LzU4OXZXZlB3PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVVjBWRU5EUVhBeVowRjNTVUpCWjBsVlVXOHdNRGQ2Y3pCUGFFZFBTemd2UVdOcGF5dGhlR0UzZG1Vd2QwUlJXVXBMYjFwSmFIWmpUa0ZSUlV3S1FsRkJkMlpxUlUxTlFXOUhRVEZWUlVKb1RVUldWazVDVFZKTmQwVlJXVVJXVVZGSlJYZHdSRmxYZUhCYWJUbDVZbTFzYUUxU1dYZEdRVmxFVmxGUlNBcEZkekZVV1ZjMFoxSnVTbWhpYlU1d1l6Sk9kazFTV1hkR1FWbEVWbEZSU2tWM01ERk9SR2RuVkZkR2VXRXlWakJKUms0d1RWRTBkMFJCV1VSV1VWRlNDa1YzVlRGT2Vra3pUa1JGV2sxQ1kwZEJNVlZGUTJoTlVWUkhiSFZrV0dkblVtMDVNV0p0VW1oa1IyeDJZbXBCWlVaM01IbE9SRUV6VFZSSmVFOVVRVElLVFdwb1lVWjNNSGxPUkVFelRWUkplRTlVUlRKTmFtaGhUVUZCZDFkVVFWUkNaMk54YUd0cVQxQlJTVUpDWjJkeGFHdHFUMUJSVFVKQ2QwNURRVUZSTWdwbVlYTmhUSHBCVVRaT1Z6RkVaVTQwTjJGb1RGRXJORUl2ZVhsclZFNXliRkJPTVV3MEwwWmtNbTQzSzB0b2F6Sk9jREJ6UTA5NmJqRnhNVW96UVRsakNuUlVZVXgzYUcxaFYzZzVPRlpZVm1GNE9YVk9ielJKUW1OcVEwTkJWelIzUkdkWlJGWlNNRkJCVVVndlFrRlJSRUZuWlVGTlFrMUhRVEZWWkVwUlVVMEtUVUZ2UjBORGMwZEJVVlZHUW5kTlJFMUNNRWRCTVZWa1JHZFJWMEpDVVdGMk4zcHBiV28yU1doU1NTOWlSWEoxTjFWT2IxVmtNazFOUkVGbVFtZE9WZ3BJVTAxRlIwUkJWMmRDVTFCRU5YWnNTR0ZZVmsxU1JEUlZiREJZSzNrdlQwRktSV3czVkVGelFtZE9Wa2hTUlVKQlpqaEZTV3BCWjI5Q05FZERhWE5IQ2tGUlVVSm5OemgzUVZGbFowVkJkMDlhYlRsMlNWYzVjRnBIVFhWaVJ6bHFXVmQzZDBwQldVdExkMWxDUWtGSFJIWjZRVUpCVVZGWFlVaFNNR05FYjNZS1RESTVjRnBIVFhWaVJ6bHFXVmQzTms5RVFUUk5SRUZ0UW1kdmNrSm5SVVZCV1U4dlRVRkZTVUpDWjAxR2JXZ3daRWhCTmt4NU9YWmhWMUpxVEcxNGRncFpNa1p6VDJwbmQwOUVRWGRuV1c5SFEybHpSMEZSVVVJeGJtdERRa0ZKUldaQlVqWkJTR2RCWkdkRVpYTklSRmxJZW10NVVGTkhUVFI2WlVkd2MxQnFDbWt3SzBacmRXODFTell3TVVSM1VrcFZWMUZFV0VGQlFVRmFRMjlXZGtkNFFVRkJSVUYzUWtoTlJWVkRTVVk0UzBGVWJrZFNMMEV3VFRBd2QyVkhXVWtLVTI1TGJFMUlkU3N2VUZGUVRGaDFOM2xQTUVjeWFYUm1RV2xGUVRKck1rSkhPVWg2WkhBeVFXTm5kbVZ5YUc1elpXZHVXSGhxUzA1UE5VWk9kRzUzVndvdmFtNVBTVzgwZDBSUldVcExiMXBKYUhaalRrRlJSVXhDVVVGRVoyZEpRa0ZIVDBSbEwzWlFVSHBFZUdGeWIwaHNTVzB2TW5WSGIwRnNOMkV2WVZkS0NscDJhbTlpWnpkaE9WRnhVMDAwTTI1R2FIQnlVa1l6UXpVeE9HcEJWRkI0YlhweU1IaDZiVVJOVDJOSk5pdGhWREZsZWtzMmNFSlNTelZWTDNaWksyMEtUSHBaU0hoQ1p6bERZMEpFWkRaQk9HMVBiRGc1VVc0eGVEWmhkMU5ZYjNFck0wUTVOVEJGZDNjemRraG1SVXBWVXpWblFVWm1SREJUUlRreFdUbE1OZ3BtVGpGMU9WWjZabU5DTWpkelZFaG1ibVpEYXpjNGFWRm1LM05CTUV0WFlWUkdaMlZyUTFSclYyVjBVRGs0TXpsbFptTlJielY0V1RWS2EzaElla05YQ25oTFJITmFjbHB4U0RObmIwZElRM0ZrU1V3NU0yY3dObEZNU2tsSWNVOUlNM3AwVFhabWExbGlURzFXZFZSV01sSnBlWE5rV1Zab1JEWnpTbEpzUlVzS2VXbFlkR0ZZZDNSb2NXUmljMmRpYVV0RU9HZFNiVkZTU21seU9UWXhVRzk0VkV0clUzWklhR1JoWmxadFZsVlpkR3RYVHpaM1VUazRVSGR0VDFrd1VBcHZhaXN6ZWxkdlQwRnpibnB4Y2pCcWQwWnVPRkZXVG1SbFYwdHNSRzE2V0hGa1dHNDFZVUp2V0VKd2FHeFJlUzlxTW5VeFZGZHpiRGhJWXpkS1RDdElDbWh0VmpOSGFIRlNZbWhFTXpGWGVGWkJVWEZwTUhCdlN6ZHBaek5hUWl0eE16WlVXSFpsYzIxTVJWZGxia2xEY0d4WWMyTlZlVEpNY2pNNVF6VnpRbVVLYVV4M1RITmxNMkZoV0hObE9UVlpTSEZLYTFsblVEUTBZMU16TXk5dGJWUnRlVEpETVVaak5GQjFNREZoYTFWb1RIZzJPUzl6WjB4SVV5OHpSeklyVlFweFowYzRibk5zZWpKT04ydzNVMVZZWVhRMFJHcHhaV014V0ZGMmIxZEhMMlkzYTFWaWJqTXJaSFF3VGpoMmRqUlpTRlp4Vm5saFZ6ZFJhMWhqVURab0NubHFibFE0WTJodGFuTnhRMU5EZVRoTFYzTm5lSEl3Y0hGd1RFTnljblZ0YkZOclpURkNTa2RNTkVWYWJUQm9VMFIyY21nd1pHaHhWR2R5YjNNNFIxb0tjMWx4T0VGS1FrRkJiWEZxQ2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIn19fX0=" + } + ] + }, + "messageSignature": { + "messageDigest": { + "algorithm": "SHA2_256", + "digest": "vBA7SoSXHvZFmylKK5hWiiv7cs3tCdSs0eFjZqQB+Vs=" + }, + "signature": "MEUCICjJbf5evQG0ceCuHq/gUVyb8tU98pZiQnu71bDnOgkmAiEAto6Ky2XB8Oz+ZoSPG4PJ87rsTz1dGXtWuy/589vWfPw=" + } +} diff --git a/pkg/testing/data/trusted-root-scaffolding.json b/pkg/testing/data/trusted-root-scaffolding.json new file mode 100644 index 00000000..3d725600 --- /dev/null +++ b/pkg/testing/data/trusted-root-scaffolding.json @@ -0,0 +1,53 @@ +{ + "mediaType": "application/vnd.dev.sigstore.trustedroot+json;version=0.1", + "tlogs": [ + { + "baseUrl": "http://rekor.rekor-system.172.18.255.1.sslip.io", + "hashAlgorithm": "SHA2_256", + "publicKey": { + "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnPyeVMLRWPJQpCHcUdG41k+oJiQEjX4uGSX7ujPH7Iv5zQD3VYiHhyQ/oMJvc1vx+2Zk2DBcBhN9IT0eZjB2RQ==", + "keyDetails": "PKIX_ECDSA_P256_SHA_256", + "validFor": { + "start": "2024-07-12T18:35:53Z" + } + }, + "logId": { + "keyId": "9vs1fkgdlblPyMuWiLRAQbEg0hmDHE6UwC92VxyLS8g=" + } + } + ], + "certificateAuthorities": [ + { + "subject": { + "organization": "Linux Foundation" + }, + "uri": "http://fulcio.fulcio-system.172.18.255.1.sslip.io", + "certChain": { + "certificates": [ + { + "rawBytes": "MIIFwzCCA6ugAwIBAgIIGOK4JTIvAnQwDQYJKoZIhvcNAQELBQAwfjEMMAoGA1UEBhMDVVNBMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRYwFAYDVQQJEw01NDggTWFya2V0IFN0MQ4wDAYDVQQREwU1NzI3NDEZMBcGA1UEChMQTGludXggRm91bmRhdGlvbjAeFw0yNDA3MTEyMjI4NDFaFw0yNTA3MTEyMjI4NDFaMH4xDDAKBgNVBAYTA1VTQTETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEWMBQGA1UECRMNNTQ4IE1hcmtldCBTdDEOMAwGA1UEERMFNTcyNzQxGTAXBgNVBAoTEExpbnV4IEZvdW5kYXRpb24wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCrq2z5byNpomZGJsrEloYzae0zU6bZK2x+9C16DdocsLavJNX2MaxQ28imb5YYp4z6M52SDPW4NZKCtJRSOp4Z+jK6194z6r08SCbU4JdU6qhBWhzb5PqDN8JYImnWAsUAg2MHu8DWDHsNVfyivxkqeeyTf/c4aAJX0YqVv8WnvEnI6rstV6CO3/Q7VqZrK3vfUH4rFuiIBwCO1TLnVh9RHARM43oDdeKAQLKh2p4PD6VoOVPNEw8uxuokG8qyJZOUVgUETovR8E3puTVn3iopea2BvMADZQA1u6MT4MCjY/Hqv+RdQ6W4c2eyey/ZZSoiQUZmkO2YTqtYPH2B+ucDmIOJ07MtraFeB1CXfRlPa5sv02N6NzZN/iD66GQ/fV2PiuMyJVmhnYJp0Yf3onVmmpxIEOkUDnWudUtMJHZuLy0rhu/hAid6l0KEGjXlBvXu7txZHw1AMerQbvn5VJdPgm4PT/5xK5f1PpPGxVZwGkjmBMZmj9+hRt0OHH59aK31vqGqPbQtIXguAlF89O1UaZv4JGnpdaJl4K3huXnahcI16+8s+Vu9sJ4dfZT/NlFV26a4aU7q+E7yH3n8+zmsk3+l06BWxz7R6SSp6Fx4yPB/3SBs2c5SJ5k6a+/3SssqVHWwgSZD6cXDt1ByYDMjkHFExV0oLDr0Q057l/ainQIDAQABo0UwQzAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUjw+b5R2l1TEQ+FJdF/svzgCRJe0wDQYJKoZIhvcNAQELBQADggIBAECAX4HbC+MWJS5+D6aZmu7P85ZDzHMpIk5LJiAJwLUIOZwF4K0z9AOHE/nqg5+PnZGWWI3a9UheuzsZauerz/jaP8thBWjVDJCROJZpMMvALAjJfgIFJw3YLNPUup0EL4UohZ7iWoD6e/vfY64DKzCpdfGDRfcBCnWqBIYeSSPNqH+i0L059oR9kXv3jwR4os0CWk8TUMBYGeDADeE27QuZ4qafLkmOaqp//yWXwOoe4MZBxettZz/Nib5RRhCxRQ88hbs/zH3T5bBgp+DZ0anjy2iVhOj2x02mdD6Zcb32JgEJLQHCTAdGamcdulQDXC+YS9N2U0ap8J3tZCrEPQkdkeRzJ2EzQx38NIiY16BPlAqnnRpOZiXqee4O7bni4qdyVAYpkArSRNvKQbTyLHYLiQ+TEMs0SboajbQtC38I4ztZXr2ozM2b1MU0d3rBLsozmAhqT99od8wiBValo0EEi2mSxArRHy0puIOMs1i4kIz2yTbyeEI5pnkq/2uaX+RPmS2UB83SmbZ7Ex9eNe6QjnMhCv5fU0wcjtwwPp0GMMRulErGvnZ39PRMjEH79C8Nfhx9nZZoEN5VCG9qrM1KMlDLwNc09W5RJTYRQ7d41sC2hdMgwmxVJ08Ai3XMn7xiJ9JwnaypClc14XsQERoy2afgBUME9CL00G20nVYb" + } + ] + }, + "validFor": { + "start": "2024-07-12T18:35:53Z" + } + } + ], + "ctlogs": [ + { + "baseUrl": "http://ctlog.ctlog-system.172.18.255.1.sslip.io", + "hashAlgorithm": "SHA2_256", + "publicKey": { + "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJ7v1OnMWwYi4O5oaycBsWKom3McZBDzNqXsIOq9AXc3z2HOeWVbaDd1V/9c91WRFyAv77Ao9hS9D9MEboT7lZg==", + "keyDetails": "PKIX_ECDSA_P256_SHA_256", + "validFor": { + "start": "2024-07-12T18:35:53Z" + } + }, + "logId": { + "keyId": "3rBw2B85Mj0hjOM3hqbD44tPhZLqOSutNQ8ESVFkA1w=" + } + } + ] +} diff --git a/pkg/verify/signed_entity.go b/pkg/verify/signed_entity.go index 45bc014d..1b607fb3 100644 --- a/pkg/verify/signed_entity.go +++ b/pkg/verify/signed_entity.go @@ -15,6 +15,7 @@ package verify import ( + "encoding/asn1" "errors" "fmt" "io" @@ -23,6 +24,7 @@ import ( "github.com/in-toto/in-toto-golang/in_toto" "github.com/sigstore/sigstore-go/pkg/fulcio/certificate" "github.com/sigstore/sigstore-go/pkg/root" + "github.com/sigstore/sigstore/pkg/cryptoutils" ) const ( @@ -497,11 +499,26 @@ func (v *SignedEntityVerifier) Verify(entity SignedEntity, pb PolicyBuilder) (*V if leafCert := verificationContent.GetCertificate(); leafCert != nil { signedWithCertificate = true + certSummary, err = certificate.SummarizeCertificate(leafCert) + if err != nil { + return nil, fmt.Errorf("failed to summarize certificate: %w", err) + } + // From spec: // > ## Certificate // > … // > The Verifier MUST perform certification path validation (RFC 5280 §6) of the certificate chain with the pre-distributed Fulcio root certificate(s) as a trust anchor, but with a fake “current time.” If a timestamp from the timestamping service is available, the Verifier MUST perform path validation using the timestamp from the Timestamping Service. If a timestamp from the Transparency Service is available, the Verifier MUST perform path validation using the timestamp from the Transparency Service. If both are available, the Verifier performs path validation twice. If either fails, verification fails. + if len(leafCert.UnhandledCriticalExtensions) > 0 { + var unhandledExts []asn1.ObjectIdentifier + for _, oid := range leafCert.UnhandledCriticalExtensions { + if !oid.Equal(cryptoutils.SANOID) { + unhandledExts = append(unhandledExts, oid) + } + } + leafCert.UnhandledCriticalExtensions = unhandledExts + } + for _, verifiedTs := range verifiedTimestamps { // verify the leaf certificate against the root err = VerifyLeafCertificate(verifiedTs.Timestamp, leafCert, v.trustedMaterial) @@ -519,11 +536,6 @@ func (v *SignedEntityVerifier) Verify(entity SignedEntity, pb PolicyBuilder) (*V return nil, fmt.Errorf("failed to verify signed certificate timestamp: %w", err) } } - - certSummary, err = certificate.SummarizeCertificate(leafCert) - if err != nil { - return nil, fmt.Errorf("failed to summarize certificate: %w", err) - } } // From spec: diff --git a/pkg/verify/signed_entity_test.go b/pkg/verify/signed_entity_test.go index 99be505b..8cc1ec97 100644 --- a/pkg/verify/signed_entity_test.go +++ b/pkg/verify/signed_entity_test.go @@ -199,6 +199,30 @@ func TestEntitySignedByPublicGoodWithHighObserverTimestampThresholdFails(t *test } } +func TestEntityWithOthernameSan(t *testing.T) { + tr := data.ScaffoldingTrustedMaterialRoot(t) + entity := data.OthernameBundle(t) + + v, err := verify.NewSignedEntityVerifier(tr, verify.WithoutAnyObserverTimestampsUnsafe()) + assert.NoError(t, err) + + digest, err := hex.DecodeString("bc103b4a84971ef6459b294a2b98568a2bfb72cded09d4acd1e16366a401f95b") + assert.NoError(t, err) + + certID, err := verify.NewShortCertificateIdentity("http://oidc.local:8080", "foo!oidc.local", "") + res, err := v.Verify(entity, verify.NewPolicy(verify.WithArtifactDigest("sha256", digest), verify.WithCertificateIdentity(certID))) + assert.NoError(t, err) + assert.NotNil(t, res) + + assert.Equal(t, res.VerifiedIdentity.Issuer, "http://oidc.local:8080") + assert.Equal(t, res.VerifiedIdentity.SubjectAlternativeName.SubjectAlternativeName, "foo!oidc.local") + + // an email address doesn't verify + certID, err = verify.NewShortCertificateIdentity("http://oidc.local:8080", "foo@oidc.local", "") + _, err = v.Verify(entity, verify.NewPolicy(verify.WithArtifactDigest("sha256", digest), verify.WithCertificateIdentity(certID))) + assert.Error(t, err) +} + // Now we test policy: func TestVerifyPolicyOptionErors(t *testing.T) {