Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
laurentsimon committed Aug 23, 2022
1 parent da715b8 commit 3e76571
Show file tree
Hide file tree
Showing 7 changed files with 293 additions and 71 deletions.
1 change: 1 addition & 0 deletions errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ var (
ErrorInvalidPEM = errors.New("invalid PEM")
ErrorInvalidSignature = errors.New("invalid signature")
ErrorNoValidSignature = errors.New("no valid signature")
ErrorInternal = errors.New("internal error")
)
12 changes: 0 additions & 12 deletions verifiers/internal/gcb/keys/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,3 @@ func (self *PublicKey) VerifySignature(digest [32]byte, sig []byte) error {

return nil
}

// TODO: load public keys
/*func init() {
pubKey, err := PublicKeyNew("asia-east1")
if err != nil {
panic(err)
}
fmt.Println(string(pubKey.Value))
}
*/
37 changes: 0 additions & 37 deletions verifiers/internal/gcb/keys/list

This file was deleted.

112 changes: 90 additions & 22 deletions verifiers/internal/gcb/provenance.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,31 @@ type v01IntotoStatement struct {
Predicate slsa01.ProvenancePredicate `json:"predicate"`
}

type provenance struct {
Build struct {
// TODO: compare to verified provenance.
// IntotoStatement v01IntotoStatement `json:"intotoStatement"`
} `json:"build"`
Kind string `json:"kind"`
ResourceURI string `json:"resourceUri"`
Envelope dsselib.Envelope `json:"envelope"`
}

type gloudProvenance struct {
ImageSummary struct {
Digest string `json:"digest"`
FullQualifiedDigest string `json:"fully_qualified_digest"`
Registry string `json:"registry"`
Repsitory string `json:"repository"`
Digest string `json:"digest"`
FullyQualifiedDigest string `json:"fully_qualified_digest"`
Registry string `json:"registry"`
Repsitory string `json:"repository"`
} `json:"image_summary"`
ProvenanceSummary struct {
Provenance []struct {
Build struct {
// Note: used for testing only. This value is not trusted
// and should not be used.
// IntotoStatement v01IntotoStatement `json:"intotoStatement"`
} `json:"build"`
Kind string `json:"kind"`
ResourceUri string `json:"resourceUri"`
Envelope dsselib.Envelope `json:"envelope"`
} `json:"provenance"`
Provenance []provenance `json:"provenance"`
} `json:"provenance_summary"`
}

type GCBProvenance struct {
gcloudProv *gloudProvenance
verifiedProvenance *provenance
verifiedIntotoStatementStruct *v01IntotoStatement
}

