1. Project Overview & Quickstart (sigstore/cosign_bundle_create-from-container)
Technical Documentation: sigstore/cosign_bundle_create-from-container
โน๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference
cosign_bundle_create-from-container
Agent Skills & Rules
- Discovered 21 agent rule(s) and prompt configurations.
--- METRICS ---
- Files Extracted: 1
- Estimated Token Budget: ~31 tokens
- Canonical Reference: https://codewiki.google/github.com/sigstore/cosign_bundle_create-from-container
2. Official Technical Reference & Guides (sigstore/docs)
File: archetypes/docs/lorem/ipsum/index.md
title: "Ipsum"
lead: ""
date: 2022-01-25T14:41:39+01:00
lastmod: 2022-01-25T14:41:39+01:00
draft: false
images: []
type: docs
menu:
{{ .Section }}:
parent: "lorem"
identifier: "{{ .Name }}-{{ delimit (shuffle (split (md5 .Name) "" )) "" }}"
weight: 100
toc: true
File: archetypes/docs/lorem/_index.md
title: "Lorem" lead: "" date: 2022-01-25T14:41:21+01:00 lastmod: 2022-01-25T14:41:21+01:00 draft: false images: [] type: docs
File: archetypes/docs/_index.md
title: "Docs" lead: "" date: 2022-01-25T14:40:56+01:00 lastmod: 2022-01-25T14:40:56+01:00 draft: false images: [] type: docs
File: content/en/logging/CLI.md
type: docs category: Transparency Log title: CLI weight: 1825
The following guide is targeted towards developers / software maintainers who would like to make a provenance entry into the Rekor transparency log.
The steps outlined below will show how to sign your software and use the Rekor CLI to make and verify an entry. It uses GPG to demonstrate, but other types are outlined in the [Signing and Uploading Other Types]({{< relref "logging/sign-upload">}}) page.
Prerequisites
You need to have Rekor CLI installed. See the [Installation]({{< relref "logging/installation">}}) page for instructions.
Sign your release
Before using Rekor, you are required to sign your release. The following example illustrates this using GPG.
You may use either armored or plain binary:
gpg --armor -u [email protected] --output mysignature.asc --detach-sig myrelease.tar.gzYou will also need to export your public key
gpg --export --armor "[email protected]" > mypublickey.keyUpload an entry to Rekor
The upload command sends your public key / signature and artifact URL to the Rekor transparency log.
rekor-cli upload --rekor_server https://rekor.sigstore.dev --signature <artifact_signature> --public-key <your_public_key> --artifact <url_to_artifact>|<local_path_artifact>The Rekor command will first verify your public key and signature and download a local copy of the artifact. Then it will validate the artifact signing (no access to your private key is required).
If the validations above pass correctly, the entry will be made to Rekor and an entry URL will be returned:
Created entry at: https://rekor.sigstore.dev/api/v1/log/entries/b08416d417acdb0610d4a030d8f697f9d0a718024681a00fa0b9ba67072a38b5This URL contains the UUID entry / merkle tree hash (in the above case b08416d417acdb0610d4a030d8f697f9d0a718024681a00fa0b9ba67072a38b5).
Verify Proof of Entry
The verify command allows you to send a public key / signature and artifact to the Rekor transparency log for verification of entry.
You would typically use this command as a means to verify an 'inclusion proof' showing that your artifact is stored within the transparency log.
rekor-cli verify --rekor_server <rekor_url> --signature <artifact-signature> --public-key <your_public_key> --artifact <url_to_artifact>|<local_path_artifact>Note that alternatively you can use a local artifact path with
--artifact.
Get Entry
An entry in the log can be retrieved by using the get command with either the log index or the artifact UUID:
rekor-cli get --rekor_server https://rekor.sigstore.dev --log-index <log-index>rekor-cli get --rekor_server https://rekor.sigstore.dev --uuid <uuid>Log Info
The loginfo command retrieves the public key of the transparency log (unless already declared within the client ~/.rekor/rekor.yaml) and then uses this public key to verify the signature on the signed tree head.
rekor-cli loginfo --rekor_server https://rekor.sigstore.devSearch
If running a redis instance within Rekor, the search command performs a redis lookup using a file or a public key.
This command requires one of an artifact, a public key, or a SHA hash (should be prefixed by sha256:).
rekor-cli search --rekor_server https://rekor.sigstore.dev --[artifact|public-key|sha]For example:
rekor-cli search --rekor_server https://rekor.sigstore.dev --sha sha256:e2e90d1a25f90a3156a27f00f3a4179578e3132ed4f010dc3498d09175b6071aFile: content/en/logging/event_stream.md
type: docs category: Transparency Log menuTitle: Event Stream title: Using the Rekor Event Stream weight: 1850
The public Rekor instance provides an event stream of new entries added to the transparency log using GCP Pub/Sub. This can be used to monitor the log in real-time for events you are interested in.
Pub/Sub details
Important: Pub/Sub usage is not free. Please familiarize yourself with the pricing before proceeding.
Tip: You can avoid paying egress network costs by placing the workload that processes events from the subscription in the same region as the topic.
The details needed to subscribe to the topic are below. Any authenticated GCP principal can create subscriptions to the topic.
| Item | Value |
|---|---|
| GCP Project ID | project-rekor |
| Pub/Sub Topic ID | new-entry |
| Pub/Sub Topic Region | us-central1 |
Event format
The events are published in both the JSON and Protobuf formats of the CloudEvents specificaion. The data field is always a serialized TransparencyLogEntry proto message. For JSON formatted events, this follows the JSON serialization specification for Protobufs.
All published events have attributes attached to them. Attributes that are custom to Rekor and not a part of the CloudEvents specification are prefixed by rekor_. You can add server-side filters based on these attributes to automatically acknowledge messages you are not interested in to reduce costs.
| Attribute | Value |
|---|---|
datacontenttype |
application/json or application/protobuf |
id |
The unique identifier for the entry in the Rekor log. |
rekor_entry_kind |
The kind of the Rekor entry (hashedrekord, intoto, etc.) |
rekor_signing_subjects |
A sorted, comma-delimeted list of the principals that signed the entry. |
source |
/createLogEntry |
specversion |
1.0 |
time |
The timestamp the entry was added to the log. |
type |
dev.sigstore.rekor.events.v1.NewEntry |
Example usage
In this example scenario, we want to monitor all log entries added by GitHub Actions CI workers in the Sigstore GitHub org. We do this to verify that all CI runs that upload entries to Rekor are from the main branch only. Uploads from other branches could indicate a GitHub Actions misconfiguration that allowed a malicious actor to submit a pull request which triggered a CI run that allowed them to sign their artifacts using our identity.
Setting up the subscription
Note: To perform the steps below, you will need the gcloud CLI.
First, define some environment variables. You may pick any value you like for SUBSCRIPTION_PROJECT_ID and SUBSCRIPTION_ID. The value for SUBSCRIPTION_PROJECT_ID must be globally unique among all GCP projects.
export SUBSCRIPTION_PROJECT_ID="your-gcp-project-id"
export SUBSCRIPTION_ID="new-entry-subscription"
export SUBSCRIPTION_CONTENT_TYPE="application/protobuf"If the project does not exist, you can create it with the command below.
gcloud projects create $SUBSCRIPTION_PROJECT_IDThen, you can create the subscription, filtering on the content type and the signing subjects we want to receive messages about.
export SUBSCRIPTION_FILTER='attributes.datacontenttype = "$SUBSCRIPTION_CONTENT_TYPE" AND hasPrefix(attributes.rekor_signing_subjects, "https://github.com/sigstore/")'gcloud pubsub subscriptions create \
$SUBSCRIPTION_ID \
--project $SUBSCRIPTION_PROJECT_ID \
--topic new-entry \
--topic-project project-rekor \
--message-filter $SUBSCRIPTION_FILTERThe subscription is now set up and we can pull the events
Processing log entries
The first step is to create a program that pulls all messages from the subscription indefinitely. It passes all received messages to a handleMsg function. The code examples below omit imports from Go's standard library for brevity.
import (
"cloud.google.com/go/pubsub"
)
func main() {
ctx, done := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)
defer done()
// Bootstrap the PubSub client.
client, err := pubsub.NewClient(ctx, os.Getenv("SUBSCRIPTION_PROJECT_ID"))
if err != nil {
log.Fatal(err)
}
sub := client.Subscription(os.Getenv("SUBSCRIPTION_ID"))
// Continuously read messages into a channel.
msgs := make(chan *pubsub.Message)
go func() {
for {
err := sub.Receive(ctx, func(_ context.Context, msg *pubsub.Message) {
msgs <- msg
})
if err != nil {
log.Print(err)
}
}
}()
// Handle each message received until the process is terminated.
for {
select {
case msg := <-msgs:
if err := handleMsg(msg); err != nil {
log.Print(err)
}
msg.Ack()
case <-ctx.Done():
return
}
}
}The handleMsg function deserializes the event and then checks the signing subjects.
import (
"google.golang.org/protobuf/proto"
eventspb "github.com/sigstore/protobuf-specs/gen/pb-go/events/v1"
)
func handleMsg(msg *pubsub.Message) error {
event := new(eventspb.CloudEvent)
if err := proto.Unmarshal(msg.Data, event); err != nil {
return fmt.Errorf("unmarshal msg %q: %w", msg.ID, err)
}
// In a real implementation, you should verify the entry before proceeding!
attr, ok := event.Attributes["rekor_signing_subjects"]
if !ok {
return fmt.Errorf("entry %q is missing rekor_signing_subjects", event.GetId())
}
subjects := strings.Split(attr.GetCeString(), ",")
for _, subject := range subjects {
if !strings.HasSuffix(subject, "@refs/heads/main") {
return fmt.Errorf("entry %q was not run from the main branch: %s", event.GetId(), subject)
}
}
return nil
}File: content/en/logging/installation.md
type: docs category: Transparency Log title: Installation weight: 1805
There are several ways to install both the rekor-cli and rekor-server.
Using Go install
If you have Go installed, you can use Go to retrieve the rekor-cli binaries
go install -v github.com/sigstore/rekor/cmd/rekor-cli@latestYou may also do the same for rekor-server, but please note that the Rekor server also requires Trillian and a database. (see below for setup instructions).
go install -v github.com/sigstore/rekor/cmd/rekor-server@latestFrom the release page
Rekor releases are available on the Release page.
Releases are available for both rekor-server and rekor-cli.
Review [Verifying Binaries]({{< relref "logging/verify-release">}}) for details on how to verify Rekor release binaries.
Build Rekor CLI manually
git clone https://github.com/sigstore/rekor.git rekor-cli
cd rekor-cli
make rekor-cli
cp rekor-cli /usr/local/bin/Deploy a Rekor Server manually
There are a few ways you can deploy a Rekor Server:
- We have a docker-compose file available.
- Alternatively, you can build a Rekor server yourself.
Note: The Rekor server manually creates a new Merkle tree (or shard) in the Trillian backend every time it starts up, unless an existing one is specified in via the --trillian_log_server.tlog_id flag. If you are building the server yourself and do not need [sharding]({{< relref "logging/sharding">}}) functionality, you can find the existing tree's TreeID by issuing this client command while the server is running:
CURRENT_TREE_ID=$(rekor-cli loginfo --format json | jq -r .TreeID)
Then pass in this TreeID at the next server startup to tell Rekor to use the same existing tree:
rekor-server serve --trillian_log_server.tlog_id=$CURRENT_TREE_ID
Setting this flag isn't necessary in an environment like docker-compose.
Prerequisites
You will need golang version 1.16 or greater and a $GOPATH set.
You will also need a MySQL compatible database and, if you want to perform fast queries, an instance of redis running.
Otherwise you must pass the --enable_retrieve_api=false flag when running rekor-server in the later steps of this page.
Get Rekor
Grab the Rekor source:
go get -u -t -v github.com/sigstore/rekor/cmd/rekor-server
Should you prefer, you can also
git clone https://github.com/sigstore/rekor.git
Create database and populate tables
Rekor requires a database. We use MariaDB for now (others to be explored later). Install and set up MariaDB on your machine.
dnf install mariadb mariadb-server
systemctl start mariadb
systemctl enable mariadb
mysql_secure_installationThe Rekor directory has a scripts/createdb.sh file that will set up a test database (default user: test; default password: zaphod) and populate the needed tables for Trillian. If youโre just trying out Rekor, keep the DB user name and password the same as in the script (test/zaphod). If you change these, you need to make the changes on Trillianโs side (visit the Trillian repo for details).
cd $GOPATH/pkg/mod/github.com/sigstore/[email protected]/scripts/
sh createdb.shBuild Trillian
You also need to build Trillian, an append-only log:
go get -u -t -v github.com/google/trillian
cd $GOPATH/src/github.com/google/trillian/cmd/trillian_log_server
go build
cp trillian_log_server /usr/local/bin/
cd $GOPATH/src/github.com/google/trillian/cmd/trillian_log_signer
go build
cp trillian_log_signer /usr/local/bin/
cd $GOPATH/src/github.com/google/trillian/cmd/createtree
go build
cp createtree /usr/local/bin/Next, run the Trillian log server:
trillian_log_server --logtostderr ...Run the signer:
trillian_log_signer --logtostderr --force_master --rpc_endpoint=localhost:8190 -http_endpoint=localhost:8191 --batch_size=1000 --sequencer_guard_window=0 --sequencer_interval=200msNote: you can log both to files and to stderr using
--alsologtostderr
Create the tree:
createtree --admin_server=localhost:8090Build the Rekor Server
With Trillian and MariaDB set up, you can now build the Rekor Server:
cd $GOPATH/pkg/mod/github.com/sigstore/[email protected]/cmd/rekor-server
go build -v -o rekor-server
cp rekor-server /usr/local/bin/Start the Rekor Server
rekor-server serve --enable_retrieve_api=false
2020-09-12T16:32:22.705+0100 INFO cmd/root.go:87 Using config file: /Users/lukehinds/go/src/github.com/sigstore/rekor-server/rekor-server.yaml
2020-09-12T16:32:22.705+0100 INFO app/server.go:55 Starting server...
2020-09-12T16:32:22.705+0100 INFO app/server.go:61 Listening on 127.0.0.1:3000If you have a redis server running to enable searching your Rekor server, remove the
enable_reprieve_apiflag
Next Steps
Congratulations! Your local Rekor server is now running. You can interact with it using the [Rekor CLI]({{< relref "logging/cli">}}).
File: content/en/logging/overview.md
type: docs category: Transparency Log menuTitle: Overview title: Rekor weight: 1800
Rekor aims to provide an immutable, tamper-resistant ledger of metadata generated within a software projectโs supply chain.
It enables software maintainers and build systems to record signed metadata to an immutable record. Other parties can then query this metadata, enabling them to make informed decisions on trust and non-repudiation of an objectโs lifecycle.
The Rekor project provides a restful API-based server for validation, and a transparency log for storage. A CLI application is available to make and verify entries, query the log for inclusion proof, integrity verification of the log or retrieval of entries (either by a public key or an artifact).
Rekor fulfils the signature transparency role of Sigstoreโs software signing infrastructure. It can also be run on its own, and itโs designed to be extendable to working with different manifest schemas and PKI tooling.
Usage and installation
You can download and setup the Rekor Server and Rekor CLI by following the instructions on the [Installation]({{< relref "logging/installation">}}) page.
A public instance of Rekor can be found at rekor.sigstore.dev. The public instance offers an SLO of 99.5% availability and is monitored by an oncall team.
Auditing the Public Instance
Rekor is built on top of a verifiable data structure. Auditors can monitor the log for consistency, meaning that the log remains append-only and entries are never mutated or removed.
Verifiers can also monitor the log for their identities.
Learn more about transparency logs here, and about binary transparency here.
There are few options for auditing and monitoring the Rekor log. We've built a monitor that runs on GitHub Actions,
Rekor monitor. Follow the instructions to set up a new repository and use the provided reusable workflow to audit the log. You can also monitor the log for specified identities, though this feature is a work in progress and supports a limited set of identities and entry types.
You can also run omniwitness to
audit the log, built by the team who created Trillian, which provides Rekor's verifiable log.
File: content/en/logging/pluggable-types.md
type: docs category: Transparency Log title: Pluggable Types weight: 1830
Rekor supports pluggable types (aka different schemas) for entries stored in the transparency log. This will allow you to develop your own manifest type in your preferred formatting style (json|yaml|xml).
Currently supported types
The list of currently supported types and their schema is maintained in the repository.
Base schema
The base schema for all types is modeled off of the schema used by Kubernetes and can be found in openapi.yaml as #/definitions/ProposedEntry:
definitions:
ProposedEntry:
type: object
discriminator: kind
properties:
kind:
type: string
required:
- kindThe kind property is a discriminator that is used to differentiate between different pluggable types. Types can have one or more versions of the schema supported concurrently by the same Rekor instance; an example implementation can be seen in rekord.go.
Adding support for a new type
To add a new type (called newType in this example):
Add a new definition in
openapi.yamlthat is a derived type of ProposedEntry (expressed in theallOflist seen below); for example:yamlnewType: type: object description: newType object allOf: - $ref: "#/definitions/ProposedEntry" - properties: version: type: string metadata: type: object additionalProperties: true data: type: object $ref: "pkg/types/newType/newType_schema.json" required: - version - data additionalProperties: falseNote: the
$reffeature can be used to refer to an externally defined JSON schema document; however it is also permitted to describe the entirety of the type in valid Swagger (aka OpenAPI) v2 format withinopenapi.yaml.Create a subdirectory under
pkg/types/with your type name (e.g.newType) as a new Go packageIn this new Go package, define a struct that implements the
TypeImplinterface as defined inpkg/types/types.go:gotype TypeImpl interface { CreateProposedEntry(context.Context, version string, ArtifactProperties) (models.ProposedEntry, error) DefaultVersion() string SupportedVersions() []string IsSupportedVersion(version string) bool UnmarshalEntry(pe models.ProposedEntry) (EntryImpl, error) }CreateProposedEntrycreates an instance of a proposed entry based on the specified API version and the provided artifact propertiesDefaultVersionreturns the default API version string across all types (to be used if a caller does not specify an explicit version)SupportedVersionsreturns the list of all API version strings that can currently be inserted into the log.IsSupportedVersionreturns a boolean denoting whether the specified version could be inserted into the logUnmarshalEntrywill be called with a pointer to a struct that was automatically generated for the type defined inopenapi.yamlby the go-swagger tool used by Rekor- This struct will be defined in the generated file at
pkg/generated/models/newType.go(wherenewTypeis replaced with the name of the type you are adding) - This method should return a pointer to an instance of a struct that implements the
EntryImplinterface as defined inpkg/types/types.go, or anilpointer with an error specified
- This struct will be defined in the generated file at
Also, the
Kindconstant must return the exact same string as you named your new type inopenapi.yaml(e.g. "newType")Also in this Go package, provide an implementation of the
EntryImplinterface as defined inpkg/types/entries.go:gotype EntryImpl interface { APIVersion() string // the supported versions for this implementation IndexKeys() ([]string, error) // the keys that should be added to the external index for this entry Canonicalize(ctx context.Context) ([]byte, error) // marshal the canonical entry to be put into the tlog Unmarshal(e models.ProposedEntry) error // unmarshal the abstract entry into the specific struct for this versioned type CreateFromArtifactProperties(context.Context, ArtifactProperties) (models.ProposedEntry, error) Verifier() (pki.PublicKey, error) Insertable() (bool, error) // denotes whether the entry that was unmarshalled has the writeOnly fields required to validate and insert into the log }APIVersionshould return a version string that identifies the version of the type supported by the Rekor serverIndexKeysshould return a[]stringcontaining the keys that are stored in the search index that should map to this log entry's ID.Canonicalizeshould return a[]bytecontaining the canonicalized contents representing the entry. The canonicalization of contents is important as we should have one record per unique signed object in the transparency log.Unmarshalwill be called with a pointer to a struct that was automatically generated for the type defined inopenapi.yamlby the go-swagger tool used by Rekor- This method should validate the contents of the struct to ensure any string or cross-field dependencies are met to successfully insert an entry of this type into the transparency log
CreateFromArtifactPropertiesreturns a proposed entry of this specific entry implementation given the provided artifact propertiesVerifierreturns the verification material that was used to verify the digital signatureInsertableintrospects the entry and determines if the object is sufficiently hydrated to make a new entry into the log. Entry instances that are created by reading the contents stored in the log may not be sufficiently hydrated.
In the Go package you have created for the new type, be sure to add an entry in the
TypeMapingithub.com/sigstore/rekor/pkg/typesfor your new type in theinitmethod for your package. The key for the map is the unique string used to define your type inopenapi.yaml(e.g.newType), and the value for the map is the name of a factory function for an instance ofTypeImpl.gofunc init() { types.TypeMap.Set("newType", NewEntry) }Add an entry to
pluggableTypeMapincmd/server/app/serve.gothat provides a reference to your package. This ensures that theinitfunction of your type (and optionally, your version implementation) will be called before the server starts to process incoming requests and therefore will be added to the map that is used to route request processing for different types.After adding sufficient unit & integration tests, submit a pull request to
sigstore/rekorfor review and addition to the codebase.
Adding a new version of the `Rekord` type
To add new version of the default Rekord type:
Create a new subdirectory under
pkg/types/rekord/for the new versionIf there are changes to the Rekord schema for this version, create a new JSON schema document and add a reference to it within the
oneOfclause inrekord_schema.json. If there are no changes, skip this step.Provide an implementation of the
EntryImplinterface as defined inpkg/types/types.gofor the new version.In your package's
initmethod, ensure there is a call toSemVerToFacFnMap.Set()which provides the link between the valid semver ranges that your package can successfully process and the factory function that creates an instance of a struct for your new version.Add an entry to
pluggableTypeMapincmd/server/app/serve.gothat provides a reference to the Go package implementing the new version. This ensures that theinitfunction will be called before the server starts to process incoming requests and therefore will be added to the map that is used to route request processing for different types.After adding sufficient unit & integration tests, submit a pull request to
sigstore/rekorfor review and addition to the codebase.
File: content/en/logging/sharding.md
type: docs category: Transparency Log title: Sharding weight: 1820
NOTE: The suggestions on this page are no longer recommended. This page only covers Rekor v1, aka rekor.sigstore.dev.
New Rekor deployments should use Rekor v2 only. We recommend creating
new log instances periodically depending on the growth of the log. Each log instance should be accessible via a unique
URL.
For the public infrastructure, we aim to create a new log every year, publishing the new log's URL in the
TUF-distributed signing configuration file
and its key in the trusted root file.
This document covers what Rekor log sharding is and how to shard the log.
What is sharding?
When Rekor is started for the first time, its backend is a transparency log built on a single Merkle Tree.
This log can grow indefinitely as entries are added, which can present issues over time.
To resolve some of these issues the log can be "sharded" into multiple Merkle Trees.
Why do we shard the log?
Sharding the log allows for:
- Freezing the current log and rotating signing keys if needed
- Easier and faster querying for entries from the tree
- Easier scaling and platform migrations
How does this impact user experience?
It shouldn't!
End users shouldn't notice any difference in their experience.
They can still query via UUID, and Rekor will find the correct entry from whichever shard it's in.
Querying by log index works as well, since log indices are distinct and increase across shards.
More Details
For more details around sharding, see the original design doc!
Note: You'll need to join the [email protected] Google group for access to the doc.
How do I shard the Rekor log?
Sharding the Rekor log will require some downtime in your Rekor service.
This is necessary because you'll need the length of the current shard later on, so new entries can't be added while sharding is in progress.
Follow these steps to shard the log:
Stop all traffic to Rekor so new entries can't be added to the log
Store the tree ID and length of the current active shard:
bashCURRENT_TREE_ID=$(rekor-cli loginfo --format json | jq -r .TreeID) CURRENT_SHARD_LENGTH=$(rekor-cli loginfo --format json | jq -r .ActiveTreeSize)Connect to the production cluster. Port-forward the running
trillian_logservercontainer and run the createtree script.
This will create a new Merkle Tree which will become the new active shard.bashkubectl port-forward -n trillian-system deploy/trillian-log-server 8090:8090 # This is the Tree ID of the new active shard NEW_TREE_ID=$(createtree --admin_server localhost:8090)Note: Sigstore Helm Charts use port 8091 for gRPC
Update the Rekor
sharding-configConfigMap with details of the inactive shard:bashkubectl edit configmap sharding-config -n rekor-systemAppend the following onto the
sharding-config.yamlkey (it will be empty if this is the first shard):yaml- treeID: $CURRENT_TREE_ID treeLength: $CURRENT_SHARD_LENGTHIn your rekor-server Deployment, update the
--trillian_log_server.tlog_idflag to point to the new Tree ID.bash"--trillian_log_server.tlog_id=$NEW_TREE_ID",Redeploy Rekor to the cluster with these changes.
Restart traffic to your Rekor service.
Congratulations, you've successfully sharded the log!
Identifier Definitions: EntryID, UUID, LogID, Log Index
An EntryID is the unique identifier for an artifact in Rekor. It is made up of two parts, the TreeID and UUID:
EntryID = TreeID (8 byte hex) + UUID (32 byte hex)
The TreeID refers to the specific trillian tree (also known as log or shard) where the artifact can be found. Rekor uses Trillian to implement each transparency log.
The UUID refers to the specific artifact within a specified tree.
The LogIndex of an artifact identifies the index or order in which the artifact was entered into a specific tree.
File: content/en/logging/sign-upload.md
type: docs category: Transparency Log title: Signing and Uploading Other Types weight: 1835
This documentation contains information on how to sign and upload data in different [pluggable types]({{< relref "logging/pluggable-types">}}).
The following are covered:
Minisign
Create a keypair with something like:
$ minisign -G
Please enter a password to protect the secret key.
Password:
Password (one more time):
Deriving a key from the password in order to encrypt the secret key... done
The secret key was saved as /Users/dlorenc/.minisign/minisign.key - Keep it secret!
The public key was saved as minisign.pub - That one can be public.
Files signed using this key pair can be verified with the following command:
minisign -Vm <file> -P RWSzQI7+S6M0c4yReOwcDZ2petL8pAZsrNfkdyqr0V7j/HGafpjdKZQmSign a file:
$ minisign -S -m README.md
Password:
Deriving a key from the password and decrypting the secret key... doneUpload to Rekor:
$ rekor-cli upload --artifact README.md --signature README.md.minisig --pki-format=minisign --public-key=minisign.pub
Created entry at index 5895, available at: https://rekor.sigstore.dev/api/v1/log/entries/008bfbbaa8f473a0b17cba5f8078d2c08410bca55f01d2ec71860795ef823b36Look at the entry with:
$ ./rekor-cli get --uuid=008bfbbaa8f473a0b17cba5f8078d2c08410bca55f01d2ec71860795ef823b36
LogID: c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d
Index: 5895
IntegratedTime: 2021-07-14T01:39:50Z
UUID: 008bfbbaa8f473a0b17cba5f8078d2c08410bca55f01d2ec71860795ef823b36
Body: {
"RekordObj": {
"data": {
"hash": {
"algorithm": "sha256",
"value": "3d80236772ca7c5405e398a4d685e715859260a8733070b86de7322e233c68d2"
}
},
"signature": {
"content": "dW50cnVzdGVkIGNvbW1lbnQ6ClJXU3pRSTcrUzZNMGMrNUcxbVZzcmw2dmgvYi91VjlxclJySWpxd21abDFKYjZhTGJ2U1NWUzdObDNvUmpVTUdHUWpLVlEyd2JnMnJxNDZxdDdPTHE3L1c3Z2liMlo5Rzh3az0=",
"format": "minisign",
"publicKey": {
"content": "akpGNDdCd05uYWw2MHZ5a0JteXMxK1IzS3F2Ulh1UDhjWnArbU4wcGxDWT0="
}
}
}
}SSH
Generate a keypair with:
$ ssh-keygen -C [email protected] -t ed25519 -f id_ed25519
Generating public/private ed25519 key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in id_ed25519.
Your public key has been saved in id_ed25519.pub.
The key fingerprint is:
SHA256:73u0etmm2h7BehcLbjrwXqXe193k5R5Uz0Lnl83nTt4 [email protected]
The key's randomart image is:
+--[ED25519 256]--+
| |
| |
| . o|
| . . ==|
| S + +oO|
| .. o.=.==|
| oo.B+o=B|
| .oB=+o+X|
| .BO=o.oE|
+----[SHA256]-----+Sign a file with:
$ ssh-keygen -Y sign -n file -f id_ed25519 README.md
Enter passphrase:
Signing file README.md
Write signature to README.md.sigUpload it to Rekor with:
$ rekor-cli upload --artifact README.md --signature README.md.sig --pki-format=ssh --public-key=id_ed25519.pub
Created entry at index 5896, available at: https://rekor.sigstore.dev/api/v1/log/entries/0e81b4d9299e2609e45b5c453a4c0e7820ac74e02c4935a8b830d104632fd2dLook at the entry with:
$ rekor-cli get --uuid=0e81b4d9299e2609e45b5c453a4c0e7820ac74e02c4935a8b830d104632fd2d1
LogID: c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d
Index: 5896
IntegratedTime: 2021-07-14T01:45:06Z
UUID: 0e81b4d9299e2609e45b5c453a4c0e7820ac74e02c4935a8b830d104632fd2d1
Body: {
"RekordObj": {
"data": {
"hash": {
"algorithm": "sha256",
"value": "3d80236772ca7c5405e398a4d685e715859260a8733070b86de7322e233c68d2"
}
},
"signature": {
"content": "LS0t...",
"format": "ssh",
"publicKey": {
"content": "c3NoLWVkMjU1MTkgQUFBQUMzTnphQzFsWkRJMU5URTVBQUFBSUkrc1RWNmxiVVQ2R3pZY2ZUTElKc1FvbDltZTAxOGdNTmc3NHV5T2UrMjUK"
}
}
}
}PKIX/X509
Generate a keypair with:
$ openssl ecparam -genkey -name prime256v1 > ec_private.pem
$ openssl ec -in ec_private.pem -pubout > ec_public.pem
read EC key
writing EC keySign the file with:
openssl dgst -sha256 -sign ec_private.pem -out README.md.sig README.mdUpload it to Rekor with:
$ ./rekor-cli upload --artifact README.md --signature README.md.sig --pki-format=x509 --public-key=ec_public.pem
Created entry at index 5897, available at: https://rekor.sigstore.dev/api/v1/log/entries/31a51c1bc20da83b66b2f24899184b85dbf8261c2de8571479165619ad87cd5dView the entry with:
$ rekor-cli get --uuid=31a51c1bc20da83b66b2f24899184b85dbf8261c2de8571479165619ad87cd5d
LogID: c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d
Index: 5897
IntegratedTime: 2021-07-14T01:49:54Z
UUID: 31a51c1bc20da83b66b2f24899184b85dbf8261c2de8571479165619ad87cd5d
Body: {
"RekordObj": {
"data": {
"hash": {
"algorithm": "sha256",
"value": "3d80236772ca7c5405e398a4d685e715859260a8733070b86de7322e233c68d2"
}
},
"signature": {
"content": "MEUCICwZpVU/3fnWSZkejA8R2j/t5futtl5Co3CDj7k6J6PwAiEA75Cn2txgpg/KjsOitSKsydL3D6cQIf7NQJtsmvsRTRQ=",
"format": "x509",
"publicKey": {
"content": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFYzJKUkJZbS9OQVo5ZHhhUnNWV05mdTcxV3B5TAo2cGx4L1hsZnNVTlM2SmcrWEhEVmpsaVNBNHV2ZEQ4ZW5XdUhNdWQybS9WdEVQaDZYT0M3bjR0aCtnPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="
}
}
}
}RPM
TODO
Alpine
TODO
RPM
TODO
TSR
TODO
TUF
Generate a TUF repository (for example, with the Python reference implementation or go-tuf).
With go-tuf:
tuf init
tuf gen-key root
tuf gen-key targets
tuf gen-key snapshot
tuf gen-key timestamp
tuf add path/to/some/target.txt
tuf snapshot
tuf timestamp
tuf commitYou will find the signed metadata in your TUF repository/ directory:
$ tree .
.
โโโ keys
โ โโโ snapshot.json
โ โโโ targets.json
โ โโโ timestamp.json
โโโ repository
โ โโโ root.json
โ โโโ snapshot.json
โ โโโ targets
โ โ โโโ foo
โ โ โโโ bar
โ โ โโโ baz.txt
โ โโโ targets.json
โ โโโ timestamp.json
โโโ stagedUpload any TUF manifest to Rekor by using the root.json as the public key:
$ ./rekor-cli upload --artifact repository/timestamp.json --type tuf --public-key repository/root.json
Created entry at index 0, available at: https://rekor.sigstore.dev/api/v1/log/entries/6ed8fa5e9f0aa31b6cdfd2cc6877692f5afba52edd7ff5774eebfb22228e8847View the entry with:
$ rekor-cli get --uuid=31a51c1bc20da83b66b2f24899184b85dbf8261c2de8571479165619ad87cd5d
LogID: 5c4ceffb024e0d0b50bb9e03bc308ce83a76353f1003f8e57a21c51f74cc1e0e
Index: 0
IntegratedTime: 2021-08-13T19:17:33Z
UUID: 6ed8fa5e9f0aa31b6cdfd2cc6877692f5afba52edd7ff5774eebfb22228e8847
Body: {
"TufObj": {
"manifest": {
"expires": "2021-12-18 13:28:12.99008 -0600 CST",
"signed": {
"content": [...]
},
"version": 1
},
"root": {
"expires": "2021-12-18 13:28:12.99008 -0600 CST",
"signed": {
"content": [...]
},
"version": 1
}
}
}File: content/en/logging/verify-release.md
type: docs category: Transparency Log title: Verifying Binaries weight: 1840
๐ We will refine this process over time to be more streamlined with a higher consensus threshold
as well as an implementation of a TUF style policy. For now this is quite a
multi step process. We will also deep dive a fair amount here, as its a good opporuntity to pull the covers aside
and see how this all works.
Rekor releases are currently signed and verified using Fulcio OpenID Connect based on short lived signing certificates.
Rekor release signing artifacts are also stored within the public Rekor instance.
Here we will show you how to verify a release, but also take the opportunity to dig down into Sigstore's signing implementation and process.
How to verify a release
Head over to the release page and select the correct release for your systems architecture. Alongside downloading the main binary, also download the signature and signing certificate from the same release.
For example, with binary rekor-cli-linux-amd64, also retrieve rekor-cli-linux-amd64_cert.pem and rekor-cli-linux-amd64_signature.sig.
rekor-cli-linux-amd64: The binary itself.rekor-cli-linux-amd64_cert.pem: The signing certificate. This is an X509 certificate generated by the Sigstore root CA, with the email of a project maintainer embedded as a X509v3 Subject Alternative Name. This provides a guarantee that the binary was signed by the individual with access to that account (the email comes from an OpenID provider). In turn, this information is recorded into the transparency log so that the account can be monitored for misuse or compromise.rekor-cli-linux-amd64_signature.sig: This is the signature generated as a result of the signing event.
Basic verify
With the above three files, we can now perform a rudimentary verification.
We grab the public key from the signing cert:
openssl x509 -pubkey -noout -in rekor-cli-linux-amd64_cert.pem > rekor-cli-linux-amd64_cert_public.pemAnd then we verify:
$ openssl dgst -sha256 -verify rekor-cli-linux-amd64_cert_public.pem -signature rekor-cli-linux-amd64_signature.sig rekor-cli-linux-amd64
Verified OKVerify the certificate chain
We should check the trust chain against Sigstore's Root CA.
The root CA for Fulcio is currently:
-----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-----Save this locally to your machine as fulcio-root.pem
๐ We are looking at leveraging other trust stores for our Root CA, so consider getting the root CA from here, a temporary approach.
Let's now validate the certificate chain:
$ openssl verify -verbose -no_check_time -CAfile fulcio-root.pem rekor-cli-linux-amd64_cert.pem
rekor-cli-linux-amd64_cert.pem: OK๐ We use
-no_check_timeas Fulcio certificates are short-lived (so they always will be expired by design!)
Verify the signing identity
However, how do we know we can trust that public key is from a maintainer at Sigstore?
Let's look at mapping this to an identity:
$ openssl x509 -in rekor-cli-linux-amd64_cert.pem -noout -text |grep email
email:[email protected]OK, so we can see that a Fulcio-generated certificate, with a chain to the root certificate has the email identity of a Sigstore maintainer (in this instance, our own release manager the awesome Carlos).
Verify the entry is in the transparency log
On the release page you will see some URLs, these are the Rekor entries of the signing events.
This is the link for the binary and signing materials we have been working with throughout this guide
Let's dig into this:
$ curl -X GET "https://rekor.sigstore.dev/api/v1/log/entries/b6fdc91e6af5bdd8df133802b7966aa53c1e59365741ee56e287f11263e02c33" | jq
{
"b6fdc91e6af5bdd8df133802b7966aa53c1e59365741ee56e287f11263e02c33": {
"attestation": {},
"body": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJzcGVjIjp7ImRhdGEiOnsiaGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImNlOWE3YzgyZjMyMTk0OTk1ODg4NzU4Y2YxMDdlZjBjYzUyZTBiOGNkY2U3M2I0MjQwNjU4ZWU5ZTczNzgzY2IifX0sInNpZ25hdHVyZSI6eyJjb250ZW50IjoiTUdVQ01EM29LemdzR25QQWtKRVhlZ0RJc2RsaDRCRkNRYk02am5nNFN5M2F4WS8rMnRsSzk3b2UvQ2t4YWJUMVpYVXFDQUl4QUpEcSt6TGZSWlpFSkQ1RHZhS2hGRXUrSm0rakQ0VVhjM0NhWnAyTVNhamlyYWxtdGFsQTZmU0dDWGp3R2ZVek93PT0iLCJmb3JtYXQiOiJ4NTA5IiwicHVibGljS2V5Ijp7ImNvbnRlbnQiOiJMUzB0TFMxQ1JVZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VOcmFrTkRRV2hwWjBGM1NVSkJaMGxWUVUwckswZFlSRk41YlVOUFNXODJZbXhNTUc1RVpuZ3hiMjFuZDBObldVbExiMXBKZW1vd1JVRjNUWGNLUzJwRlZrMUNUVWRCTVZWRlEyaE5UV015Ykc1ak0xSjJZMjFWZFZwSFZqSk5Va1YzUkhkWlJGWlJVVVJGZDJoNllWZGtlbVJIT1hsYVZFRmxSbmN3ZVFwTlZFRXpUV3BuZDA5RVRUTk9SRXBoUm5jd2VVMVVRVE5OYW1kM1QwUlZNMDVFUm1GTlFVRjNaR3BCVVVKblkzRm9hMnBQVUZGSlFrSm5WWEpuVVZGQkNrbG5UbWxCUVZKak1ETXJVVTR2VEhCck9HcHFVRlF3VG1WNWEwMXVjbTltTW5wWlVrSnhObTA1ZWk5VE1YaFJTemR1Wm5aaFUzZ3JSalVyVEV0d04zZ0tSMkV4YkhZMlNXTnZSWGR3VUhBMk1VZHNZbmQ1UzBWUVZXSkxkekpyWW5KeVJWcFBNbmhLVjNreGIwVkVVSEJZTWxKcWNUQllTMFJaY0VGNVppOW1Rd295WnpKalNuVnRhbWRuUlc1TlNVbENTWHBCVDBKblRsWklVVGhDUVdZNFJVSkJUVU5DTkVGM1JYZFpSRlpTTUd4Q1FYZDNRMmRaU1V0M1dVSkNVVlZJQ2tGM1RYZEVRVmxFVmxJd1ZFRlJTQzlDUVVsM1FVUkJaRUpuVGxaSVVUUkZSbWRSVlRSQlVEaHRUa0k0ZWpoU1pGSnlUVlZMWjFBMk1tMHhVRkVyZDNjS1NIZFpSRlpTTUdwQ1FtZDNSbTlCVlhsTlZXUkJSVWRoU2tOcmVWVlRWSEpFWVRWTE4xVnZSekFyZDNkbldUQkhRME56UjBGUlZVWkNkMFZDUWtsSFFRcE5TRFIzWmtGWlNVdDNXVUpDVVZWSVRVRkxSMk5IYURCa1NFRTJUSGs1ZDJOdGJESlpXRkpzV1RKRmRGa3lPWFZrUjFaMVpFTXdNazFFVG0xYVZHUnNDazU1TUhkTlJFRjNURlJKZVUxcVkzUlpiVmt6VGxNeGJVNUhXVEZhVkdkM1drUkpOVTVVVVhWak0xSjJZMjFHYmxwVE5XNWlNamx1WWtkV2FHTkhiSG9LVEcxT2RtSlRPV3BaVkUweVdWUkdiRTlVV1hsT1JFcHBUMWRhYWxscVJUQk9hVGxxV1ZNMWFtTnVVWGRJWjFsRVZsSXdVa0ZSU0M5Q1FsRjNSVzlGVVFwWk0xSm9Xa2RXTVZGSFpIUlpWMnh6VEcxT2RtSlVRVXRDWjJkeGFHdHFUMUJSVVVSQmQwNXZRVVJDYkVGcVJVRTNUVEp3U3poUlVGUnJTR3MxYnpaMENtZG5hbXBaZGpCTFYxQlVhalJLVVRBd1UzUmpSMHhxYTFnM1NVMWlOQzlIZFhwWVJrUTRjelpET0VkM05tcHdNRUZxUVcxWGEySlJPVFZzTXpsblVHUUtSMnBqVWpCU1FVUmFUMGRZYjBOUFFVUndPRTVsU3poQkwyZEtkV2RuUjBaSU5IWllaMmwxT0RKc1FtNU1PRVpTYzA5alBRb3RMUzB0TFVWT1JDQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENnPT0ifX19LCJraW5kIjoicmVrb3JkIn0=",
"integratedTime": 1627461494,
"logID": "c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d",
"logIndex": 25579,
"verification": {
"inclusionProof": {
"hashes": [
"779627efafc2601b0b5c4755d1aa27eb3cf8c1f034960ccc1038b9556b1d78bb",
"892fffd41824856e3279fb815cdedd28fc76722985e508f6ffdf5af5684f0d84",
"24259982c90f0e2e8a051c06f4cda83fb7f06c3e47b1307d5bc3e50a625b3dcb",
"a5d5a5841e65786e0718db2f43d909ff92fbfa19bc90bc3187fc143e7af2eb29",
"2cba9779511cd8c964ac59e4763ecd5257aa67695b20bfbc957341e326848b60",
"b269aa60e284ce1f866f5c7abd9da16d8e29bf9ab069c66ce91cfe8d6a852458",
"8af1c7cd9d0a691984963b643c572c560cee08fe38083e2013763df5131e63b3",
"e86d56509d6b149df6bf2389e066a4b21516f5a13540b8ef52c790b8ed70efe2",
"04cfd843e6ee05e78eee3229a1b547a4776a81e651888c09bdbfde866ac38d97",
"339bd5b9f48f7ecf1116f0b0ec9f9eeb95b84f3d53f27b8081e56facbcd71548",
"74c9aba66c2704b143e33c3ebc5f12a17be5bda8a8eba055bd471ed57a6fd0b4",
"43a291f5a7301f1b18bbf76829435251a9189c477aa52633fef3f589e3d47446",
"73b38c2b7d6eac887dc9f028500b67ede8655f136bc75354f8bfe94515327513",
"4a33ede5d1c6be306c0362fbee2e9f692522327b4cae78165f810f7dcfaa8dd0"
],
"logIndex": 25579,
"rootHash": "9ecebec82edb290e54f8b45702b05b94efe1e2a7848d5b5aa2c4bbe188995ca2",
"treeSize": 27024
},
"signedEntryTimestamp": "MEUCIHPlKC5Hh34bdjBrqk++9yuznsFOyolHsoulTTxjM+hGAiEA3MEf6d2zsxFJaiZsGCHIhN8Yvc+NZMmUolIBqUGCps0="
}
}
}Let's focus on the body and decode it from base64:
$ curl -X GET "https://rekor.sigstore.dev/api/v1/log/entries/b6fdc91e6af5bdd8df133802b7966aa53c1e59365741ee56e287f11263e02c33" | jq -r '.[].body'| base64 -d | jq -r '.[]'
{
"data": {
"hash": {
"algorithm": "sha256",
"value": "ce9a7c82f32194995888758cf107ef0cc52e0b8cdce73b4240658ee9e73783cb"
}
},
"signature": {
"content": "MGUCMD3oKzgsGnPAkJEXegDIsdlh4BFCQbM6jng4Sy3axY/+2tlK97oe/CkxabT1ZXUqCAIxAJDq+zLfRZZEJD5DvaKhFEu+Jm+jD4UXc3CaZp2MSajiralmtalA6fSGCXjwGfUzOw==",
"format": "x509",
"publicKey": {
"content": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNrakNDQWhpZ0F3SUJBZ0lVQU0rK0dYRFN5bUNPSW82YmxMMG5EZngxb21nd0NnWUlLb1pJemowRUF3TXcKS2pFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUkV3RHdZRFZRUURFd2h6YVdkemRHOXlaVEFlRncweQpNVEEzTWpnd09ETTNOREphRncweU1UQTNNamd3T0RVM05ERmFNQUF3ZGpBUUJnY3Foa2pPUFFJQkJnVXJnUVFBCklnTmlBQVJjMDMrUU4vTHBrOGpqUFQwTmV5a01ucm9mMnpZUkJxNm05ei9TMXhRSzduZnZhU3grRjUrTEtwN3gKR2ExbHY2SWNvRXdwUHA2MUdsYnd5S0VQVWJLdzJrYnJyRVpPMnhKV3kxb0VEUHBYMlJqcTBYS0RZcEF5Zi9mQwoyZzJjSnVtamdnRW5NSUlCSXpBT0JnTlZIUThCQWY4RUJBTUNCNEF3RXdZRFZSMGxCQXd3Q2dZSUt3WUJCUVVICkF3TXdEQVlEVlIwVEFRSC9CQUl3QURBZEJnTlZIUTRFRmdRVTRBUDhtTkI4ejhSZFJyTVVLZ1A2Mm0xUFErd3cKSHdZRFZSMGpCQmd3Rm9BVXlNVWRBRUdhSkNreVVTVHJEYTVLN1VvRzArd3dnWTBHQ0NzR0FRVUZCd0VCQklHQQpNSDR3ZkFZSUt3WUJCUVVITUFLR2NHaDBkSEE2THk5d2NtbDJZWFJsWTJFdFkyOXVkR1Z1ZEMwMk1ETm1aVGRsCk55MHdNREF3TFRJeU1qY3RZbVkzTlMxbU5HWTFaVGd3WkRJNU5UUXVjM1J2Y21GblpTNW5iMjluYkdWaGNHbHoKTG1OdmJTOWpZVE0yWVRGbE9UWXlOREppT1daallqRTBOaTlqWVM1amNuUXdIZ1lEVlIwUkFRSC9CQlF3RW9FUQpZM1JoWkdWMVFHZHRZV2xzTG1OdmJUQUtCZ2dxaGtqT1BRUURBd05vQURCbEFqRUE3TTJwSzhRUFRrSGs1bzZ0CmdnampZdjBLV1BUajRKUTAwU3RjR0xqa1g3SU1iNC9HdXpYRkQ4czZDOEd3NmpwMEFqQW1Xa2JROTVsMzlnUGQKR2pjUjBSQURaT0dYb0NPQURwOE5lSzhBL2dKdWdnR0ZINHZYZ2l1ODJsQm5MOEZSc09jPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg=="
}
}
}Ok, now you can see the digest of the artifact that was signed:
$ sha256sum rekor-cli-linux-amd64
ce9a7c82f32194995888758cf107ef0cc52e0b8cdce73b4240658ee9e73783cb rekor-cli-linux-amd64We can then grab the signing certificate:
echo LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNrakNDQWhpZ0F3SUJBZ0lVQU0rK0dYRFN5bUNPSW82YmxMMG5EZngxb21nd0NnWUlLb1pJemowRUF3TXcKS2pFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUkV3RHdZRFZRUURFd2h6YVdkemRHOXlaVEFlRncweQpNVEEzTWpnd09ETTNOREphRncweU1UQTNNamd3T0RVM05ERmFNQUF3ZGpBUUJnY3Foa2pPUFFJQkJnVXJnUVFBCklnTmlBQVJjMDMrUU4vTHBrOGpqUFQwTmV5a01ucm9mMnpZUkJxNm05ei9TMXhRSzduZnZhU3grRjUrTEtwN3gKR2ExbHY2SWNvRXdwUHA2MUdsYnd5S0VQVWJLdzJrYnJyRVpPMnhKV3kxb0VEUHBYMlJqcTBYS0RZcEF5Zi9mQwoyZzJjSnVtamdnRW5NSUlCSXpBT0JnTlZIUThCQWY4RUJBTUNCNEF3RXdZRFZSMGxCQXd3Q2dZSUt3WUJCUVVICkF3TXdEQVlEVlIwVEFRSC9CQUl3QURBZEJnTlZIUTRFRmdRVTRBUDhtTkI4ejhSZFJyTVVLZ1A2Mm0xUFErd3cKSHdZRFZSMGpCQmd3Rm9BVXlNVWRBRUdhSkNreVVTVHJEYTVLN1VvRzArd3dnWTBHQ0NzR0FRVUZCd0VCQklHQQpNSDR3ZkFZSUt3WUJCUVVITUFLR2NHaDBkSEE2THk5d2NtbDJZWFJsWTJFdFkyOXVkR1Z1ZEMwMk1ETm1aVGRsCk55MHdNREF3TFRJeU1qY3RZbVkzTlMxbU5HWTFaVGd3WkRJNU5UUXVjM1J2Y21GblpTNW5iMjluYkdWaGNHbHoKTG1OdmJTOWpZVE0yWVRGbE9UWXlOREppT1daallqRTBOaTlqWVM1amNuUXdIZ1lEVlIwUkFRSC9CQlF3RW9FUQpZM1JoWkdWMVFHZHRZV2xzTG1OdmJUQUtCZ2dxaGtqT1BRUURBd05vQURCbEFqRUE3TTJwSzhRUFRrSGs1bzZ0CmdnampZdjBLV1BUajRKUTAwU3RjR0xqa1g3SU1iNC9HdXpYRkQ4czZDOEd3NmpwMEFqQW1Xa2JROTVsMzlnUGQKR2pjUjBSQURaT0dYb0NPQURwOE5lSzhBL2dKdWdnR0ZINHZYZ2l1ODJsQm5MOEZSc09jPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==| base64 -d > tlog_signing_cert.pem```Which is, of course, the same as our signing certificate:
diff tlog_signing_cert.pem rekor-cli-linux-amd64_cert.pemAnd the signature can also be retrieved:
echo MGUCMD3oKzgsGnPAkJEXegDIsdlh4BFCQbM6jng4Sy3axY/+2tlK97oe/CkxabT1ZXUqCAIxAJDq+zLfRZZEJD5DvaKhFEu+Jm+jD4UXc3CaZp2MSajiralmtalA6fSGCXjwGfUzOw== | base64 -d > tlog_signing_sig.sig
$ diff tlog_signing_sig.sig rekor-cli-linux-amd64_signature.sigSo we can then do the same verification using just the tlog!
$ openssl x509 -pubkey -noout -in tlog_signing_cert.pem > tlog_signing_cert_public.pem
$ openssl dgst -sha256 -verify tlog_signing_cert_public.pem -signature tlog_signing_sig.sig rekor-cli-linux-amd64
Verified OKSummarise
So we now know that the binary you downloaded was signed by the individual in control of the OpenID based account.
In turn, this account has 2FA enabled and is monitored for misuse, since records are transparent within the public Rekor transparency log.
There is a lot more we could do well. Using the sha256 digest you could retrieve the signing materials for a release binary, even if you sourced the binary from elsewhere!
File: content/en/language_clients/go.md
type: docs category: Language Clients title: Go weight: 10
sigstore-go is the Go language client library for Sigstore.
sigstore-go is intended as a minimal dependency library for signing and verifying. It's not intended to replace Cosign, which provides a CLI with many features for interacting with Sigstore. Over time, cosign will use sigstore-go for verification.
- Friendly API for integrating Go code with Sigstore
- Smaller dependency tree
- Focuses on newly specified data structures in sigstore/protobuf-specs
- Perfect for simple signing and verififcation tasks
sigstore-go is currently in beta.
Features
- Signing and verification of Sigstore bundles
- Verification of raw Sigstore signatures
- Signing and verifying with a Timestamp Authority (TSA)
- Online and offline signing and verifying with Rekor (Artifact Transparency Log)
- Structured verification results including certificate metadata
- TUF support
- Verification support for custom trusted root
- Basic CLI
Installation
Main CLI installation
sigstore-go requires Go 1.21 or greater. The package is tested with Go 1.23.
To compile/install the CLI, clone sigstore-go and run.
make installAlternatively, you can use go run cmd/sigstore-go/main.go to access the CLI, as show in the example.
Example
CLI example
The following is an example of using the sigstore-go CLI to verify a signature.
go run cmd/sigstore-go/main.go \
-artifact-digest 76176ffa33808b54602c7c35de5c6e9a4deb96066dba6533f50ac234f4f1f4c6b3527515dc17c06fbe2860030f410eee69ea20079bd3a2c6f3dcf3b329b10751 \
-artifact-digest-algorithm sha512 \
-expectedIssuer https://token.actions.githubusercontent.com \
-expectedSAN https://github.com/sigstore/sigstore-js/.github/workflows/release.yml@refs/heads/main \
examples/bundle-provenance.json
Verification successful!
{
"version": 20230823,
"statement": {
"_type": "https://in-toto.io/Statement/v0.1",
"predicateType": "https://slsa.dev/provenance/v0.2",
"subject": ...
},
...
}Additional examples
Additional examples are available in the project documentation.