Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 15 additions & 102 deletions cmd/notation/verify.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,16 @@
package main

import (
"context"
"errors"
"fmt"
"os"

"github.com/notaryproject/notation-core-go/signature/jws"
"github.com/notaryproject/notation-go"
"github.com/notaryproject/notation-go/dir"
"github.com/notaryproject/notation-go/signature"
"github.com/notaryproject/notation/internal/cmd"
"github.com/notaryproject/notation/internal/slices"
"github.com/notaryproject/notation/pkg/cache"
"github.com/notaryproject/notation/pkg/configutil"
"github.com/opencontainers/go-digest"
"github.com/notaryproject/notation-go/verification"
"github.com/spf13/cobra"
"oras.land/oras-go/v2/registry"
)

type verifyOpts struct {
RemoteFlagOpts
signatures []string
certs []string
certFiles []string
pull bool
reference string
reference string
}

func verifyCommand(opts *verifyOpts) *cobra.Command {
Expand All @@ -45,113 +31,40 @@ func verifyCommand(opts *verifyOpts) *cobra.Command {
return runVerify(cmd, opts)
},
}
setFlagSignature(command.Flags(), &opts.signatures)
command.Flags().StringSliceVarP(&opts.certs, "cert", "c", []string{}, "certificate names for verification")
command.Flags().StringSliceVar(&opts.certFiles, cmd.PflagCertFile.Name, []string{}, "certificate files for verification")
command.Flags().BoolVar(&opts.pull, "pull", true, "pull remote signatures before verification")
opts.ApplyFlags(command.Flags())
return command
}