Expand Down Expand Up @@ -83,7 +85,8 @@ func payloadFromEnvelope(env *dsselib.Envelope) ([]byte, error) {

func (self *GCBProvenance) isVerified() error {
// Check that the signature is verified.
if self.verifiedIntotoStatementStruct == nil {
if self.verifiedIntotoStatementStruct == nil ||
self.verifiedProvenance == nil {
return serrors.ErrorNoValidSignature
}
return nil
Expand All @@ -100,6 +103,57 @@ func (self *GCBProvenance) GetVerifiedIntotoStatement() ([]byte, error) {
return d, nil
}

func (self *GCBProvenance) VerifyMetadata(provenanceOpts *options.ProvenanceOpts) error {
if err := self.isVerified(); err != nil {
return err
}

if provenanceOpts == nil {
return nil
}
prov := self.verifiedProvenance

if prov.Kind != "BUILD" {
return fmt.Errorf("%w: expected kind to be 'BUILD', got %s", serrors.ErrorInvalidFormat, prov.Kind)
}

// Note: this could be verified in `VerifySourceURI`, but it is kept here
// because it is not part of the DSSE intoto payload.
// The `ResourceURI` is container@sha256:hash, without the tag.
// We only verify the URI's sha256 for simplicity.
if !strings.HasSuffix(prov.ResourceURI, "@sha256:"+provenanceOpts.ExpectedDigest) {
return fmt.Errorf("%w: expected resourceUri '%s', got '%s'",
serrors.ErrorMismatchHash, provenanceOpts.ExpectedDigest, prov.ResourceURI)
}
return nil
}

func (self *GCBProvenance) VerifySummary(provenanceOpts *options.ProvenanceOpts) error {
if err := self.isVerified(); err != nil {
return err
}

if provenanceOpts == nil {
return nil
}

// Validate the digest.
if self.gcloudProv.ImageSummary.Digest != "sha256:"+provenanceOpts.ExpectedDigest {
return fmt.Errorf("%w: expected summary digest '%s', got '%s'",
serrors.ErrorMismatchHash, provenanceOpts.ExpectedDigest,
self.gcloudProv.ImageSummary.Digest)
}

// Validate the qualified digest.
if !strings.HasSuffix(self.gcloudProv.ImageSummary.FullyQualifiedDigest,
"sha256:"+provenanceOpts.ExpectedDigest) {
return fmt.Errorf("%w: expected fully qualifiedd digest '%s', got '%s'",
serrors.ErrorMismatchHash, provenanceOpts.ExpectedDigest,
self.gcloudProv.ImageSummary.FullyQualifiedDigest)
}
return nil
}

func (self *GCBProvenance) VerifyIntotoHeaders() error {
if err := self.isVerified(); err != nil {
return err
Expand Down Expand Up @@ -244,13 +298,7 @@ func (self *GCBProvenance) VerifyVersionedTag(tag string) error {
return fmt.Errorf("%w: GCB versioned-tag verification", serrors.ErrorNotSupported)
}

func (self *GCBProvenance) VerifySignature() error {
if len(self.gcloudProv.ProvenanceSummary.Provenance) == 0 {
return fmt.Errorf("%w: no provenance found", serrors.ErrorInvalidDssePayload)
}
// Assume a single provenance in the array.
prov := self.gcloudProv.ProvenanceSummary.Provenance[0]

func (self *GCBProvenance) verifySignatures(prov *provenance) error {
// Verify the envelope type. It should be an intoto type.
if prov.Envelope.PayloadType != intoto.PayloadType {
return fmt.Errorf("%w: expected payload type '%s', got %s",
Expand Down Expand Up @@ -297,10 +345,30 @@ func (self *GCBProvenance) VerifySignature() error {
return fmt.Errorf("%w: %s", serrors.ErrorInvalidDssePayload, err.Error())
}
self.verifiedIntotoStatementStruct = &statement
self.verifiedProvenance = prov
fmt.Fprintf(os.Stderr, "Verification succeeded with region key '%s'\n", region)
return nil
}
}

return fmt.Errorf("%w: %v", serrors.ErrorNoValidSignature, errs)
}

func (self *GCBProvenance) VerifySignature() error {
if len(self.gcloudProv.ProvenanceSummary.Provenance) == 0 {
return fmt.Errorf("%w: no provenance found", serrors.ErrorInvalidDssePayload)
}
// Iterate over all provenances available.
var errs []error
for i := range self.gcloudProv.ProvenanceSummary.Provenance {
err := self.verifySignatures(&self.gcloudProv.ProvenanceSummary.Provenance[i])
if err != nil {
errs = append(errs, err)
continue
}

return nil
}

return fmt.Errorf("%w: %v", serrors.ErrorNoValidSignature, errs)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
{
"image_summary": {
"digest": "sha256:1a033b002f89ed2b8ea733162497fb70f1a4049a7f8602d6a33682b4ad9921fd",
"fully_qualified_digest": "us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image@sha256:2a033b002f89ed2b8ea733162497fb70f1a4049a7f8602d6a33682b4ad9921fd",
"registry": "us-west2-docker.pkg.dev",
"repository": "quickstart-docker-repo"
},
"provenance_summary": {
"provenance": [
{
"build": {
"intotoStatement": {
"_type": "https://in-toto.io/Statement/v0.1",
"predicateType": "https://slsa.dev/provenance/v0.1",
"slsaProvenance": {
"builder": {
"id": "https://cloudbuild.googleapis.com/[email protected]"
},
"materials": [
{
"uri": "https://github.com/laurentsimon/gcb-tests/commit/fbbb98765e85ad464302dc5977968104d36e455e"
}
],
"metadata": {
"buildFinishedOn": "2022-08-15T22:43:34.366498Z",
"buildInvocationId": "b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b",
"buildStartedOn": "2022-08-15T22:43:18.700638187Z"
},
"recipe": {
"arguments": {
"@type": "type.googleapis.com/google.devtools.cloudbuild.v1.Build",
"id": "b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b",
"options": {
"dynamicSubstitutions": true,
"logging": "LEGACY",
"pool": {},
"substitutionOption": "ALLOW_LOOSE"
},
"sourceProvenance": {},
"steps": [
{
"args": [
"build",
"-t",
"us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image:v14",
"."
],
"name": "gcr.io/cloud-builders/docker",
"pullTiming": {
"endTime": "2022-08-15T22:43:21.662016533Z",
"startTime": "2022-08-15T22:43:21.657262492Z"
},
"status": "SUCCESS",
"timing": {
"endTime": "2022-08-15T22:43:27.056377441Z",
"startTime": "2022-08-15T22:43:21.657262492Z"
}
}
]
},
"entryPoint": "cloudbuild.yaml",
"type": "https://cloudbuild.googleapis.com/[email protected]"
}
},
"subject": [
{
"digest": {
"sha256": "1a033b002f89ed2b8ea733162497fb70f1a4049a7f8602d6a33682b4ad9921fd"
},
"name": "https://us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image:v14"
}
]
}
},
"createTime": "2022-08-15T22:43:35.649016Z",
"envelope": {
"payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZSI6eyJidWlsZGVyIjp7ImlkIjoiaHR0cHM6Ly9jbG91ZGJ1aWxkLmdvb2dsZWFwaXMuY29tL0dvb2dsZUhvc3RlZFdvcmtlckB2MC4yIn0sIm1hdGVyaWFscyI6W3sidXJpIjoiaHR0cHM6Ly9naXRodWIuY29tL2xhdXJlbnRzaW1vbi9nY2ItdGVzdHMvY29tbWl0L2ZiYmI5ODc2NWU4NWFkNDY0MzAyZGM1OTc3OTY4MTA0ZDM2ZTQ1NWUifV0sIm1ldGFkYXRhIjp7ImJ1aWxkRmluaXNoZWRPbiI6IjIwMjItMDgtMTVUMjI6NDM6MzQuMzY2NDk4WiIsImJ1aWxkSW52b2NhdGlvbklkIjoiYjZlMDUyYTctNWFhNC00MWJmLWE1NmItOWJjNGU0ZjMwNThiIiwiYnVpbGRTdGFydGVkT24iOiIyMDIyLTA4LTE1VDIyOjQzOjE4LjcwMDYzODE4N1oifSwicmVjaXBlIjp7ImFyZ3VtZW50cyI6eyJAdHlwZSI6InR5cGUuZ29vZ2xlYXBpcy5jb20vZ29vZ2xlLmRldnRvb2xzLmNsb3VkYnVpbGQudjEuQnVpbGQiLCJpZCI6ImI2ZTA1MmE3LTVhYTQtNDFiZi1hNTZiLTliYzRlNGYzMDU4YiIsIm9wdGlvbnMiOnsiZHluYW1pY1N1YnN0aXR1dGlvbnMiOnRydWUsImxvZ2dpbmciOiJMRUdBQ1kiLCJwb29sIjp7fSwic3Vic3RpdHV0aW9uT3B0aW9uIjoiQUxMT1dfTE9PU0UifSwic291cmNlUHJvdmVuYW5jZSI6e30sInN0ZXBzIjpbeyJhcmdzIjpbImJ1aWxkIiwiLXQiLCJ1cy13ZXN0Mi1kb2NrZXIucGtnLmRldi9nb3NzdC1zY2FyZS1zYW5kYm94L3F1aWNrc3RhcnQtZG9ja2VyLXJlcG8vcXVpY2tzdGFydC1pbWFnZTp2MTQiLCIuIl0sIm5hbWUiOiJnY3IuaW8vY2xvdWQtYnVpbGRlcnMvZG9ja2VyIiwicHVsbFRpbWluZyI6eyJlbmRUaW1lIjoiMjAyMi0wOC0xNVQyMjo0MzoyMS42NjIwMTY1MzNaIiwic3RhcnRUaW1lIjoiMjAyMi0wOC0xNVQyMjo0MzoyMS42NTcyNjI0OTJaIn0sInN0YXR1cyI6IlNVQ0NFU1MiLCJ0aW1pbmciOnsiZW5kVGltZSI6IjIwMjItMDgtMTVUMjI6NDM6MjcuMDU2Mzc3NDQxWiIsInN0YXJ0VGltZSI6IjIwMjItMDgtMTVUMjI6NDM6MjEuNjU3MjYyNDkyWiJ9fV19LCJlbnRyeVBvaW50IjoiY2xvdWRidWlsZC55YW1sIiwidHlwZSI6Imh0dHBzOi8vY2xvdWRidWlsZC5nb29nbGVhcGlzLmNvbS9Hb29nbGVIb3N0ZWRXb3JrZXJAdjAuMiJ9fSwicHJlZGljYXRlVHlwZSI6Imh0dHBzOi8vc2xzYS5kZXYvcHJvdmVuYW5jZS92MC4xIiwic2xzYVByb3ZlbmFuY2UiOnsiYnVpbGRlciI6eyJpZCI6Imh0dHBzOi8vY2xvdWRidWlsZC5nb29nbGVhcGlzLmNvbS9Hb29nbGVIb3N0ZWRXb3JrZXJAdjAuMiJ9LCJtYXRlcmlhbHMiOlt7InVyaSI6Imh0dHBzOi8vZ2l0aHViLmNvbS9sYXVyZW50c2ltb24vZ2NiLXRlc3RzL2NvbW1pdC9mYmJiOTg3NjVlODVhZDQ2NDMwMmRjNTk3Nzk2ODEwNGQzNmU0NTVlIn1dLCJtZXRhZGF0YSI6eyJidWlsZEZpbmlzaGVkT24iOiIyMDIyLTA4LTE1VDIyOjQzOjM0LjM2NjQ5OFoiLCJidWlsZEludm9jYXRpb25JZCI6ImI2ZTA1MmE3LTVhYTQtNDFiZi1hNTZiLTliYzRlNGYzMDU4YiIsImJ1aWxkU3RhcnRlZE9uIjoiMjAyMi0wOC0xNVQyMjo0MzoxOC43MDA2MzgxODdaIn0sInJlY2lwZSI6eyJhcmd1bWVudHMiOnsiQHR5cGUiOiJ0eXBlLmdvb2dsZWFwaXMuY29tL2dvb2dsZS5kZXZ0b29scy5jbG91ZGJ1aWxkLnYxLkJ1aWxkIiwiaWQiOiJiNmUwNTJhNy01YWE0LTQxYmYtYTU2Yi05YmM0ZTRmMzA1OGIiLCJvcHRpb25zIjp7ImR5bmFtaWNTdWJzdGl0dXRpb25zIjp0cnVlLCJsb2dnaW5nIjoiTEVHQUNZIiwicG9vbCI6e30sInN1YnN0aXR1dGlvbk9wdGlvbiI6IkFMTE9XX0xPT1NFIn0sInNvdXJjZVByb3ZlbmFuY2UiOnt9LCJzdGVwcyI6W3siYXJncyI6WyJidWlsZCIsIi10IiwidXMtd2VzdDItZG9ja2VyLnBrZy5kZXYvZ29zc3Qtc2NhcmUtc2FuZGJveC9xdWlja3N0YXJ0LWRvY2tlci1yZXBvL3F1aWNrc3RhcnQtaW1hZ2U6djE0IiwiLiJdLCJuYW1lIjoiZ2NyLmlvL2Nsb3VkLWJ1aWxkZXJzL2RvY2tlciIsInB1bGxUaW1pbmciOnsiZW5kVGltZSI6IjIwMjItMDgtMTVUMjI6NDM6MjEuNjYyMDE2NTMzWiIsInN0YXJ0VGltZSI6IjIwMjItMDgtMTVUMjI6NDM6MjEuNjU3MjYyNDkyWiJ9LCJzdGF0dXMiOiJTVUNDRVNTIiwidGltaW5nIjp7ImVuZFRpbWUiOiIyMDIyLTA4LTE1VDIyOjQzOjI3LjA1NjM3NzQ0MVoiLCJzdGFydFRpbWUiOiIyMDIyLTA4LTE1VDIyOjQzOjIxLjY1NzI2MjQ5MloifX1dfSwiZW50cnlQb2ludCI6ImNsb3VkYnVpbGQueWFtbCIsInR5cGUiOiJodHRwczovL2Nsb3VkYnVpbGQuZ29vZ2xlYXBpcy5jb20vR29vZ2xlSG9zdGVkV29ya2VyQHYwLjIifX0sInN1YmplY3QiOlt7ImRpZ2VzdCI6eyJzaGEyNTYiOiIxYTAzM2IwMDJmODllZDJiOGVhNzMzMTYyNDk3ZmI3MGYxYTQwNDlhN2Y4NjAyZDZhMzM2ODJiNGFkOTkyMWZkIn0sIm5hbWUiOiJodHRwczovL3VzLXdlc3QyLWRvY2tlci5wa2cuZGV2L2dvc3N0LXNjYXJlLXNhbmRib3gvcXVpY2tzdGFydC1kb2NrZXItcmVwby9xdWlja3N0YXJ0LWltYWdlOnYxNCJ9XX0=",
"payloadType": "application/vnd.in-toto+json",
"signatures": [
{
"keyid": "projects/verified-builder/locations/global/keyRings/attestor/cryptoKeys/builtByGCB/cryptoKeyVersions/1",
"sig": "MEYCIQD-0xUsdkYnsmKnQL_ndEvXknLfn82zsG-hGyYUd4aYsAIhAP4KSCxN2VPNc-dvfrQIGduMUNmAiHxLttdezqdrSf3F"
}
]
},
"kind": "BUILD",
"name": "projects/gosst-scare-sandbox/occurrences/8ce06798-f94d-4772-a224-04e473163790",
"noteName": "projects/verified-builder/notes/intoto_b6e052a7-5aa4-41bf-a56b-9bc4e4f3058b",
"resourceUri": "https://us-west2-docker.pkg.dev/gosst-scare-sandbox/quickstart-docker-repo/quickstart-image@sha256:1a033b002f89ed2b8ea733162497fb70f1a4049a7f8602d6a33682b4ad9921fd",
"updateTime": "2022-08-15T22:43:35.649016Z"
}
]
}
}

Loading

0 comments on commit 3e76571

Please sign in to comment.