Skip to content

Commit

Permalink
add optional issuer to root policy
Browse files Browse the repository at this point in the history
Signed-off-by: Asra Ali <[email protected]>
  • Loading branch information
asraa committed Nov 4, 2021
1 parent 5deaca0 commit dbad868
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 41 deletions.
4 changes: 4 additions & 0 deletions cmd/cosign/cli/options/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
type PolicyInitOptions struct {
ImageRef string
Maintainers []string
Issuer string
Threshold int
Expires int
OutFile string
Expand All @@ -39,6 +40,9 @@ func (o *PolicyInitOptions) AddFlags(cmd *cobra.Command) {
cmd.Flags().StringVar(&o.OutFile, "out", "o",
"output policy locally")

cmd.Flags().StringVar(&o.Issuer, "issuer", "",
"trusted issuer to use for identity tokens, e.g. https://accounts.google.com")

cmd.Flags().IntVar(&o.Threshold, "threshold", 1,
"threshold for root policy signers")

Expand Down
13 changes: 7 additions & 6 deletions cmd/cosign/cli/policy_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
cremote "github.com/sigstore/cosign/pkg/cosign/remote"
"github.com/sigstore/cosign/pkg/cosign/tuf"
"github.com/sigstore/cosign/pkg/sget"
sigs "github.com/sigstore/cosign/pkg/signature"
signatureoptions "github.com/sigstore/sigstore/pkg/signature/options"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -92,8 +93,8 @@ func initPolicy() *cobra.Command {
if !validEmail(email) {
panic(fmt.Sprintf("Invalid email format: %s", email))
} else {
// Currently no issuer is set: this would need to be set by the initializer.
key := tuf.FulcioVerificationKey(strings.TrimSpace(email), "")
// Currently only a single issuer can be set for all the maintainers.
key := tuf.FulcioVerificationKey(strings.TrimSpace(email), o.Issuer)
publicKeys = append(publicKeys, key)
}
}
Expand Down Expand Up @@ -187,7 +188,8 @@ func signPolicy() *cobra.Command {
if len(certs) == 0 || certs[0].EmailAddresses == nil {
return errors.New("error decoding certificate")
}
signerEmail := certs[0].EmailAddresses[0]
signerEmail := sigs.CertSubject(certs[0])
signerIssuer := sigs.CertIssuerExtension(certs[0])

// Retrieve root.json from registry.
imgName := rootPath(o.ImageRef)
Expand Down Expand Up @@ -225,17 +227,16 @@ func signPolicy() *cobra.Command {
}

// Create and add signature
key := tuf.FulcioVerificationKey(signerEmail, "")
key := tuf.FulcioVerificationKey(signerEmail, signerIssuer)
sig, err := sv.SignMessage(bytes.NewReader(signed.Signed), signatureoptions.WithContext(cmd.Context()))
if err != nil {
return errors.Wrap(err, "error occurred while during artifact signing")
}
signature := tuf.Signature{
KeyID: key.ID(),
Signature: base64.StdEncoding.EncodeToString(sig),
Cert: base64.StdEncoding.EncodeToString(sv.Cert),
}
if err := signed.AddOrUpdateSignature(signature); err != nil {
if err := signed.AddOrUpdateSignature(key, signature); err != nil {
return err
}

Expand Down
28 changes: 4 additions & 24 deletions cmd/cosign/cli/verify/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ package verify

import (
"context"
"crypto/x509"
"encoding/json"
"flag"
"fmt"
Expand Down Expand Up @@ -149,33 +148,14 @@ func PrintVerificationHeader(imgRef string, co *cosign.CheckOpts, bundleVerified
fmt.Fprintln(os.Stderr, " - Any certificates were verified against the Fulcio roots.")
}

func certSubject(c *x509.Certificate) string {
switch {
case c.EmailAddresses != nil:
return c.EmailAddresses[0]
case c.URIs != nil:
return c.URIs[0].String()
}
return ""
}

func certIssuerExtension(cert *x509.Certificate) string {
for _, ext := range cert.Extensions {
if ext.Id.String() == "1.3.6.1.4.1.57264.1.1" {
return string(ext.Value)
}
}
return ""
}

// PrintVerification logs details about the verification to stdout
func PrintVerification(imgRef string, verified []oci.Signature, output string) {
switch output {
case "text":
for _, sig := range verified {
if cert, err := sig.Cert(); err == nil && cert != nil {
fmt.Println("Certificate subject: ", certSubject(cert))
if issuerURL := certIssuerExtension(cert); issuerURL != "" {
fmt.Println("Certificate subject: ", sigs.CertSubject(cert))
if issuerURL := sigs.CertIssuerExtension(cert); issuerURL != "" {
fmt.Println("Certificate issuer URL: ", issuerURL)
}
}
Expand Down Expand Up @@ -207,8 +187,8 @@ func PrintVerification(imgRef string, verified []oci.Signature, output string) {
if ss.Optional == nil {
ss.Optional = make(map[string]interface{})
}
ss.Optional["Subject"] = certSubject(cert)
if issuerURL := certIssuerExtension(cert); issuerURL != "" {
ss.Optional["Subject"] = sigs.CertSubject(cert)
if issuerURL := sigs.CertIssuerExtension(cert); issuerURL != "" {
ss.Optional["Issuer"] = issuerURL
}
}
Expand Down
42 changes: 32 additions & 10 deletions pkg/cosign/tuf/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,21 +134,41 @@ func (r *Root) Marshal() (*Signed, error) {
return &Signed{Signed: b}, nil
}

func (r *Root) ValidKey(keyID string, role string) bool {
// Checks if id is a valid key for role
if _, ok := r.Keys[keyID]; !ok {
return false
func (r *Root) ValidKey(key *Key, role string) (string, error) {
// Checks if id is a valid key for role by matching the identity and issuer if specified.
// Returns the key ID or an error if invalid key.
fulcioKeyVal, err := GetFulcioKeyVal(key)
if err != nil {
return "", errors.Wrap(err, "error parsing signer key")
}

result := ""
for keyid, rootKey := range r.Keys {
fulcioRootKeyVal, err := GetFulcioKeyVal(rootKey)
if err != nil {
return "", errors.Wrap(err, "error parsing root key")
}
if fulcioKeyVal.Identity == fulcioRootKeyVal.Identity {
if fulcioRootKeyVal.Issuer == "" || fulcioRootKeyVal.Issuer == fulcioKeyVal.Issuer {
result = keyid
break
}
}
}
if result == "" {
return "", errors.New("key not found in root keys")
}

rootRole, ok := r.Roles[role]
if !ok {
return false
return "", errors.New("invalid role")
}
for _, id := range rootRole.KeyIDs {
if id == keyID {
return true
if id == result {
return result, nil
}
}
return false
return "", errors.New("key not found in role")
}

func (s *Signed) JSONMarshal(prefix, indent string) ([]byte, error) {
Expand All @@ -166,12 +186,14 @@ func (s *Signed) JSONMarshal(prefix, indent string) ([]byte, error) {
return out.Bytes(), nil
}

func (s *Signed) AddOrUpdateSignature(signature Signature) error {
func (s *Signed) AddOrUpdateSignature(key *Key, signature Signature) error {
root := &Root{}
if err := json.Unmarshal(s.Signed, root); err != nil {
return errors.Wrap(err, "unmarshalling root policy")
}
if !root.ValidKey(signature.KeyID, "root") {
var err error
signature.KeyID, err = root.ValidKey(key, "root")
if err != nil {
return errors.New("invalid root key")
}
signatures := []Signature{}
Expand Down
23 changes: 23 additions & 0 deletions pkg/cosign/tuf/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,29 @@ func TestAddKey(t *testing.T) {
}
}

func TestValidKey(t *testing.T) {
root := NewRoot()
publicKey := FulcioVerificationKey("[email protected]", "https://accounts.google.com")
if !root.AddKey(publicKey) {
t.Errorf("Adding new key failed")
}
role := &Role{KeyIDs: []string{}, Threshold: 1}
role.AddKeysWithThreshold([]*Key{publicKey}, 2)
root.Roles["root"] = role

if _, ok := root.Keys[publicKey.ID()]; !ok {
t.Errorf("Error adding public key")
}
if _, err := root.ValidKey(publicKey, "root"); err != nil {
t.Errorf("Error checking key validit %s", err)
}
// Now change issuer, and expect error.
publicKey = FulcioVerificationKey("[email protected]", "")
if _, err := root.ValidKey(publicKey, "root"); err == nil {
t.Errorf("Expected invalid key with mismatching issuer")
}
}

func TestRootRole(t *testing.T) {
root := NewRoot()
publicKey := FulcioVerificationKey("[email protected]", "")
Expand Down
8 changes: 7 additions & 1 deletion pkg/cosign/tuf/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ var (

type FulcioKeyVal struct {
Identity string `json:"identity"`
Issuer string `json:"issuer"`
Issuer string `json:"issuer,omitempty"`
}

func FulcioVerificationKey(email string, issuer string) *Key {
Expand All @@ -42,3 +42,9 @@ func FulcioVerificationKey(email string, issuer string) *Key {
Value: keyValBytes,
}
}

func GetFulcioKeyVal(key *Key) (*FulcioKeyVal, error) {
fulcioKeyVal := &FulcioKeyVal{}
err := json.Unmarshal(key.Value, fulcioKeyVal)
return fulcioKeyVal, err
}
20 changes: 20 additions & 0 deletions pkg/signature/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package signature
import (
"context"
"crypto"
"crypto/x509"
"os"
"path/filepath"
"strings"
Expand Down Expand Up @@ -161,3 +162,22 @@ func PublicKeyPem(key signature.PublicKeyProvider, pkOpts ...signature.PublicKey
}
return cryptoutils.MarshalPublicKeyToPEM(pub)
}

func CertSubject(c *x509.Certificate) string {
switch {
case c.EmailAddresses != nil:
return c.EmailAddresses[0]
case c.URIs != nil:
return c.URIs[0].String()
}
return ""
}

func CertIssuerExtension(cert *x509.Certificate) string {
for _, ext := range cert.Extensions {
if ext.Id.String() == "1.3.6.1.4.1.57264.1.1" {
return string(ext.Value)
}
}
return ""
}

0 comments on commit dbad868

Please sign in to comment.