* add attestation cmd
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add codeowners
Signed-off-by: Meredith Lancaster <malancas@github.com>
* update args passed to the attestation cmd
Signed-off-by: Meredith Lancaster <malancas@github.com>
* rename file
Signed-off-by: Meredith Lancaster <malancas@github.com>
* use gh-attestation branch for passing iostreams from the root
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add package security team entry to codeowners
Signed-off-by: Meredith Lancaster <malancas@github.com>
* start moving over verify cmd and general verification code
Signed-off-by: Meredith Lancaster <malancas@github.com>
* clean up common and verify specific policy code
Signed-off-by: Meredith Lancaster <malancas@github.com>
* move artifact package over
Signed-off-by: Meredith Lancaster <malancas@github.com>
* start pulling in the github api client wrapper
Signed-off-by: Meredith Lancaster <malancas@github.com>
* fix imports
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add logger and test packages
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add additional packages to support verify command
Signed-off-by: Meredith Lancaster <malancas@github.com>
* fix mock api client
Signed-off-by: Meredith Lancaster <malancas@github.com>
* clean up mock api client
Signed-off-by: Meredith Lancaster <malancas@github.com>
* include missing fields
Signed-off-by: Meredith Lancaster <malancas@github.com>
* use correct owner
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add more mock api client options
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add download cmd
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add inspect cmd
Signed-off-by: Meredith Lancaster <malancas@github.com>
* pass factory object to inspect cmd, add inspect sub cmd to attestation cmd
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add verify-tuf-root cmd
Signed-off-by: Meredith Lancaster <malancas@github.com>
* pass iostream struct from command
Signed-off-by: Meredith Lancaster <malancas@github.com>
* rename logger pkg to logger
Signed-off-by: Meredith Lancaster <malancas@github.com>
* fix path in codeowners
Signed-off-by: Meredith Lancaster <malancas@github.com>
* formatter
Signed-off-by: Meredith Lancaster <malancas@github.com>
* go mod tidy
Signed-off-by: Meredith Lancaster <malancas@github.com>
* fix printf linter issue
Signed-off-by: Meredith Lancaster <malancas@github.com>
* fix printf linter issue
Signed-off-by: Meredith Lancaster <malancas@github.com>
* check user's GH host for compatibility
Signed-off-by: Meredith Lancaster <malancas@github.com>
* pass oci client to commands directly
Signed-off-by: Meredith Lancaster <malancas@github.com>
* rename command
Signed-off-by: Meredith Lancaster <malancas@github.com>
* mark tuf-root-verify cmd hidden
Signed-off-by: Meredith Lancaster <malancas@github.com>
* move client initialization back to subcommands
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add more verbose options and logging
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add missing logger
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add testing around OCI and API client
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add integration test
Signed-off-by: Meredith Lancaster <malancas@github.com>
* fix file path
Signed-off-by: Meredith Lancaster <malancas@github.com>
* fix command
Signed-off-by: Meredith Lancaster <malancas@github.com>
* build executable before integration test
Signed-off-by: Meredith Lancaster <malancas@github.com>
* split integration tests
Signed-off-by: Meredith Lancaster <malancas@github.com>
* remove integration test steps
Signed-off-by: Meredith Lancaster <malancas@github.com>
* fix flag value
Signed-off-by: Meredith Lancaster <malancas@github.com>
* run integration tests on ubuntu for now
Signed-off-by: Meredith Lancaster <malancas@github.com>
* pull over doc updates
Signed-off-by: Meredith Lancaster <malancas@github.com>
* delete unused test data
Signed-off-by: Meredith Lancaster <malancas@github.com>
* remove Go patch version
Signed-off-by: Meredith Lancaster <malancas@github.com>
* switch assert to require
Signed-off-by: Meredith Lancaster <malancas@github.com>
* rename file
Signed-off-by: Meredith Lancaster <malancas@github.com>
* move integration tests to prexisting test workflow
Signed-off-by: Meredith Lancaster <malancas@github.com>
* use platform matrix for integration tests
Signed-off-by: Meredith Lancaster <malancas@github.com>
* simplify build step
Signed-off-by: Meredith Lancaster <malancas@github.com>
* use StringEnumFlag handling
Signed-off-by: Meredith Lancaster <malancas@github.com>
* typo
Signed-off-by: Meredith Lancaster <malancas@github.com>
* use the iostreams.Test helper func
Signed-off-by: Meredith Lancaster <malancas@github.com>
* create interface for oci client
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add tests for oci client
Signed-off-by: Meredith Lancaster <malancas@github.com>
* rename files
Signed-off-by: Meredith Lancaster <malancas@github.com>
* format file
Signed-off-by: Meredith Lancaster <malancas@github.com>
* fix shellcheck issues
Signed-off-by: Meredith Lancaster <malancas@github.com>
* use testing TempDir method
Signed-off-by: Meredith Lancaster <malancas@github.com>
* cleanup unused tempdir handling
Signed-off-by: Meredith Lancaster <malancas@github.com>
* use table driven tests
Signed-off-by: Meredith Lancaster <malancas@github.com>
* check correct cmd
Signed-off-by: Meredith Lancaster <malancas@github.com>
* support repo option in download sub cmd
Signed-off-by: Meredith Lancaster <malancas@github.com>
* switch over to using RunE
Signed-off-by: Meredith Lancaster <malancas@github.com>
* unexport top level subcommand funcs
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add comment around keychain option
Signed-off-by: Meredith Lancaster <malancas@github.com>
* update comments
Signed-off-by: Meredith Lancaster <malancas@github.com>
* fix inconsistent naming
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add tests for CLI commands
Signed-off-by: Meredith Lancaster <malancas@github.com>
* check for noattestationsfound err
Signed-off-by: Meredith Lancaster <malancas@github.com>
* try out metadata abstraction instead
Signed-off-by: Meredith Lancaster <malancas@github.com>
* switch to using MetadataStore abstraction
Signed-off-by: Meredith Lancaster <malancas@github.com>
* include test case with failing metadata store
Signed-off-by: Meredith Lancaster <malancas@github.com>
* look for err specific to file write
Signed-off-by: Meredith Lancaster <malancas@github.com>
* unexport fields
Signed-off-by: Meredith Lancaster <malancas@github.com>
* return err when an unsupported hash alg is provided
Signed-off-by: Meredith Lancaster <malancas@github.com>
* PrintTableToStdOut returns err when rendering fails
Signed-off-by: Meredith Lancaster <malancas@github.com>
* start adding sigstore verifier unit tests
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add more sigstore verifier specific tests
Signed-off-by: Meredith Lancaster <malancas@github.com>
* use cli table printer
Signed-off-by: Meredith Lancaster <malancas@github.com>
* return JSON results in slice instead of table
Signed-off-by: Meredith Lancaster <malancas@github.com>
* move mock client to test file
Signed-off-by: Meredith Lancaster <malancas@github.com>
* remove unneeded table printer method
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add initial tests for tufrootverify cmd
Signed-off-by: Meredith Lancaster <malancas@github.com>
* formatting
Signed-off-by: Meredith Lancaster <malancas@github.com>
* cleanup method
Signed-off-by: Meredith Lancaster <malancas@github.com>
* close file in error handling branch
Signed-off-by: Meredith Lancaster <malancas@github.com>
* normalize artifact path
Signed-off-by: Meredith Lancaster <malancas@github.com>
* remove unneeded embedded file system
Signed-off-by: Meredith Lancaster <malancas@github.com>
* include image name reference err
Signed-off-by: Meredith Lancaster <malancas@github.com>
* use GH_DEBUG value for io handling
Signed-off-by: Meredith Lancaster <malancas@github.com>
* remove quiet and verbose flags
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add more tufrootveriify tests
Signed-off-by: Meredith Lancaster <malancas@github.com>
* GitHubTUFOptions no longer needs to return error
Signed-off-by: Meredith Lancaster <malancas@github.com>
* remove unneeded slice
Signed-off-by: Meredith Lancaster <malancas@github.com>
* normalize all relative paths
Signed-off-by: Meredith Lancaster <malancas@github.com>
* clean up nil client checks
Signed-off-by: Meredith Lancaster <malancas@github.com>
* set api server based on host
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add comment about http client
Signed-off-by: Meredith Lancaster <malancas@github.com>
* use format flag to handle json output in verify cmd
Signed-off-by: Meredith Lancaster <malancas@github.com>
* use format flag to handle json output
Signed-off-by: Meredith Lancaster <malancas@github.com>
* use normalized path for cli test arg
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add tests for json output
Signed-off-by: Meredith Lancaster <malancas@github.com>
* cleanup error wrapping
Signed-off-by: Meredith Lancaster <malancas@github.com>
* use test fixtures correctly by normalizing path
Signed-off-by: Meredith Lancaster <malancas@github.com>
* dont clean
Signed-off-by: Meredith Lancaster <malancas@github.com>
* escape backwards slash for windows files with replace
Signed-off-by: Meredith Lancaster <malancas@github.com>
* use strings.Split func
Signed-off-by: Meredith Lancaster <malancas@github.com>
* use strings.Replace for all command tests
Signed-off-by: Meredith Lancaster <malancas@github.com>
* use CLI cache dir to store tuf metadata
Signed-off-by: Meredith Lancaster <malancas@github.com>
* Tweaked docstrings for gh attestation download
* Tweaked docstrings for gh attestation verify
* Fix for bug in gh attestation where the wrong hostname was being passed to the API client.
* lets hide tuf-root-verify eh?
* Forgot verify's short str.
* add remote verification test
Signed-off-by: Meredith Lancaster <malancas@github.com>
* Revert "add remote verification test"
This reverts commit c0ceb99ca8.
* update json result handling
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add json tags to struct returned by command
Signed-off-by: Meredith Lancaster <malancas@github.com>
* fix how json results are handled
Signed-off-by: Meredith Lancaster <malancas@github.com>
* add test to ensure JSON output is valid
Signed-off-by: Meredith Lancaster <malancas@github.com>
---------
Signed-off-by: Meredith Lancaster <malancas@github.com>
Co-authored-by: Phill MV <phillmv@github.com>
208 lines
6.5 KiB
Go
208 lines
6.5 KiB
Go
package verification
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/cli/cli/v2/pkg/cmd/attestation/api"
|
|
"github.com/cli/cli/v2/pkg/cmd/attestation/io"
|
|
|
|
"github.com/sigstore/sigstore-go/pkg/bundle"
|
|
"github.com/sigstore/sigstore-go/pkg/root"
|
|
"github.com/sigstore/sigstore-go/pkg/tuf"
|
|
"github.com/sigstore/sigstore-go/pkg/verify"
|
|
)
|
|
|
|
const (
|
|
PublicGoodIssuerOrg = "sigstore.dev"
|
|
GitHubIssuerOrg = "GitHub, Inc."
|
|
)
|
|
|
|
// AttestationProcessingResult captures processing a given attestation's signature verification and policy evaluation
|
|
type AttestationProcessingResult struct {
|
|
Attestation *api.Attestation `json:"attestation"`
|
|
VerificationResult *verify.VerificationResult `json:"verificationResult"`
|
|
}
|
|
|
|
type SigstoreResults struct {
|
|
VerifyResults []*AttestationProcessingResult
|
|
Error error
|
|
}
|
|
|
|
type SigstoreConfig struct {
|
|
CustomTrustedRoot string
|
|
Logger *io.Handler
|
|
NoPublicGood bool
|
|
}
|
|
|
|
type SigstoreVerifier struct {
|
|
ghVerifier *verify.SignedEntityVerifier
|
|
publicGoodVerifier *verify.SignedEntityVerifier
|
|
customVerifier *verify.SignedEntityVerifier
|
|
policy verify.PolicyBuilder
|
|
onlyVerifyWithGithub bool
|
|
Logger *io.Handler
|
|
}
|
|
|
|
// NewSigstoreVerifier creates a new SigstoreVerifier struct
|
|
// that is used to verify artifacts and attestations against the
|
|
// Public Good, GitHub, or a custom trusted root.
|
|
func NewSigstoreVerifier(config SigstoreConfig, policy verify.PolicyBuilder) (*SigstoreVerifier, error) {
|
|
customVerifier, err := newCustomVerifier(config.CustomTrustedRoot)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create custom verifier: %v", err)
|
|
}
|
|
|
|
publicGoodVerifier, err := newPublicGoodVerifier()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create Public Good Sigstore verifier: %v", err)
|
|
}
|
|
|
|
ghVerifier, err := newGitHubVerifier()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create GitHub Sigstore verifier: %v", err)
|
|
}
|
|
|
|
return &SigstoreVerifier{
|
|
ghVerifier: ghVerifier,
|
|
publicGoodVerifier: publicGoodVerifier,
|
|
customVerifier: customVerifier,
|
|
Logger: config.Logger,
|
|
policy: policy,
|
|
onlyVerifyWithGithub: config.NoPublicGood,
|
|
}, nil
|
|
}
|
|
|
|
func (v *SigstoreVerifier) chooseVerifier(b *bundle.ProtobufBundle) (*verify.SignedEntityVerifier, string, error) {
|
|
verifyContent, err := b.VerificationContent()
|
|
if err != nil {
|
|
return nil, "", fmt.Errorf("failed to get bundle verification content: %v", err)
|
|
}
|
|
leafCert, ok := verifyContent.HasCertificate()
|
|
if !ok {
|
|
return nil, "", fmt.Errorf("leaf cert not found")
|
|
}
|
|
if len(leafCert.Issuer.Organization) != 1 {
|
|
return nil, "", fmt.Errorf("expected the leaf certificate issuer to only have one organization")
|
|
}
|
|
issuer := leafCert.Issuer.Organization[0]
|
|
|
|
// if user provided a custom trusted root file path, use the custom verifier
|
|
if v.customVerifier != nil {
|
|
return v.customVerifier, issuer, nil
|
|
}
|
|
|
|
if v.onlyVerifyWithGithub {
|
|
return v.ghVerifier, issuer, nil
|
|
}
|
|
|
|
if leafCert.Issuer.Organization[0] == PublicGoodIssuerOrg {
|
|
return v.publicGoodVerifier, issuer, nil
|
|
} else if leafCert.Issuer.Organization[0] == GitHubIssuerOrg {
|
|
return v.ghVerifier, issuer, nil
|
|
}
|
|
return nil, "", fmt.Errorf("leaf certificate issuer is not recognized")
|
|
}
|
|
|
|
func (v *SigstoreVerifier) Verify(attestations []*api.Attestation) *SigstoreResults {
|
|
// initialize the processing results before attempting to verify
|
|
// with multiple verifiers
|
|
results := make([]*AttestationProcessingResult, len(attestations))
|
|
for i, att := range attestations {
|
|
apr := &AttestationProcessingResult{
|
|
Attestation: att,
|
|
}
|
|
results[i] = apr
|
|
}
|
|
|
|
totalAttestations := len(attestations)
|
|
for i, apr := range results {
|
|
v.Logger.VerbosePrintf("Verifying attestation %d/%d against the configured Sigstore trust roots\n", i+1, totalAttestations)
|
|
|
|
// determine which verifier should attempt verification against the bundle
|
|
verifier, issuer, err := v.chooseVerifier(apr.Attestation.Bundle)
|
|
if err != nil {
|
|
return &SigstoreResults{
|
|
Error: fmt.Errorf("failed to find recognized issuer from bundle content: %v", err),
|
|
}
|
|
}
|
|
|
|
v.Logger.VerbosePrintf("Attempting verification against issuer \"%s\"\n", issuer)
|
|
// attempt to verify the attestation
|
|
result, err := verifier.Verify(apr.Attestation.Bundle, v.policy)
|
|
// if verification fails, create the error and exit verification early
|
|
if err != nil {
|
|
v.Logger.VerbosePrint(v.Logger.ColorScheme.Redf(
|
|
"Failed to verify against issuer \"%s\" \n\n", issuer,
|
|
))
|
|
|
|
return &SigstoreResults{
|
|
Error: fmt.Errorf("verifying with issuer \"%s\": %v", issuer, err),
|
|
}
|
|
}
|
|
|
|
// if verification is successful, add the result
|
|
// to the AttestationProcessingResult entry
|
|
v.Logger.VerbosePrint(v.Logger.ColorScheme.Greenf(
|
|
"SUCCESS - attestation signature verified with \"%s\"\n", issuer,
|
|
))
|
|
apr.VerificationResult = result
|
|
}
|
|
|
|
return &SigstoreResults{
|
|
VerifyResults: results,
|
|
}
|
|
}
|
|
|
|
func newCustomVerifier(trustedRootFilePath string) (*verify.SignedEntityVerifier, error) {
|
|
if trustedRootFilePath == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
trustedRoot, err := root.NewTrustedRootFromPath(trustedRootFilePath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create trusted root from file %s: %v", trustedRootFilePath, err)
|
|
}
|
|
|
|
gv, err := verify.NewSignedEntityVerifier(trustedRoot, verify.WithSignedTimestamps(1))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create custom verifier: %v", err)
|
|
}
|
|
|
|
return gv, nil
|
|
}
|
|
|
|
func newGitHubVerifier() (*verify.SignedEntityVerifier, error) {
|
|
opts := GitHubTUFOptions()
|
|
client, err := tuf.New(opts)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create TUF client: %v", err)
|
|
}
|
|
trustedRoot, err := root.GetTrustedRoot(client)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
gv, err := verify.NewSignedEntityVerifier(trustedRoot, verify.WithSignedTimestamps(1))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create GitHub verifier: %v", err)
|
|
}
|
|
|
|
return gv, nil
|
|
}
|
|
|
|
func newPublicGoodVerifier() (*verify.SignedEntityVerifier, error) {
|
|
client, err := tuf.DefaultClient()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create TUF client: %v", err)
|
|
}
|
|
trustedRoot, err := root.GetTrustedRoot(client)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get trusted root: %v", err)
|
|
}
|
|
|
|
sv, err := verify.NewSignedEntityVerifier(trustedRoot, verify.WithSignedCertificateTimestamps(1), verify.WithTransparencyLog(1), verify.WithObserverTimestamps(1))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create Public Good verifier: %v", err)
|
|
}
|
|
|
|
return sv, nil
|
|
}
|