Skip to content

Commit

Permalink
WIP: Rough support for Sigstore Bundles using sigstore-go verifier
Browse files Browse the repository at this point in the history
Signed-off-by: Cody Soyland <[email protected]>
  • Loading branch information
codysoyland committed Feb 27, 2024
1 parent b0fd308 commit dd295ec
Show file tree
Hide file tree
Showing 5 changed files with 266 additions and 5 deletions.
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ require (
github.com/docker/docker v24.0.7+incompatible
github.com/docker/go-connections v0.5.0
github.com/go-jose/go-jose/v3 v3.0.1
github.com/sigstore/sigstore-go v0.2.0
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.1
github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.1
github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.1
Expand Down Expand Up @@ -227,6 +228,7 @@ require (
github.com/sassoftware/relic v7.2.1+incompatible // indirect
github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/sigstore/protobuf-specs v0.2.1 // indirect
github.com/sigstore/timestamp-authority v1.2.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
Expand All @@ -237,6 +239,7 @@ require (
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/thales-e-security/pool v0.0.2 // indirect
github.com/theupdateframework/go-tuf/v2 v2.0.0-20240207172116-f5cf71290141 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/transparency-dev/merkle v0.0.2 // indirect
github.com/vbatts/tar-split v0.11.5 // indirect
Expand All @@ -255,7 +258,7 @@ require (
go.uber.org/automaxprocs v1.5.3 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/mod v0.15.0 // indirect
golang.org/x/oauth2 v0.16.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/term v0.17.0 // indirect
Expand Down
12 changes: 10 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -718,14 +718,20 @@ github.com/sassoftware/relic/v7 v7.6.1 h1:O5s8ewCgq5QYNpv45dK4u6IpBmDM9RIcsbf/G1
github.com/sassoftware/relic/v7 v7.6.1/go.mod h1:NxwtWxWxlUa9as2qZi635Ye6bBT/tGnMALLq7dSfOOU=
github.com/secure-systems-lab/go-securesystemslib v0.8.0 h1:mr5An6X45Kb2nddcFlbmfHkLguCE9laoZCUzEEpIZXA=
github.com/secure-systems-lab/go-securesystemslib v0.8.0/go.mod h1:UH2VZVuJfCYR8WgMlCU1uFsOUU+KeyrTWcSS73NBOzU=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
github.com/sigstore/cosign/v2 v2.2.3 h1:WX7yawI+EXu9h7S5bZsfYCbB9XW6Jc43ctKy/NoOSiA=
github.com/sigstore/cosign/v2 v2.2.3/go.mod h1:WpMn4MBt0cI23GdHsePwO4NxhX1FOz1ITGB3ALUjFaI=
github.com/sigstore/protobuf-specs v0.2.1 h1:KIoM7E3C4uaK092q8YoSj/XSf9720f8dlsbYwwOmgEA=
github.com/sigstore/protobuf-specs v0.2.1/go.mod h1:xPqQGnH/HllKuZ4VFPz/g+78epWM/NLRGl7Fuy45UdE=
github.com/sigstore/rekor v1.3.5 h1:QoVXcS7NppKY+rpbEFVHr4evGDZBBSh65X0g8PXoUkQ=
github.com/sigstore/rekor v1.3.5/go.mod h1:CWqOk/fmnPwORQmm7SyDgB54GTJizqobbZ7yOP1lvw8=
github.com/sigstore/sigstore v1.8.1 h1:mAVposMb14oplk2h/bayPmIVdzbq2IhCgy4g6R0ZSjo=
github.com/sigstore/sigstore v1.8.1/go.mod h1:02SL1158BSj15bZyOFz7m+/nJzLZfFd9A8ab3Kz7w/E=
github.com/sigstore/sigstore-go v0.2.0 h1:pbDfn8voPQZCySzCpiDE+3qljzsczHUX26dQsnjH2Cg=
github.com/sigstore/sigstore-go v0.2.0/go.mod h1:M6iQfFjmK0wbez+lRTg+O7cJxjYa7s++zfW30rzZBKk=
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.1 h1:rEDdUefulkIQaMJyzLwtgPDLNXBIltBABiFYfb0YmgQ=
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.1/go.mod h1:RCdYCc1IxCYWzh2IdzdA6Yf7JIY0cMRqH08fpQYechw=
github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.1 h1:DvRWG99QGWZC5mp42SEde2Xke/Q384Idnj2da7yB+Mk=
Expand Down Expand Up @@ -785,6 +791,8 @@ github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gt
github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU=
github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI=
github.com/theupdateframework/go-tuf v0.7.0/go.mod h1:uEB7WSY+7ZIugK6R1hiBMBjQftaFzn7ZCDJcp1tCUug=
github.com/theupdateframework/go-tuf/v2 v2.0.0-20240207172116-f5cf71290141 h1:SsiWxSpJ9AD71/vqiZVUjXW1Uusv1wlKn4zPKFNq25w=
github.com/theupdateframework/go-tuf/v2 v2.0.0-20240207172116-f5cf71290141/go.mod h1:D7dcS4bZMmF3pXOgUo8Vs6GLYM9sdrFFd37JqiP3hN4=
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0=
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
Expand Down Expand Up @@ -909,8 +917,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down
93 changes: 91 additions & 2 deletions pkg/webhook/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,11 @@ import (
"knative.dev/pkg/apis"
duckv1 "knative.dev/pkg/apis/duck/v1"

"github.com/sigstore/policy-controller/pkg/webhook/verify"
kubeclient "knative.dev/pkg/client/injection/kube/client"
"knative.dev/pkg/logging"

sgverify "github.com/sigstore/sigstore-go/pkg/verify"
)

type Validator struct{}
Expand Down Expand Up @@ -523,8 +526,17 @@ func ValidatePolicy(ctx context.Context, namespace string, ref name.Reference, c
result.static = true

case len(authority.Attestations) > 0:
// We're doing the verify-attestations path, so validate (.att)
result.attestations, result.err = ValidatePolicyAttestationsForAuthority(ctx, ref, authority, authorityRemoteOpts...)
// To support bundle verification, we need to decide when to invoke the
// bundle verifier. For now, we just use the bundle verifier if the
// authority name is "github", otherwise we use the regular verifier.
// TODO: Add option to ClusterImagePolicy to allow user to specify which
// verifier to use.
if authority.Keyless != nil && authority.Keyless.TrustRootRef == "github" {
result.attestations, result.err = ValidatePolicyAttestationsForAuthorityWithBundle(ctx, ref, authority, kc)
} else {
// We're doing the verify-attestations path, so validate (.att)
result.attestations, result.err = ValidatePolicyAttestationsForAuthority(ctx, ref, authority, authorityRemoteOpts...)
}

default:
result.signatures, result.err = ValidatePolicySignaturesForAuthority(ctx, ref, authority, authorityRemoteOpts...)
Expand Down Expand Up @@ -964,6 +976,83 @@ func ValidatePolicyAttestationsForAuthority(ctx context.Context, ref name.Refere
return ret, nil
}

func ValidatePolicyAttestationsForAuthorityWithBundle(ctx context.Context, ref name.Reference, authority webhookcip.Authority, kc authn.Keychain) (map[string][]PolicyAttestation, error) {
_ = ctx // TODO: Use context for verifier when it lands in sigstore-go
trustedRoot, err := verify.TrustedRootGithubStaging()
if err != nil {
return nil, err
}

if authority.Keyless.Identities == nil {
return nil, errors.New("must specify at least one identity for keyless authority")
}

// TODO: support more than one identity
id := authority.Keyless.Identities[0]
certID, err := sgverify.NewShortCertificateIdentity(id.Issuer, id.Subject, "", id.SubjectRegExp)
if err != nil {
return nil, err
}

bundle, result, err := verify.AttestationBundle(ref, trustedRoot, kc, sgverify.WithCertificateIdentity(certID))
if err != nil {
return nil, err
}

statementBytes, err := json.Marshal(result.Statement)
if err != nil {
return nil, err
}

// sha256 of statement
statementDigest := sha256.Sum256(statementBytes)
if err != nil {
return nil, err
}

// TODO: generate "signature ID" from the signature?
sig := string(bundle.GetDsseEnvelope().Signatures[0].Sig)

ret := make(map[string][]PolicyAttestation, 1)
pa := PolicyAttestation{
PolicySignature: PolicySignature{
ID: sig,
Subject: result.VerifiedIdentity.SubjectAlternativeName.Value,
Issuer: result.VerifiedIdentity.Issuer,
GithubExtensions: GithubExtensions{
WorkflowTrigger: result.VerifiedIdentity.Extensions.GithubWorkflowTrigger,
WorkflowSHA: result.VerifiedIdentity.Extensions.GithubWorkflowSHA,
WorkflowName: result.VerifiedIdentity.Extensions.GithubWorkflowName,
WorkflowRepo: result.VerifiedIdentity.Extensions.GithubWorkflowRepository,
WorkflowRef: result.VerifiedIdentity.Extensions.GithubWorkflowRef,
},
},
PredicateType: result.Statement.PredicateType,
Payload: statementBytes,
Digest: string(statementDigest[:]),
}

// TODO: support more than one attestation
att := authority.Attestations[0]
if att.PredicateType != result.Statement.PredicateType {
return nil, fmt.Errorf("predicate type mismatch: %s != %s", att.PredicateType, result.Statement.PredicateType)
}
ret[att.Name] = []PolicyAttestation{pa}

if att.Type != "" {
warn, err := policy.EvaluatePolicyAgainstJSON(ctx, att.Name, att.Type, att.Data, statementBytes)
if err != nil || warn != nil {
logging.FromContext(ctx).Warnf("failed policy validation for %s: %v", att.Name, err)
if err != nil {
return nil, err
}
return nil, warn
}
}

return nil, nil
}

// ResolvePodScalable implements policyduckv1beta1.PodScalableValidator
func (v *Validator) ResolvePodScalable(ctx context.Context, ps *policyduckv1beta1.PodScalable) {
// Don't mess with things that are being deleted or already deleted or
Expand Down
53 changes: 53 additions & 0 deletions pkg/webhook/verify/trusted-root-github-staging.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"mediaType": "application/vnd.dev.sigstore.trustedroot+json;version=0.1",
"certificateAuthorities": [
{
"subject": {
"organization": "GitHub, Inc.",
"commonName": "Internal Services Root"
},
"uri": "fulcio.githubapp.com",
"certChain": {
"certificates": [
{
"rawBytes": "MIICKjCCAbCgAwIBAgIUW3TJVeOvr+NSvJXdOw8nEEn7HhQwCgYIKoZIzj0EAwMwODEVMBMGA1UEChMMR2l0SHViLCBJbmMuMR8wHQYDVQQDExZGdWxjaW8gSW50ZXJtZWRpYXRlIGwxMB4XDTIzMDkxMjE0MDY1NFoXDTI0MDkxMTE0MDY1NFowODEVMBMGA1UEChMMR2l0SHViLCBJbmMuMR8wHQYDVQQDExZGdWxjaW8gSW50ZXJtZWRpYXRlIGwyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEsosodObhuHG6Pr5vp5y+pmnKawS1h2hwv3r3hBwqh3ZHJAw64mhDnDs9fw4jKkZEBYRSVyOHyZppz4day8hgpTIDwdj44Oan4RDb+wmj04jfhVLjLsQ4Q/X4K/ynRgNXo3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUm0vkDkQZ29hutYdayJobIRmf/iMwHwYDVR0jBBgwFoAUwOG4UqRLTz7eejgRBs9JjqFFmzMwCgYIKoZIzj0EAwMDaAAwZQIwIBl93E7vkWTvdeIm1WSIM4qNsj0ApE8LCj3k1vrY5x6/7yhAZs7QlO3/FBCoEeaZAjEAlJcNr37uZq9BYHODHBeO/gP+6EfbzsNaLV22ASBlhF/a9y83ESLuqCNN7IxGxmWT"
},
{
"rawBytes": "MIICFTCCAZugAwIBAgIUD3Jlqt4qhrcZI4UnGfPGrEq/pjQwCgYIKoZIzj0EAwMwODEVMBMGA1UEChMMR2l0SHViLCBJbmMuMR8wHQYDVQQDExZJbnRlcm5hbCBTZXJ2aWNlcyBSb290MB4XDTIzMDkxMTEyMDAwMFoXDTI4MDkwOTEyMDAwMFowODEVMBMGA1UEChMMR2l0SHViLCBJbmMuMR8wHQYDVQQDExZGdWxjaW8gSW50ZXJtZWRpYXRlIGwxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE7X7nK0wC7uEmDjW+on0sXIX3FacL3hhcrhneA+M/kl1OtvQiPmFrH9lbUQqOj/AfspJ8uGY3jaq8WuSg6ghatzYfuuzLAJIK4nGpCBafncF8EynOssPq64/Dz+JUWXqlo2YwZDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUwOG4UqRLTz7eejgRBs9JjqFFmzMwHwYDVR0jBBgwFoAUfFJ5/6rhfHEZPnXAhrQLhGkJJMwwCgYIKoZIzj0EAwMDaAAwZQIxAI8HWLrke7uzhOpwlD1cNixPmoX9XFKe7bEPozo0D+vKi0Gt6VlC7xPedFIw4/AypAIwQP+FGRWvfx0IAH5/n0aRiN7/LVpyFA5RkJASZOVOib2Y8pNuhXa9V3ZbWO6v6kW/"
},
{
"rawBytes": "MIIB9TCCAXqgAwIBAgIUNFryA06EHDIcd5EIbe8swbl9OY4wCgYIKoZIzj0EAwMwODEVMBMGA1UEChMMR2l0SHViLCBJbmMuMR8wHQYDVQQDExZJbnRlcm5hbCBTZXJ2aWNlcyBSb290MB4XDTIzMDgwNzEyMDAwMFoXDTMzMDgwNDEyMDAwMFowODEVMBMGA1UEChMMR2l0SHViLCBJbmMuMR8wHQYDVQQDExZJbnRlcm5hbCBTZXJ2aWNlcyBSb290MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEXYaXx4H0oNuVP/2cfydA3oaafvvkkkgb5hbL8/j/BO25S7uTmDOCA5e4QLLWCKFuc+xp2j14tCH4WmHzMUDvf2tXtInVliY5wZgQMM9L6klo/IwA9x4omdcjnT+kKJAjo0UwQzAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBAjAdBgNVHQ4EFgQUfFJ5/6rhfHEZPnXAhrQLhGkJJMwwCgYIKoZIzj0EAwMDaQAwZgIxAPzXsV+eokrqOHSQZH/XhhHE1slOscKy3DQpYpYJ1AWmJ2lJu/XOmubBX5s7apllUwIxALw2Ts8CDACiK42UymC8fk6sbNfoXUAWqdyKTVt2Lst+wNdkRniGvx7jT65BKTkcsQ=="
}
]
},
"validFor": {
"start": "2023-10-27T16:30:00Z"
}
}
],
"timestampAuthorities": [
{
"subject": {
"organization": "GitHub, Inc.",
"commonName": "Internal Services Root"
},
"uri": "timestamp.githubapp.com",
"certChain": {
"certificates": [
{
"rawBytes": "MIICHDCCAaGgAwIBAgIUNDVlmtZuvoujn4KwiC/oxIr8hxAwCgYIKoZIzj0EAwMwMjEVMBMGA1UEChMMR2l0SHViLCBJbmMuMRkwFwYDVQQDExBUU0EgaW50ZXJtZWRpYXRlMB4XDTIzMDgzMTEyMDAwMFoXDTI0MDgzMDEyMDAwMFowMjEVMBMGA1UEChMMR2l0SHViLCBJbmMuMRkwFwYDVQQDExBUU0EgVGltZXN0YW1waW5nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEV/zJhNTdu0Fa9hGCUih/JvqEoE81tEWrAVwUXXhdRgIY9hIFErLhNo6sSOpV9d7Zuy0KWMHhcimCUr41a1732ByVRy3f+Z4QhqpsgFMh5b5J90HJLK7HOyUZjehAnvSno3gwdjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUGwE6T5ZIh6lY9wP6vt42UHyVMewwHwYDVR0jBBgwFoAUdh+GTP65aetHLVLs9hdhGgDIKIwwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwCgYIKoZIzj0EAwMDaQAwZgIxAJo48LtrSsn0UmLtqGiUKg2EUvso+aDN5EyjpvMmobZ/Oq9zjnR7Of369hoABW4/1gIxANg5ZW4FqijhsXnA3md6jM9yLrLCI9QL+KnuZnXq6WgAcNQaAN7PNNjVDKV3iJEklw=="
},
{
"rawBytes": "MIICJDCCAaqgAwIBAgIUckXVHpiw7iJY1V/jY8LYLj5TgqAwCgYIKoZIzj0EAwMwODEVMBMGA1UEChMMR2l0SHViLCBJbmMuMR8wHQYDVQQDExZJbnRlcm5hbCBTZXJ2aWNlcyBSb290MB4XDTIzMDgwNzEyMDAwMFoXDTI4MDgwNTEyMDAwMFowMjEVMBMGA1UEChMMR2l0SHViLCBJbmMuMRkwFwYDVQQDExBUU0EgaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEISv96hTQ58QroEzzu4K+o9p8YkwDCBia2U7Y+VBNbOG/w1mLRibve9hSeUE1FSyLBMkiFSSm6MexcsbjyqOoNtRxuMinyYt6DSEox+/It2s/bTPyNAN0QP0DCQQOpnTZo3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwgwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUdh+GTP65aetHLVLs9hdhGgDIKIwwHwYDVR0jBBgwFoAUfFJ5/6rhfHEZPnXAhrQLhGkJJMwwCgYIKoZIzj0EAwMDaAAwZQIxAIhf+2E5W2yOb/fCDAjhL/G/jerf74M0tG/zyo32U2keawxkzZosDdwnPaHaGLynAQIwa8nr3en4fZz1AdOZm6nK5hr1qK2F94nifgnAJ/WeT0fZnK/oHan0R28x363qYuYH"
},
{
"rawBytes": "MIIB9TCCAXqgAwIBAgIUNFryA06EHDIcd5EIbe8swbl9OY4wCgYIKoZIzj0EAwMwODEVMBMGA1UEChMMR2l0SHViLCBJbmMuMR8wHQYDVQQDExZJbnRlcm5hbCBTZXJ2aWNlcyBSb290MB4XDTIzMDgwNzEyMDAwMFoXDTMzMDgwNDEyMDAwMFowODEVMBMGA1UEChMMR2l0SHViLCBJbmMuMR8wHQYDVQQDExZJbnRlcm5hbCBTZXJ2aWNlcyBSb290MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEXYaXx4H0oNuVP/2cfydA3oaafvvkkkgb5hbL8/j/BO25S7uTmDOCA5e4QLLWCKFuc+xp2j14tCH4WmHzMUDvf2tXtInVliY5wZgQMM9L6klo/IwA9x4omdcjnT+kKJAjo0UwQzAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBAjAdBgNVHQ4EFgQUfFJ5/6rhfHEZPnXAhrQLhGkJJMwwCgYIKoZIzj0EAwMDaQAwZgIxAPzXsV+eokrqOHSQZH/XhhHE1slOscKy3DQpYpYJ1AWmJ2lJu/XOmubBX5s7apllUwIxALw2Ts8CDACiK42UymC8fk6sbNfoXUAWqdyKTVt2Lst+wNdkRniGvx7jT65BKTkcsQ=="
}
]
},
"validFor": {
"start": "2023-10-27T16:30:00Z"
}
}
]
}
108 changes: 108 additions & 0 deletions pkg/webhook/verify/verify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package verify

import (
"encoding/hex"
"fmt"
"io"
"strings"

"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"

_ "embed"

"github.com/sigstore/sigstore-go/pkg/bundle"
"github.com/sigstore/sigstore-go/pkg/root"
"github.com/sigstore/sigstore-go/pkg/verify"
)

//go:embed trusted-root-github-staging.json
var trustedRootGithubStaging []byte

// TODO: Replace with TUF from TrustRoot CRD when TUF client is updated to support TrustedRoot files.
func TrustedRootGithubStaging() (root.TrustedMaterial, error) {
return root.NewTrustedRootFromJSON(trustedRootGithubStaging)
}

func AttestationBundle(ref name.Reference, trustedMaterial root.TrustedMaterial, kc authn.Keychain, policyOption verify.PolicyOption) (*bundle.ProtobufBundle, *verify.VerificationResult, error) {
b, imageDigest, err := getBundle(ref, kc)
if err != nil {
return nil, nil, err
}
_ = imageDigest

verifierConfig := []verify.VerifierOption{}
var artifactPolicy verify.ArtifactPolicyOption

verifierConfig = append(verifierConfig, verify.WithObserverTimestamps(1))

sev, err := verify.NewSignedEntityVerifier(trustedMaterial, verifierConfig...)
if err != nil {
return nil, nil, err
}

digestBytes, err := hex.DecodeString(imageDigest.Hex)
if err != nil {
return nil, nil, err
}
artifactPolicy = verify.WithArtifactDigest(imageDigest.Algorithm, digestBytes)

result, err := sev.Verify(b, verify.NewPolicy(artifactPolicy, policyOption))
if err != nil {
return nil, nil, err
}
return b, result, nil
}

func getBundle(ref name.Reference, kc authn.Keychain) (*bundle.ProtobufBundle, *v1.Hash, error) {
desc, err := remote.Get(ref, remote.WithAuthFromKeychain(kc))
if err != nil {
return nil, nil, fmt.Errorf("error getting image descriptor: %w", err)
}

digest := ref.Context().Digest(desc.Digest.String())

referrers, err := remote.Referrers(digest, remote.WithAuthFromKeychain(kc))
if err != nil {
return nil, nil, fmt.Errorf("error getting referrers: %w", err)
}
refManifest, err := referrers.IndexManifest()
if err != nil {
return nil, nil, fmt.Errorf("error getting referrers manifest: %w", err)
}

var bundleBytes []byte
for _, refDesc := range refManifest.Manifests {
if !strings.HasPrefix(refDesc.ArtifactType, "application/vnd.dev.sigstore.bundle+json") {
continue
}

refImg, err := remote.Image(ref.Context().Digest(refDesc.Digest.String()), remote.WithAuthFromKeychain(kc))
if err != nil {
return nil, nil, fmt.Errorf("error getting referrer image: %w", err)
}
layers, err := refImg.Layers()
if err != nil {
return nil, nil, fmt.Errorf("error getting referrer image: %w", err)
}
layer0, err := layers[0].Uncompressed()
if err != nil {
return nil, nil, fmt.Errorf("error getting referrer image: %w", err)
}
bundleBytes, err = io.ReadAll(layer0)
if err != nil {
return nil, nil, fmt.Errorf("error getting referrer image: %w", err)
}
}
if len(refManifest.Manifests) == 0 || len(bundleBytes) == 0 {
return nil, nil, fmt.Errorf("no bundle found in referrers")
}
b := &bundle.ProtobufBundle{}
err = b.UnmarshalJSON(bundleBytes)
if err != nil {
return nil, nil, fmt.Errorf("error unmarshalling bundle: %w", err)
}
return b, &desc.Digest, nil
}

0 comments on commit dd295ec

Please sign in to comment.