forked from sigstore/cosign
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bundle TUF timestamp with signature on signing (sigstore#1294)
* Bundle TUF timestamp with signature on signing This updates the code to support adding the TUF timestamp to the OCI signature. Changes to pkg/oci add support for reading and saving the timestamp by annotation key. Changes to the TUF client add putting the timestamp in memory on client initialization, so callers can access the timestamp. Signed-off-by: Hayden Blauzvern <hblauzvern@google.com> * Add TUF timestamp to OCI signature on sign This adds the TUF timestamp to the Fulcio and Rekor signers. Both are necessary since each relies on TUF metadata. If both signers are used, the latter one will overwrite the TUF timestamp. I also added a basic mock Rekor client for tests. A number of methods are not implemented yet. Signed-off-by: Hayden Blauzvern <hblauzvern@google.com> * Add license Signed-off-by: Hayden Blauzvern <hblauzvern@google.com> * Move timestamp to TUF package Signed-off-by: Hayden Blauzvern <hblauzvern@google.com> * Update TUF client to persist local store Signed-off-by: Hayden Blauzvern <hblauzvern@google.com>
- Loading branch information
1 parent
be769b5
commit 4749356
Showing
19 changed files
with
697 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
// Copyright 2022 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 fulcio | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"crypto" | ||
"encoding/base64" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/sigstore/cosign/internal/pkg/cosign/payload" | ||
"github.com/sigstore/cosign/pkg/cosign" | ||
"github.com/sigstore/sigstore/pkg/signature" | ||
) | ||
|
||
var ( | ||
testCertBytes = []byte(` | ||
-----BEGIN CERTIFICATE----- | ||
MIICjzCCAhSgAwIBAgITV2heiswW9YldtVEAu98QxDO8TTAKBggqhkjOPQQDAzAq | ||
MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIx | ||
MDkxNDE5MTI0MFoXDTIxMDkxNDE5MzIzOVowADBZMBMGByqGSM49AgEGCCqGSM49 | ||
AwEHA0IABMF1AWZcfvubslc4ABNnvGbRjm6GWVHxrJ1RRthTHMCE4FpFmiHQBfGt | ||
6n80DqszGj77Whb35O33+Dal4Y2po+CjggFBMIIBPTAOBgNVHQ8BAf8EBAMCB4Aw | ||
EwYDVR0lBAwwCgYIKwYBBQUHAwMwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU340G | ||
3G1ozVNmFC5TBFV0yNuouvowHwYDVR0jBBgwFoAUyMUdAEGaJCkyUSTrDa5K7UoG | ||
0+wwgY0GCCsGAQUFBwEBBIGAMH4wfAYIKwYBBQUHMAKGcGh0dHA6Ly9wcml2YXRl | ||
Y2EtY29udGVudC02MDNmZTdlNy0wMDAwLTIyMjctYmY3NS1mNGY1ZTgwZDI5NTQu | ||
c3RvcmFnZS5nb29nbGVhcGlzLmNvbS9jYTM2YTFlOTYyNDJiOWZjYjE0Ni9jYS5j | ||
cnQwOAYDVR0RAQH/BC4wLIEqa2V5bGVzc0BkaXN0cm9sZXNzLmlhbS5nc2Vydmlj | ||
ZWFjY291bnQuY29tMAoGCCqGSM49BAMDA2kAMGYCMQDcH9cdkxW6ugsbPHqX9qrM | ||
wlMaprcwnlktS3+5xuABr5icuqwrB/Fj5doFtS7AnM0CMQD9MjSaUmHFFF7zoLMx | ||
uThR1Z6JuA21HwxtL3GyJ8UQZcEPOlTBV593HrSAwBhiCoY= | ||
-----END CERTIFICATE----- | ||
`) | ||
testChainBytes = []byte(` | ||
-----BEGIN CERTIFICATE----- | ||
MIIB+DCCAX6gAwIBAgITNVkDZoCiofPDsy7dfm6geLbuhzAKBggqhkjOPQQDAzAq | ||
MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIx | ||
MDMwNzAzMjAyOVoXDTMxMDIyMzAzMjAyOVowKjEVMBMGA1UEChMMc2lnc3RvcmUu | ||
ZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLSy | ||
A7Ii5k+pNO8ZEWY0ylemWDowOkNa3kL+GZE5Z5GWehL9/A9bRNA3RbrsZ5i0Jcas | ||
taRL7Sp5fp/jD5dxqc/UdTVnlvS16an+2Yfswe/QuLolRUCrcOE2+2iA5+tzd6Nm | ||
MGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYE | ||
FMjFHQBBmiQpMlEk6w2uSu1KBtPsMB8GA1UdIwQYMBaAFMjFHQBBmiQpMlEk6w2u | ||
Su1KBtPsMAoGCCqGSM49BAMDA2gAMGUCMH8liWJfMui6vXXBhjDgY4MwslmN/TJx | ||
Ve/83WrFomwmNf056y1X48F9c4m3a3ozXAIxAKjRay5/aj/jsKKGIkmQatjI8uup | ||
Hr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ== | ||
-----END CERTIFICATE----- | ||
`) | ||
) | ||
|
||
func mustGetNewSigner(t *testing.T) signature.Signer { | ||
t.Helper() | ||
priv, err := cosign.GeneratePrivateKey() | ||
if err != nil { | ||
t.Fatalf("cosign.GeneratePrivateKey() failed: %v", err) | ||
} | ||
s, err := signature.LoadECDSASignerVerifier(priv, crypto.SHA256) | ||
if err != nil { | ||
t.Fatalf("signature.LoadECDSASignerVerifier(key, crypto.SHA256) failed: %v", err) | ||
} | ||
return s | ||
} | ||
|
||
func TestSigner(t *testing.T) { | ||
// Need real cert and chain | ||
payloadSigner := payload.NewSigner(mustGetNewSigner(t)) | ||
testSigner := NewSigner(payloadSigner, testCertBytes, testChainBytes) | ||
|
||
testPayload := "test payload" | ||
|
||
ociSig, pub, err := testSigner.Sign(context.Background(), strings.NewReader(testPayload)) | ||
if err != nil { | ||
t.Fatalf("Sign() returned error: %v", err) | ||
} | ||
|
||
// Verify that the OCI signature contains a cert, chain and timestamp. | ||
cert, err := ociSig.Cert() | ||
if err != nil { | ||
t.Fatalf("ociSig.Cert() returned error: %v", err) | ||
} | ||
if cert == nil { | ||
t.Fatal("ociSig.Cert() missing certificate, got nil") | ||
} | ||
chain, err := ociSig.Chain() | ||
if err != nil { | ||
t.Fatalf("ociSig.Chain() returned error: %v", err) | ||
} | ||
if len(chain) != 1 { | ||
t.Fatalf("ociSig.Chain() expected to be of length 1, got %d", len(chain)) | ||
} | ||
if chain[0] == nil { | ||
t.Fatal("ociSig.Chain()[0] missing certificate, got nil") | ||
} | ||
timestamp, err := ociSig.Timestamp() | ||
if err != nil { | ||
t.Fatalf("ociSig.Timestamp() returned error: %v", err) | ||
} | ||
if timestamp == nil { | ||
t.Fatal("ociSig.Timestamp() missing TUF timestamp, got nil") | ||
} | ||
|
||
// Verify that the wrapped signer was called. | ||
verifier, err := signature.LoadVerifier(pub, crypto.SHA256) | ||
if err != nil { | ||
t.Fatalf("signature.LoadVerifier(pub) returned error: %v", err) | ||
} | ||
b64Sig, err := ociSig.Base64Signature() | ||
if err != nil { | ||
t.Fatalf("ociSig.Base64Signature() returned error: %v", err) | ||
} | ||
sig, err := base64.StdEncoding.DecodeString(b64Sig) | ||
if err != nil { | ||
t.Fatalf("base64.StdEncoding.DecodeString(b64Sig) returned error: %v", err) | ||
} | ||
gotPayload, err := ociSig.Payload() | ||
if err != nil { | ||
t.Fatalf("ociSig.Payload() returned error: %v", err) | ||
} | ||
if string(gotPayload) != testPayload { | ||
t.Errorf("ociSig.Payload() returned %q, wanted %q", string(gotPayload), testPayload) | ||
} | ||
if err = verifier.VerifySignature(bytes.NewReader(sig), bytes.NewReader(gotPayload)); err != nil { | ||
t.Errorf("VerifySignature() returned error: %v", err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// Copyright 2022 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 rekor | ||
|
||
import ( | ||
"github.com/go-openapi/runtime" | ||
"github.com/sigstore/rekor/pkg/generated/client/entries" | ||
"github.com/sigstore/rekor/pkg/generated/models" | ||
) | ||
|
||
// Client that implements entries.ClientService for Rekor | ||
// To use: | ||
// var mClient client.Rekor | ||
// mClient.entries = &MockEntriesClient{} | ||
type MockEntriesClient struct { | ||
} | ||
|
||
func (m *MockEntriesClient) CreateLogEntry(params *entries.CreateLogEntryParams, opts ...entries.ClientOption) (*entries.CreateLogEntryCreated, error) { | ||
return &entries.CreateLogEntryCreated{ | ||
ETag: "", | ||
Location: "", | ||
Payload: map[string]models.LogEntryAnon{ | ||
"sdf": { | ||
Attestation: &models.LogEntryAnonAttestation{}, | ||
Body: nil, | ||
IntegratedTime: new(int64), | ||
LogID: new(string), | ||
LogIndex: new(int64), | ||
Verification: &models.LogEntryAnonVerification{}, | ||
}, | ||
}, | ||
}, nil | ||
} | ||
|
||
// TODO: Implement mock | ||
func (m *MockEntriesClient) GetLogEntryByIndex(params *entries.GetLogEntryByIndexParams, opts ...entries.ClientOption) (*entries.GetLogEntryByIndexOK, error) { | ||
return nil, nil | ||
} | ||
|
||
// TODO: Implement mock | ||
func (m *MockEntriesClient) GetLogEntryByUUID(params *entries.GetLogEntryByUUIDParams, opts ...entries.ClientOption) (*entries.GetLogEntryByUUIDOK, error) { | ||
return nil, nil | ||
} | ||
|
||
// TODO: Implement mock | ||
func (m *MockEntriesClient) SearchLogQuery(params *entries.SearchLogQueryParams, opts ...entries.ClientOption) (*entries.SearchLogQueryOK, error) { | ||
return nil, nil | ||
} | ||
|
||
// TODO: Implement mock | ||
func (m *MockEntriesClient) SetTransport(transport runtime.ClientTransport) { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
// Copyright 2022 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 rekor | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"crypto" | ||
"encoding/base64" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/sigstore/cosign/internal/pkg/cosign/payload" | ||
"github.com/sigstore/cosign/pkg/cosign" | ||
"github.com/sigstore/rekor/pkg/generated/client" | ||
"github.com/sigstore/sigstore/pkg/signature" | ||
) | ||
|
||
func mustGetNewSigner(t *testing.T) signature.Signer { | ||
t.Helper() | ||
priv, err := cosign.GeneratePrivateKey() | ||
if err != nil { | ||
t.Fatalf("cosign.GeneratePrivateKey() failed: %v", err) | ||
} | ||
s, err := signature.LoadECDSASignerVerifier(priv, crypto.SHA256) | ||
if err != nil { | ||
t.Fatalf("signature.LoadECDSASignerVerifier(key, crypto.SHA256) failed: %v", err) | ||
} | ||
return s | ||
} | ||
|
||
func TestSigner(t *testing.T) { | ||
// Need real cert and chain | ||
payloadSigner := payload.NewSigner(mustGetNewSigner(t)) | ||
|
||
// Mock out Rekor client | ||
var mClient client.Rekor | ||
mClient.Entries = &MockEntriesClient{} | ||
|
||
testSigner := NewSigner(payloadSigner, &mClient) | ||
|
||
testPayload := "test payload" | ||
|
||
ociSig, pub, err := testSigner.Sign(context.Background(), strings.NewReader(testPayload)) | ||
if err != nil { | ||
t.Fatalf("Sign() returned error: %v", err) | ||
} | ||
|
||
// Verify that the OCI signature contains a timestamp. | ||
timestamp, err := ociSig.Timestamp() | ||
if err != nil { | ||
t.Fatalf("ociSig.Timestamp() returned error: %v", err) | ||
} | ||
if timestamp == nil { | ||
t.Fatal("ociSig.Timestamp() missing TUF timestamp, got nil") | ||
} | ||
|
||
// Verify that the wrapped signer was called. | ||
verifier, err := signature.LoadVerifier(pub, crypto.SHA256) | ||
if err != nil { | ||
t.Fatalf("signature.LoadVerifier(pub) returned error: %v", err) | ||
} | ||
b64Sig, err := ociSig.Base64Signature() | ||
if err != nil { | ||
t.Fatalf("ociSig.Base64Signature() returned error: %v", err) | ||
} | ||
sig, err := base64.StdEncoding.DecodeString(b64Sig) | ||
if err != nil { | ||
t.Fatalf("base64.StdEncoding.DecodeString(b64Sig) returned error: %v", err) | ||
} | ||
gotPayload, err := ociSig.Payload() | ||
if err != nil { | ||
t.Fatalf("ociSig.Payload() returned error: %v", err) | ||
} | ||
if string(gotPayload) != testPayload { | ||
t.Errorf("ociSig.Payload() returned %q, wanted %q", string(gotPayload), testPayload) | ||
} | ||
if err = verifier.VerifySignature(bytes.NewReader(sig), bytes.NewReader(gotPayload)); err != nil { | ||
t.Errorf("VerifySignature() returned error: %v", err) | ||
} | ||
} |
Oops, something went wrong.