From 96c8fe6b3e3cf6bc5cd034241c01254cb311b123 Mon Sep 17 00:00:00 2001 From: Zach Steindler Date: Mon, 10 Jun 2024 11:53:36 -0400 Subject: [PATCH] Update README and documentation for signing support (#203) Also switch binaries to use Transparency interface instead of Rekor directly. Signed-off-by: Zach Steindler --- Makefile | 2 +- README.md | 18 +++-- cmd/conformance/main.go | 2 +- docs/signing.md | 69 +++++++++++++++++++ docs/verification.md | 2 +- .../hello_world.txt | 0 .../intoto.txt | 0 .../{signing => sigstore-go-signing}/main.go | 2 +- pkg/sign/signer.go | 10 +-- 9 files changed, 86 insertions(+), 19 deletions(-) create mode 100644 docs/signing.md rename examples/{signing => sigstore-go-signing}/hello_world.txt (100%) rename examples/{signing => sigstore-go-signing}/intoto.txt (100%) rename examples/{signing => sigstore-go-signing}/main.go (97%) diff --git a/Makefile b/Makefile index 24c94eff..f1a5da23 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ build: .PHONY: build-examples build-examples: go build -C ./examples/oci-image-verification $(LDFLAGS) -o oci-image-verification . - go build -C ./examples/signing $(LDFLAGS) -o sigstore-signing . + go build -C ./examples/sigstore-go-signing $(LDFLAGS) -o sigstore-go-signing . .PHONY: test test: diff --git a/README.md b/README.md index 0ceed097..4c839dbe 100644 --- a/README.md +++ b/README.md @@ -7,28 +7,26 @@ A client library for [Sigstore](https://www.sigstore.dev/), written in Go. [![e2e-tests](https://github.com/sigstore/sigstore-go/actions/workflows/build.yml/badge.svg)](https://github.com/sigstore/sigstore-go/actions/workflows/build.yml) Features: -- Verification of [Sigstore bundles](https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_bundle.proto) compliant with Sigstore Client Spec +- Signing and verification of [Sigstore bundles](https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_bundle.proto) compliant with Sigstore Client Spec - Verification of raw Sigstore signatures by creating bundles for them (see [conformance tests](cmd/conformance/main.go) for example) -- Timestamp Authority (TSA) verification -- Rekor (Artifact Transparency Log) verificaton (offline or online) +- Signing and verifying with a Timestamp Authority (TSA) +- Signing and verifying (offline or online) with Rekor (Artifact Transparency Log) - Structured verification results including certificate metadata - TUF support -- Support for custom [trusted root](https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_trustroot.proto) -- Basic CLI +- Verification support for custom [trusted root](https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_trustroot.proto) +- Basic CLI and examples -Unsupported at this time: -- Signing -- KMS +There is not built-in support for signing with a KMS or other bring-your-own-key; however you can easily add support by implementing your own version of the interface `pkg/sign/keys.go:Keypair`. For an example of how to use this library, see [the verification documentation](./docs/verification.md), the CLI [cmd/sigstore-go](./cmd/sigstore-go/main.go), or the CLI examples below. Note that the CLI is to demonstrate how to use the library, and not intended as a fully-featured Sigstore CLI like [cosign](https://github.com/sigstore/cosign). ## Background -Sigstore already has a canonical Go client implementation, [cosign](https://github.com/sigstore/cosign), which was developed with a focus on container image signing/verification. It has a rich CLI and a long legacy of features and development. `sigstore-go` is a more minimal and friendly API for integrating Go code with Sigstore, with a focus on the newly specified data structures in [sigstore/protobuf-specs](https://github.com/sigstore/protobuf-specs). `sigstore-go` attempts to minimize the dependency tree for simple verification tasks, omitting KMS support and container image verification, and we intend to refactor parts of `cosign` to depend on `sigstore-go`. +Sigstore already has a canonical Go client implementation, [cosign](https://github.com/sigstore/cosign), which was developed with a focus on container image signing/verification. It has a rich CLI and a long legacy of features and development. `sigstore-go` is a more minimal and friendly API for integrating Go code with Sigstore, with a focus on the newly specified data structures in [sigstore/protobuf-specs](https://github.com/sigstore/protobuf-specs). `sigstore-go` attempts to minimize the dependency tree for simple signing and verification tasks, omitting KMS support and container image verification, and we intend to refactor parts of `cosign` to depend on `sigstore-go`. ## Status -`sigstore-go` is currently pre-1.0 and therefore does not guarantee a stable API. It does however pass the [`sigstore-conformance`](https://github.com/sigstore/sigstore-conformance) verification test suite, and verification correctness is taken very seriously. +`sigstore-go` is currently beta, and may have minor API changes before the 1.0.0 release. It does however pass the [`sigstore-conformance`](https://github.com/sigstore/sigstore-conformance) signing and verification test suite, and correctness is taken very seriously. ## Documentation diff --git a/cmd/conformance/main.go b/cmd/conformance/main.go index 0a4e7819..45a5d497 100644 --- a/cmd/conformance/main.go +++ b/cmd/conformance/main.go @@ -147,7 +147,7 @@ func signBundle(withRekor bool) (*protobundle.Bundle, error) { Timeout: timeout, LibraryVersion: Version, } - signingOptions.Rekors = append(signingOptions.Rekors, sign.NewRekor(rekorOpts)) + signingOptions.TransparencyLogs = append(signingOptions.TransparencyLogs, sign.NewRekor(rekorOpts)) } if withRekor { diff --git a/docs/signing.md b/docs/signing.md new file mode 100644 index 00000000..404545f4 --- /dev/null +++ b/docs/signing.md @@ -0,0 +1,69 @@ +# Signing using `sigstore-go` + +This document will walk you through using `sigstore-go` to generate a Sigstore bundle. + +## Requirements + +- Unix-compatible OS +- [Go 1.21](https://go.dev/doc/install) + +## Installation + +Clone this repository and run the following command: + +```shell +$ make examples +$ go install ./examples/sigstore-go-signing +``` + +## Bundle + +This library generates [Sigstore bundles](https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_bundle.proto) encoded as JSON, which are composed of raw message signatures or attestations, combined with certificates, transparency log data, signed timestamps, and other metadata to form a single, verifiable artifact. + +## Trusted Root + +You can optionally provide a [trusted root](https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_trustroot.proto), containing root/intermediate certificate of the Fulcio/TSA/Rekor instances used to sign the bundle, which the signer will use to verify the bundle before returning it. Because the trusted root content changes as key material is rotated, the example uses TUF to fetch an up-to-date trusted root. + +## Abstractions + +The library provides enough implementation to sign content with the Sigstore public good instance. It also provides interfaces so that in can be used in a wide variety of private deployments or other use-cases: + +- `Content` to represent what it is you want to be signed. Implementations are provided for `PlainData` or `DSSE` encoded content. +- `Keypair` for how the content should be signed. A default implementation of `EphemeralKeypair` is provided, intended to be used with Sigstore Fulcio, with a ECDSA key using the P-256 curve. If you need a different key type, or a durable key, or support for a KMS, you can implement the `Keypair` interface. +- `CertificateProvider` for obtaining a code signing certificate. A default `Fulcio` implementation is provided. +- `Transparency` for obtaining a transparency log entry. A default `Rekor` implementation is provided. + +Although not an interface, there is also a `TimestampAuthority` that you can use to corroborate when the signing certificate was obtained. + +## Interface + +Looking at the `Bundle()` function and its associated `BundleOptions`, you can see what is required to generate a bundle with signed content: + +- `Content` that represents what it is you want to be signed +- `Keypair` for what to use to sign the content + +And then optionally: + +- A `CertificateProvider` +- One or more `TimestampAuthorities` +- One or more `Transparency` log entry providers +- `TrustedRoot` material to verify the bundle before returning it + +See [sigstore-go-signing](../examples/sigstore-go-signing/main.go) for an example of how to use this interface. + +## Examples + +A very basic example of signing with a provided keypair with a signed timestamp: + +```bash +$ sigstore-go-signing -tsa examples/sigstore-go-signing/hello_world.txt +Using public key: + +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEY51jsbL5o8ge+FAcxpYjQeDEe1n5 +WK+8DzwCkLLPJHISIvsiS93PTVDPpmbAASOl2Y4ZHqRsxb3aPMaQmN4sew== +-----END PUBLIC KEY----- + + +{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json", "verificationMaterial":{"publicKey":{"hint":"WpMWlwBZxXlzAjt/fxK4Nd9VYm7PH3cnr3TTVmdQ5SQ="}, "timestampVerificationData":{"rfc3161Timestamps":[{"signedTimestamp":"MIIC0jADAgEAMIICyQYJKoZIhvcNAQcCoIICujCCArYCAQMxDTALBglghkgBZQMEAgIwgbwGCyqGSIb3DQEJEAEEoIGsBIGpMIGmAgEBBgkrBgEEAYO/MAIwMTANBglghkgBZQMEAgEFAAQgkaG6xajtAtsOoLy40vPZp+nDZRIWKnES2RqHDU7rmf4CFQC3ipDzPRUatDNLHxecg+XOCLSvWRgPMjAyNDA2MDcxNDExNTNaMAMCAQGgNqQ0MDIxFTATBgNVBAoTDEdpdEh1YiwgSW5jLjEZMBcGA1UEAxMQVFNBIFRpbWVzdGFtcGluZ6AAMYIB3zCCAdsCAQEwSjAyMRUwEwYDVQQKEwxHaXRIdWIsIEluYy4xGTAXBgNVBAMTEFRTQSBpbnRlcm1lZGlhdGUCFDz4J+p3q9T1P++QkPutn3Qbms2gMAsGCWCGSAFlAwQCAqCCAQUwGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNDA2MDcxNDExNTNaMD8GCSqGSIb3DQEJBDEyBDCBYMVKhy7Mh+uUo0ycmn+Cl4swv4Z2t0TVuI+v0iNFJ4KTxB94bEALa2aaJZhNURswgYcGCyqGSIb3DQEJEAIvMXgwdjB0MHIEIHlI8iapfsPTNvwCoQbp1RaqmufUNHq7MbJ0CRlZCRsHME4wNqQ0MDIxFTATBgNVBAoTDEdpdEh1YiwgSW5jLjEZMBcGA1UEAxMQVFNBIGludGVybWVkaWF0ZQIUPPgn6ner1PU/75CQ+62fdBuazaAwCgYIKoZIzj0EAwMEaDBmAjEA3LlfE26IGKXCWgfGOxohAcz7/IlnRntEOI0lmknn5TgPa+VWfs1SqUBOKrYXPZutAjEAoNgsnlcSraDhAqdnv0llxvQLVEvZDBny1I1UgrtsAEnks9LWtH67bdwYrHRsCBAW"}]}}, "messageSignature":{"messageDigest":{"algorithm":"SHA2_256", "digest":"uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek="}, "signature":"MEUCIQDlK0ZyYsGeh1NC7MiAL+mT54jdQakhelpy5Vz5MmWEbgIgRn51DlDW6rgIY7KMUq+7sC8BjZYzh4QtmcPjJiF4RSA="}} +``` diff --git a/docs/verification.md b/docs/verification.md index 55ae12f3..724a5e3e 100644 --- a/docs/verification.md +++ b/docs/verification.md @@ -20,7 +20,7 @@ go install ./cmd/... This library supports verifying [Sigstore bundles](https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_bundle.proto) encoded as JSON, which are composed of raw message signatures or attestations, combined with certificates, transparency log data, signed timestamps, and other metadata to form a single, verifiable artifact. -Signing is not currently supported by this library, but you may use [`sigstore-js`](https://github.com/sigstore/sigstore-js) or [`sigstore-python`](https://github.com/sigstore/sigstore-python) to generate/sign a bundle and verify it with this library. +See the [signing documentation](signing.md) for how to generate/sign a bundle. An example Sigstore bundle is included in this distribution at [`examples/bundle-provenance.json`](../examples/bundle-provenance.json). diff --git a/examples/signing/hello_world.txt b/examples/sigstore-go-signing/hello_world.txt similarity index 100% rename from examples/signing/hello_world.txt rename to examples/sigstore-go-signing/hello_world.txt diff --git a/examples/signing/intoto.txt b/examples/sigstore-go-signing/intoto.txt similarity index 100% rename from examples/signing/intoto.txt rename to examples/sigstore-go-signing/intoto.txt diff --git a/examples/signing/main.go b/examples/sigstore-go-signing/main.go similarity index 97% rename from examples/signing/main.go rename to examples/sigstore-go-signing/main.go index dd396077..fe7034d3 100644 --- a/examples/signing/main.go +++ b/examples/sigstore-go-signing/main.go @@ -137,7 +137,7 @@ func main() { Retries: 1, LibraryVersion: Version, } - opts.Rekors = append(opts.Rekors, sign.NewRekor(rekorOpts)) + opts.TransparencyLogs = append(opts.TransparencyLogs, sign.NewRekor(rekorOpts)) } bundle, err := sign.Bundle(content, keypair, opts) diff --git a/pkg/sign/signer.go b/pkg/sign/signer.go index fde4c42d..dfca1226 100644 --- a/pkg/sign/signer.go +++ b/pkg/sign/signer.go @@ -45,7 +45,7 @@ type BundleOptions struct { // Optional list of Rekor instances to get transparency log entry from. // // Supports hashedrekord and dsse entry types. - Rekors []*Rekor + TransparencyLogs []Transparency // Optional context for retrying network requests Context context.Context // Optional trusted root to verify signed bundle @@ -127,15 +127,15 @@ func Bundle(content Content, keypair Keypair, opts BundleOptions) (*protobundle. verifierOptions = append(verifierOptions, verify.WithSignedTimestamps(len(opts.TimestampAuthorities))) } - if len(opts.Rekors) > 0 { - for _, rekor := range opts.Rekors { - err = rekor.GetTransparencyLogEntry(verifierPEM, bundle) + if len(opts.TransparencyLogs) > 0 { + for _, transparency := range opts.TransparencyLogs { + err = transparency.GetTransparencyLogEntry(verifierPEM, bundle) if err != nil { return nil, err } } - verifierOptions = append(verifierOptions, verify.WithTransparencyLog(len(opts.Rekors)), verify.WithIntegratedTimestamps(len(opts.Rekors))) + verifierOptions = append(verifierOptions, verify.WithTransparencyLog(len(opts.TransparencyLogs)), verify.WithIntegratedTimestamps(len(opts.TransparencyLogs))) } if opts.TrustedRoot != nil && len(verifierOptions) > 0 {