func runVerify(command *cobra.Command, opts *verifyOpts) error {
// initialize
verifier, err := getVerifier(opts)
if err != nil {
return err
}
manifestDesc, err := getManifestDescriptorFromContext(command.Context(), &opts.RemoteFlagOpts, opts.reference)
if err != nil {
return err
}

sigPaths := opts.signatures
if len(sigPaths) == 0 {
if !opts.Local && opts.pull {
if err := pullSignatures(command, opts.reference, &opts.SecureFlagOpts, digest.Digest(manifestDesc.Digest)); err != nil {
return err
}
}
manifestDigest := digest.Digest(manifestDesc.Digest)
sigDigests, err := cache.SignatureDigests(manifestDigest)
if err != nil {
return err
}
for _, sigDigest := range sigDigests {
sigPaths = append(sigPaths, dir.Path.CachedSignature(manifestDigest, sigDigest))
}
}

// core process
if err := verifySignatures(command.Context(), verifier, manifestDesc, sigPaths); err != nil {
if _, err := verifier.Verify(command.Context(), opts.reference); err != nil {
return err
} else {
fmt.Println("successfully verified the reference : " + opts.reference)
}

// write out
fmt.Println(manifestDesc.Digest)
return nil
}

func verifySignatures(ctx context.Context, verifier notation.Verifier, manifestDesc notation.Descriptor, sigPaths []string) error {
if len(sigPaths) == 0 {
return errors.New("verification failure: no signatures found")
}

// TODO: support cose media type
opts := notation.VerifyOptions{
SignatureMediaType: jws.MediaTypeEnvelope,
func getVerifier(opts *verifyOpts) (*verification.Verifier, error) {
reference, err := registry.ParseReference(opts.reference)
if err != nil {
return nil, err
}
var lastErr error
for _, path := range sigPaths {
sig, err := os.ReadFile(path)
if err != nil {
return err
}
desc, err := verifier.Verify(ctx, sig, opts)
if err != nil {
lastErr = fmt.Errorf("verification failure: %v", err)
continue
}

if !desc.Equal(manifestDesc) {
lastErr = fmt.Errorf("verification failure: %s", manifestDesc.Digest)
continue
}
return nil
repository, err := getRepositoryClient(&opts.SecureFlagOpts, reference)
if err != nil {
return nil, err
}
return lastErr
}

func getVerifier(opts *verifyOpts) (notation.Verifier, error) {
certPaths, err := appendCertPathFromName(opts.certFiles, opts.certs)
verifier, err := verification.NewVerifier(repository)
if err != nil {
return nil, err
}
if len(certPaths) == 0 {
cfg, err := configutil.LoadConfigOnce()
if err != nil {
return nil, err
}
if len(cfg.VerificationCertificates.Certificates) == 0 {
return nil, errors.New("trust certificate not specified")
}
for _, ref := range cfg.VerificationCertificates.Certificates {
certPaths = append(certPaths, ref.Path)
}
}
return signature.NewVerifierFromFiles(certPaths)
}

func appendCertPathFromName(paths, names []string) ([]string, error) {
for _, name := range names {
cfg, err := configutil.LoadConfigOnce()
if err != nil {
return nil, err
}
idx := slices.Index(cfg.VerificationCertificates.Certificates, name)
if idx < 0 {
return nil, errors.New("verification certificate not found: " + name)
}
paths = append(paths, cfg.VerificationCertificates.Certificates[idx].Path)
}
return paths, nil
return verifier, nil
}
17 changes: 1 addition & 16 deletions cmd/notation/verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,11 @@ func TestVerifyCommand_BasicArgs(t *testing.T) {
MediaType: defaultMediaType,
},
},
certs: []string{"cert0", "cert1"},
certFiles: []string{"certfile0", "certfile1"},
signatures: []string{"sig0", "sig1"},
pull: true,
}
if err := command.ParseFlags([]string{
expected.reference,
"--username", expected.Username,
"--password", expected.Password,
"-c", expected.certs[0],
"--cert", expected.certs[1],
"--cert-file", expected.certFiles[0],
"--cert-file", expected.certFiles[1],
"--signature", expected.signatures[0],
"-s", expected.signatures[1]}); err != nil {
"--password", expected.Password}); err != nil {
t.Fatalf("Parse Flag failed: %v", err)
}
if err := command.Args(command, command.Flags().Args()); err != nil {
Expand All @@ -57,15 +47,10 @@ func TestVerifyCommand_MoreArgs(t *testing.T) {
MediaType: "mediaT",
},
},
certs: []string{},
certFiles: []string{},
signatures: []string{},
pull: false,
}
if err := command.ParseFlags([]string{
expected.reference,
"--plain-http",
"--pull=false",
"--media-type=mediaT"}); err != nil {
t.Fatalf("Parse Flag failed: %v", err)
}
Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@ require (
)

require (
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
github.com/go-ldap/ldap/v3 v3.4.4 // indirect
github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/opencontainers/distribution-spec/specs-go v0.0.0-20220620172159-4ab4752c3b86 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
github.com/oras-project/artifacts-spec v1.0.0-rc.2 // indirect
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect
)
24 changes: 24 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU=
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/distribution/v3 v3.0.0-20220729163034-26163d82560f h1:3NCYdjXycNd/Xn/iICZzmxkiDX1e1cjTHjbMAz+wRVk=
github.com/distribution/distribution/v3 v3.0.0-20220729163034-26163d82560f/go.mod h1:28YO/VJk9/64+sTGNuYaBjWxrXTPrj0C0XmgTIOjxX4=
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs=
github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI=
github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
Expand All @@ -19,16 +27,32 @@ github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/oras-project/artifacts-spec v1.0.0-rc.2 h1:9SMCNSxkJEHqWGDiMCuy6TXHgvjgwXGdXZZGXLKQvVE=
github.com/oras-project/artifacts-spec v1.0.0-rc.2/go.mod h1:Xch2aLzSwtkhbFFN6LUzTfLtukYvMMdXJ4oZ8O7BOdc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
oras.land/oras-go/v2 v2.0.0-rc.3 h1:O4GeIwJ9Ge7rbCkqa/M7DLrL55ww+ZEc+Rhc63OYitU=
oras.land/oras-go/v2 v2.0.0-rc.3/go.mod h1:PrY+cCglzK/DrQoJUtxbYVbL94ZHecVS3eJR01RglpE=