Skip to content

Commit

Permalink
Dsse one sig (#226)
Browse files Browse the repository at this point in the history
* Ran go fmt

Signed-off-by: Fredrik Skogman <[email protected]>

* Added test to verify that dsse envelope has exact one signature.

This is a bug-fix, as the protobuf spec for the bundl requires exactly one
signature to be present in the envelope.

Signed-off-by: Fredrik Skogman <[email protected]>

* Spelling error.

Signed-off-by: Fredrik Skogman <[email protected]>

* Correct year in copyright notice.

Signed-off-by: Fredrik Skogman <[email protected]>

* Lint fixes: removed unnecessary newline.

Signed-off-by: Fredrik Skogman <[email protected]>

* Updates from feeback.

Better error name, more explicit about the usage.
More explicit test caste name.

Signed-off-by: Fredrik Skogman <[email protected]>

---------

Signed-off-by: Fredrik Skogman <[email protected]>
  • Loading branch information
kommendorkapten authored Jul 11, 2024
1 parent 2f9185b commit a0f4538
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 11 deletions.
18 changes: 9 additions & 9 deletions examples/sigstore-go-signing/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ func main() {

if *idToken != "" {
fulcioOpts := &sign.FulcioOptions{
BaseURL: "https://fulcio.sigstage.dev",
Timeout: time.Duration(30 * time.Second),
Retries: 1,
BaseURL: "https://fulcio.sigstage.dev",
Timeout: time.Duration(30 * time.Second),
Retries: 1,
}
opts.CertificateProvider = sign.NewFulcio(fulcioOpts)
opts.CertificateProviderOptions = &sign.CertificateProviderOptions{
Expand All @@ -123,9 +123,9 @@ func main() {

if *tsa {
tsaOpts := &sign.TimestampAuthorityOptions{
URL: "https://timestamp.githubapp.com/api/v1/timestamp",
Timeout: time.Duration(30 * time.Second),
Retries: 1,
URL: "https://timestamp.githubapp.com/api/v1/timestamp",
Timeout: time.Duration(30 * time.Second),
Retries: 1,
}
opts.TimestampAuthorities = append(opts.TimestampAuthorities, sign.NewTimestampAuthority(tsaOpts))

Expand All @@ -135,9 +135,9 @@ func main() {

if *rekor {
rekorOpts := &sign.RekorOptions{
BaseURL: "https://rekor.sigstage.dev",
Timeout: time.Duration(90 * time.Second),
Retries: 1,
BaseURL: "https://rekor.sigstage.dev",
Timeout: time.Duration(90 * time.Second),
Retries: 1,
}
opts.TransparencyLogs = append(opts.TransparencyLogs, sign.NewRekor(rekorOpts))
}
Expand Down
15 changes: 14 additions & 1 deletion pkg/testing/data/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,14 @@ func Unmarshal[T any](t *testing.T, data []byte) T {
//go:embed sigstoreBundle.json
var SigstoreBundleRaw []byte

//go:embed sigstoreBundle2Sig.json
var SigstoreBundle2SigRaw []byte

//go:embed [email protected]
var SigstoreJS200ProvenanceBundleRaw []byte

// TestBundle creates *bundle.ProtobufBundle from a raw byte stream
// containing a JSON encoded protobuf bundle.
func TestBundle(t *testing.T, raw []byte) *bundle.ProtobufBundle {
var b protobundle.Bundle
err := protojson.Unmarshal(raw, &b)
Expand All @@ -56,15 +61,23 @@ func TestBundle(t *testing.T, raw []byte) *bundle.ProtobufBundle {
return bun
}

// SigstoreBundle returns a test *sigstore.Bundle
// SigstoreBundle returns a test *sigstore.Bundle.
func SigstoreBundle(t *testing.T) *bundle.ProtobufBundle {
return TestBundle(t, SigstoreBundleRaw)
}

// SigstoreBundle2Sig returns a test *sigstore.Bundle with two signatures.
func SigstoreBundle2Sig(t *testing.T) *bundle.ProtobufBundle {
return TestBundle(t, SigstoreBundle2SigRaw)
}

// SigstoreJS200ProvenanceBundle returns a test *sigstore.Bundle that
// contains a complete sigstore-js build provenance.
func SigstoreJS200ProvenanceBundle(t *testing.T) *bundle.ProtobufBundle {
return TestBundle(t, SigstoreJS200ProvenanceBundleRaw)
}

// PublicGoodTrustedMaterialRoot retruns a *root.TrustedRoot for PGI.
func PublicGoodTrustedMaterialRoot(t *testing.T) *root.TrustedRoot {
trustedrootJSON, _ := os.ReadFile("../../examples/trusted-root-public-good.json")
trustedRoot, _ := root.NewTrustedRootFromJSON(trustedrootJSON)
Expand Down
52 changes: 52 additions & 0 deletions pkg/testing/data/sigstoreBundle2Sig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.1",
"verificationMaterial": {
"tlogEntries": [
{
"logIndex": "6800908",
"logId": {
"keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="
},
"kindVersion": {
"kind": "intoto",
"version": "0.0.2"
},
"integratedTime": "1668034836",
"inclusionPromise": {
"signedEntryTimestamp": "MEYCIQCEx8HKsx9hobZjrNqHCSEJvjMEhc2wU2mUwkI7ButQHAIhAPevmw7piNjE2N1OWHmp9S5kBvlVIg93qu4i9yRaswur"
},
"canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoiYXBwbGljYXRpb24vdm5kLmluLXRvdG8ranNvbiIsInNpZ25hdHVyZXMiOlt7InB1YmxpY0tleSI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVU51ZWtORFFXbGhaMEYzU1VKQlowbFZRbTV0V2xKMFpHdFBkR1pQTDB4NVp6UXpOVU5TSzFaSmFTdEJkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BKZUUxVVFUVk5hazEzVFVSTk1WZG9ZMDVOYWtsNFRWUkJOVTFxVFhoTlJFMHhWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVWbFZVVTJUM2d2TkRFMFN6QmtRbmd6WXpOWEszUlJOMDVVVTJ4SlZsWXlORmxUWWtJS2JEWldlWFZKVmk5cE1UVkxRMnhUYWxWdk1uRlJkVXRUVlRSRmVtMUlaaklyUlUxcUwxbElXVWhsUWtGRWEwUjRhalpQUTBGVlZYZG5aMFpDVFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZVdlZrZERDbFZJVmxnMVlsaG9aWFF5TVdsMFltbzFWM1pvV25GQmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQwaDNXVVJXVWpCU1FWRklMMEpDVlhkRk5FVlNXVzVLY0ZsWE5VRmFSMVp2V1ZjeGJHTnBOV3BpTWpCM1RFRlpTMHQzV1VKQ1FVZEVkbnBCUWdwQlVWRmxZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRESjRkbG95YkhWTU1qbG9aRmhTYjAxSlIwdENaMjl5UW1kRlJVRmtXalZCWjFGRENrSklkMFZsWjBJMFFVaFpRVE5VTUhkaGMySklSVlJLYWtkU05HTnRWMk16UVhGS1MxaHlhbVZRU3pNdmFEUndlV2RET0hBM2J6UkJRVUZIUlZod0t6RUtXRkZCUVVKQlRVRlNla0pHUVdsRlFXdGtTVFk1TkM4NFFqSnlUMlJwZVZsUWJFVnVZMlpTZDJ0MVltOWtUMW8wYW14dE5HYzFNamcxVEd0RFNVaFNjQXB0UjJnMGNEVlBZeXRXYXl0QlMwaE5aSFF3Vm5SRU1pOHJZMkZJVjNsbE1WWnhSRFJ5UjJORFRVRnZSME5EY1VkVFRUUTVRa0ZOUkVFeVkwRk5SMUZEQ2sxQmVVczRkRkUxYlZCRFEybE1hbWxKYzFaelNWcFFXWFpvZGtSU00wUkdiR2sxUVZGNmFsTkRlbU5GVXk5b05XWkNNMGR3WlVSa1FrOTFPWFIxYTJFS2IzZEpkMDVyYjBWWldraFBkR3RoWTB4bGNrdzFNbVZaVTNCV2FtWlljMEV3WjBKTmNVRnViVXhIZDFoMGVtdFdTM0Z4ZWxWdFlscG9RM1k0YlRnMVlncG1kVWxTQ2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIiwic2lnIjoiVFVWVlEwbEdZeTlDZVV4b1EydFNNbGwwUVUxaWJVcHdNakF5V20xYU5GaFdSMWhHUzJrM2NpdHhOMnh6VGtSUVFXbEZRUzlLZDFneVVHbHNRMHgyYTNGRk9VNUtUVVpMVG00eVF6SnFPR05JTDNwNVJtaFJOalYzY21reVNGazkifV19LCJoYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiNjZiM2RjZmNhNjU5ZTVkNTA1NjAyYzNjOWFmOGZkMmJmNmE0YWZhY2FjMzNiMTc1ZTFkN2UwZWNhYjEwZjg5MCJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjhhNzVmNmM4ZGM0ZDlmNDA3Mjg1YmI0OWQzZTAxOWE1ZTY2NmY0MzQ5OWMyNzA3ZDkyODlhYTI3YzNjMmE2N2UifX19fQ=="
}
],
"timestampVerificationData": {
"rfc3161Timestamps": []
},
"x509CertificateChain": {
"certificates": [
{
"rawBytes": "MIICnzCCAiagAwIBAgIUBnmZRtdkOtfO/Lyg435CR+VIi+AwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA5MjMwMDM1WhcNMjIxMTA5MjMxMDM1WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeUE6Ox/414K0dBx3c3W+tQ7NTSlIVV24YSbBl6VyuIV/i15KClSjUo2qQuKSU4EzmHf2+EMj/YHYHeBADkDxj6OCAUUwggFBMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU/VGCUHVX5bXhet21itbj5WvhZqAwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEXp+1XQAABAMARzBFAiEAkdI694/8B2rOdiyYPlEncfRwkubodOZ4jlm4g5285LkCIHRpmGh4p5Oc+Vk+AKHMdt0VtD2/+caHWye1VqD4rGcCMAoGCCqGSM49BAMDA2cAMGQCMAyK8tQ5mPCCiLjiIsVsIZPYvhvDR3DFli5AQzjSCzcES/h5fB3GpeDdBOu9tukaowIwNkoEYZHOtkacLerL52eYSpVjfXsA0gBMqAnmLGwXtzkVKqqzUmbZhCv8m85bfuIR"
},
{
"rawBytes": "MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow="
},
{
"rawBytes": "MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ"
}
]
}
},
"dsseEnvelope": {
"payload": "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJzdWJqZWN0IjogWwogICAgewogICAgICAibmFtZSI6ICJzbHNhLXByb3ZlbmFuY2UtMC4wLjcudGd6IiwKICAgICAgImRpZ2VzdCI6IHsKICAgICAgICAic2hhNTEyIjogImJiZmQzNzJmYzliZWViNzc3ZmUzNWQwNWJiMTBjMGM3MGE5NzMzZDM2NmE3NWZlZDdkZDE4ODY2YzczOTFkZTNlZTdlODhiYTc0ZGQ0N2FiZjNlMTVjODQ1ZTU0N2ZjZjBlNWMzZGE4MDg1NGM3NTE1NTQyMjRkM2E2ZDRlNTVmIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsCiAgInByZWRpY2F0ZSI6IHsKICAgICJidWlsZFR5cGUiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9zbHNhLXByb3ZlbmFuY2UvZ2hhQHYwIiwKICAgICJidWlsZGVyIjogewogICAgICAiaWQiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9zbHNhLXByb3ZlbmFuY2VAMC4wLjEiCiAgICB9LAogICAgImludm9jYXRpb24iOiB7CiAgICAgICJjb25maWdTb3VyY2UiOiB7CiAgICAgICAgInVyaSI6ICJnaXQraHR0cHM6Ly9naXRodWIuY29tL2dpdGh1Yi9zbHNhLXByb3ZlbmFuY2VAcmVmcy9oZWFkcy9kZW1vIiwKICAgICAgICAiZGlnZXN0IjogewogICAgICAgICAgInNoYTEiOiAiMjljZmYzZGQ2NWY3ODBjMzYwMWJkNDU3YWNiNmZlNGU1OTMxYzgyNSIKICAgICAgICB9LAogICAgICAgICJlbnRyeVBvaW50IjogImRlbW8iCiAgICAgIH0sCiAgICAgICJwYXJhbWV0ZXJzIjoge30sCiAgICAgICJlbnZpcm9ubWVudCI6IHsKICAgICAgICAiR0lUSFVCX0VWRU5UX05BTUUiOiAicHVzaCIsCiAgICAgICAgIkdJVEhVQl9KT0IiOiAicnVuLXByb3ZlbmFuY2UtZGVtbyIsCiAgICAgICAgIkdJVEhVQl9SRUYiOiAicmVmcy9oZWFkcy9kZW1vIiwKICAgICAgICAiR0lUSFVCX1JFRl9UWVBFIjogImJyYW5jaCIsCiAgICAgICAgIkdJVEhVQl9SRVBPU0lUT1JZIjogImdpdGh1Yi9zbHNhLXByb3ZlbmFuY2UiLAogICAgICAgICJHSVRIVUJfUkVQT1NJVE9SWV9PV05FUiI6ICJnaXRodWIiLAogICAgICAgICJHSVRIVUJfUlVOX0FUVEVNUFQiOiAiNCIsCiAgICAgICAgIkdJVEhVQl9SVU5fSUQiOiAiMzAyNDA5MTU0NiIsCiAgICAgICAgIkdJVEhVQl9SVU5fTlVNQkVSIjogIjE3IiwKICAgICAgICAiR0lUSFVCX1NIQSI6ICIyOWNmZjNkZDY1Zjc4MGMzNjAxYmQ0NTdhY2I2ZmU0ZTU5MzFjODI1IiwKICAgICAgICAiR0lUSFVCX1dPUktGTE9XIjogImRlbW8iLAogICAgICAgICJJTUFHRV9PUyI6ICJ1YnVudHUyMCIsCiAgICAgICAgIklNQUdFX1ZFUlNJT04iOiAiMjAyMjA5MDUuMSIsCiAgICAgICAgIlJVTk5FUl9BUkNIIjogIlg2NCIsCiAgICAgICAgIlJVTk5FUl9OQU1FIjogIkdpdEh1YiBBY3Rpb25zIDUwIiwKICAgICAgICAiUlVOTkVSX09TIjogIkxpbnV4IgogICAgICB9CiAgICB9LAogICAgIm1ldGFkYXRhIjogewogICAgICAiYnVpbGRJbnZvY2F0aW9uSWQiOiAiMzAyNDA5MTU0Ni00IiwKICAgICAgImNvbXBsZXRlbmVzcyI6IHsKICAgICAgICAicGFyYW1ldGVycyI6IGZhbHNlLAogICAgICAgICJlbnZpcm9ubWVudCI6IGZhbHNlLAogICAgICAgICJtYXRlcmlhbHMiOiBmYWxzZQogICAgICB9LAogICAgICAicmVwcm9kdWNpYmxlIjogZmFsc2UKICAgIH0sCiAgICAibWF0ZXJpYWxzIjogWwogICAgICB7CiAgICAgICAgInVyaSI6ICJnaXQraHR0cHM6Ly9naXRodWIuY29tL2dpdGh1Yi9zbHNhLXByb3ZlbmFuY2UiLAogICAgICAgICJkaWdlc3QiOiB7CiAgICAgICAgICAic2hhMSI6ICIyOWNmZjNkZDY1Zjc4MGMzNjAxYmQ0NTdhY2I2ZmU0ZTU5MzFjODI1IgogICAgICAgIH0KICAgICAgfQogICAgXQogIH0KfQo=",
"payloadType": "application/vnd.in-toto+json",
"signatures": [
{
"sig": "MEUCIFc/ByLhCkR2YtAMbmJp202ZmZ4XVGXFKi7r+q7lsNDPAiEA/JwX2PilCLvkqE9NJMFKNn2C2j8cH/zyFhQ65wri2HY=",
"keyid": ""
},
{
"sig": "MEUCIFc/ByLhCkR2YtAMbmJp202ZmZ4XVGXFKi7r+q7lsNDPAiEA/JwX2PilCLvkqE9NJMFKNn2C2j8cH/zyFhAAAAAAAHY=",
"keyid": ""
}
]
}
}
124 changes: 124 additions & 0 deletions pkg/verify/dsse_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Copyright 2024 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package verify

import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"errors"
"fmt"
"testing"

"github.com/secure-systems-lab/go-securesystemslib/dsse"
"github.com/sigstore/sigstore/pkg/signature"
"github.com/stretchr/testify/assert"
)

type dsseSigner struct {
pk *ecdsa.PrivateKey
}

func (d *dsseSigner) Sign(_ context.Context, data []byte) ([]byte, error) {
digest := sha256.Sum256(data)
return d.pk.Sign(rand.Reader, digest[:], nil)
}

func (d *dsseSigner) KeyID() (string, error) {
return "", nil
}

type envelopeContent struct {
EnvelopeContent
e *dsse.Envelope
}

func (e *envelopeContent) RawEnvelope() *dsse.Envelope {
return e.e
}

func TestVerifyEnvelopeSignatureCount(t *testing.T) {
privk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
pubk := privk.Public().(*ecdsa.PublicKey)
assert.NoError(t, err)

var s = dsseSigner{
pk: privk,
}

testcases := []struct {
count int
fail bool
}{
{
count: 0,
fail: true,
},
{
count: 1,
fail: false,
},
{
count: 2,
fail: true,
},
}

for _, tc := range testcases {
t.Run(fmt.Sprintf("Test DSSE verify with %d signatures", tc.count),
func(t *testing.T) {
var e *dsse.Envelope

if tc.count == 0 {
// Need to create the envelope manually
e = &dsse.Envelope{
PayloadType: "test-payload-type",
// b64(test-payload)
Payload: "dGVzdC1wYXlsb2Fk",
}
} else {
var signers []dsse.Signer

for i := 0; i < tc.count; i++ {
signers = append(signers, &s)
}

es, err := dsse.NewEnvelopeSigner(signers...)
assert.NoError(t, err)
e, err = es.SignPayload(context.Background(),
"test-payload-type",
[]byte("test-payload"))
assert.NoError(t, err)
}
sigver, err := signature.LoadECDSAVerifier(pubk, crypto.SHA256)
assert.NoError(t, err)
err = verifyEnvelope(
sigver,
&envelopeContent{
e: e,
},
)

if tc.fail {
assert.True(t, errors.Is(err, ErrDSSEInvalidSignatureCount))
} else {
assert.NoError(t, err)
}
})
}
}
11 changes: 10 additions & 1 deletion pkg/verify/signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import (
"github.com/sigstore/sigstore/pkg/signature/options"
)

var ErrDSSEInvalidSignatureCount = errors.New("exactly one signature is required")

func VerifySignature(sigContent SignatureContent, verificationContent VerificationContent, trustedMaterial root.TrustedMaterial) error { // nolint: revive
var verifier signature.Verifier
var err error
Expand Down Expand Up @@ -100,6 +102,13 @@ func getSignatureVerifier(verificationContent VerificationContent, tm root.Trust
}

func verifyEnvelope(verifier signature.Verifier, envelope EnvelopeContent) error {
dsseEnv := envelope.RawEnvelope()

// A DSSE envelope in a Sigstore bundle MUST only contain one
// signature, even though DSSE is more permissive.
if len(dsseEnv.Signatures) != 1 {
return ErrDSSEInvalidSignatureCount
}
pub, err := verifier.PublicKey()
if err != nil {
return fmt.Errorf("could not fetch verifier public key: %w", err)
Expand All @@ -113,7 +122,7 @@ func verifyEnvelope(verifier signature.Verifier, envelope EnvelopeContent) error
return fmt.Errorf("could not load envelope verifier: %w", err)
}

_, err = envVerifier.Verify(context.TODO(), envelope.RawEnvelope())
_, err = envVerifier.Verify(context.TODO(), dsseEnv)
if err != nil {
return fmt.Errorf("could not verify envelope: %w", err)
}
Expand Down
13 changes: 13 additions & 0 deletions pkg/verify/signed_entity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package verify_test

import (
"errors"
"strings"
"testing"
"unicode"
Expand Down Expand Up @@ -367,3 +368,15 @@ func ensureKeysBeginWithLowercase(t *testing.T, obj interface{}) {
}
}
}

func TestSigstoreBundle2Sig(t *testing.T) {
tr := data.PublicGoodTrustedMaterialRoot(t)
entity := data.SigstoreBundle2Sig(t)

v, err := verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(1), verify.WithObserverTimestamps(1))
assert.NoError(t, err)

res, err := v.Verify(entity, SkipArtifactAndIdentitiesPolicy)
assert.True(t, errors.Is(err, verify.ErrDSSEInvalidSignatureCount))
assert.Nil(t, res)
}

0 comments on commit a0f4538

Please sign in to comment.