Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add signature algorithm to template during certificate creation #1929

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
63 changes: 63 additions & 0 deletions pkg/certmaker/certmaker.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@ package certmaker
import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"os"
"strings"
Expand Down Expand Up @@ -188,6 +192,11 @@ func CreateCertificates(sv signature.SignerVerifier, config KMSConfig,
return fmt.Errorf("error parsing root template: %w", err)
}

rootTmpl.SignatureAlgorithm, err = ToSignatureAlgorithm(rootSigner, crypto.SHA256)
if err != nil {
return fmt.Errorf("error determining signature algorithm: %w", err)
}

rootCert, err := x509util.CreateCertificate(rootTmpl, rootTmpl, rootPubKey, rootSigner)
if err != nil {
return fmt.Errorf("error creating root certificate: %w", err)
Expand Down Expand Up @@ -246,6 +255,11 @@ func CreateCertificates(sv signature.SignerVerifier, config KMSConfig,
return fmt.Errorf("error parsing intermediate template: %w", err)
}

intermediateTmpl.SignatureAlgorithm, err = ToSignatureAlgorithm(intermediateSigner, crypto.SHA256)
if err != nil {
return fmt.Errorf("error determining signature algorithm: %w", err)
}

intermediateCert, err := x509util.CreateCertificate(intermediateTmpl, rootCert, intermediatePubKey, rootSigner)
if err != nil {
return fmt.Errorf("error creating intermediate certificate: %w", err)
Expand Down Expand Up @@ -307,6 +321,11 @@ func CreateCertificates(sv signature.SignerVerifier, config KMSConfig,
return fmt.Errorf("error parsing leaf template: %w", err)
}

leafTmpl.SignatureAlgorithm, err = ToSignatureAlgorithm(signingKey, crypto.SHA256)
if err != nil {
return fmt.Errorf("error determining signature algorithm: %w", err)
}

leafCert, err := x509util.CreateCertificate(leafTmpl, signingCert, leafPubKey, signingKey)
if err != nil {
return fmt.Errorf("error creating leaf certificate: %w", err)
Expand Down Expand Up @@ -522,3 +541,47 @@ func ValidateKMSConfig(config KMSConfig) error {

return nil
}

// ToSignatureAlgorithm returns the x509.SignatureAlgorithm for the given signer and hash algorithm.
func ToSignatureAlgorithm(signer crypto.Signer, hash crypto.Hash) (x509.SignatureAlgorithm, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this to https://github.com/sigstore/fulcio/blob/main/pkg/ca/common.go?

For context, pkg/certmaker was just added to support a new CLI utility, and I'd prefer to add new functions to it that are only used by the utility.

We should still keep the places above where you updated tmpl.SignatureAlgorithm though, that LGTM.

if signer == nil {
return x509.UnknownSignatureAlgorithm, errors.New("signer is nil")
}

pub := signer.Public()
switch pub := pub.(type) {
case *rsa.PublicKey:
switch hash {
case crypto.SHA256:
return x509.SHA256WithRSA, nil
case crypto.SHA384:
return x509.SHA384WithRSA, nil
case crypto.SHA512:
return x509.SHA512WithRSA, nil
case crypto.SHA1:
return x509.SHA1WithRSA, nil
case crypto.MD5:
return x509.MD5WithRSA, nil
default:
return x509.UnknownSignatureAlgorithm, fmt.Errorf("unsupported hash algorithm for RSA: %v", hash)
}
case *ecdsa.PublicKey:
switch hash {
case crypto.SHA256:
return x509.ECDSAWithSHA256, nil
case crypto.SHA384:
return x509.ECDSAWithSHA384, nil
case crypto.SHA512:
return x509.ECDSAWithSHA512, nil
case crypto.SHA1:
return x509.ECDSAWithSHA1, nil
default:
return x509.UnknownSignatureAlgorithm, fmt.Errorf("unsupported hash algorithm for ECDSA: %v", hash)
}
case ed25519.PublicKey:
// Ed25519 has a fixed signature so we don't need to check the hash
return x509.PureEd25519, nil
default:
return x509.UnknownSignatureAlgorithm, fmt.Errorf("unsupported public key type: %T", pub)
}
}
142 changes: 142 additions & 0 deletions pkg/certmaker/certmaker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
Expand Down Expand Up @@ -515,3 +517,143 @@ func TestWriteCertificateToFile(t *testing.T) {
})
}
}

func TestToSignatureAlgorithm(t *testing.T) {
rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatalf("Failed to generate RSA key: %v", err)
}

ecdsaKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("Failed to generate ECDSA key: %v", err)
}

_, ed25519Key, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("Failed to generate Ed25519 key: %v", err)
}

tests := []struct {
name string
signer crypto.Signer
hash crypto.Hash
want x509.SignatureAlgorithm
wantErr bool
}{
{
name: "RSA with SHA256",
signer: rsaKey,
hash: crypto.SHA256,
want: x509.SHA256WithRSA,
wantErr: false,
},
{
name: "RSA with SHA384",
signer: rsaKey,
hash: crypto.SHA384,
want: x509.SHA384WithRSA,
wantErr: false,
},
{
name: "RSA with SHA512",
signer: rsaKey,
hash: crypto.SHA512,
want: x509.SHA512WithRSA,
wantErr: false,
},
{
name: "RSA with SHA1",
signer: rsaKey,
hash: crypto.SHA1,
want: x509.SHA1WithRSA,
wantErr: false,
},
{
name: "RSA with MD5",
signer: rsaKey,
hash: crypto.MD5,
want: x509.MD5WithRSA,
wantErr: false,
},
{
name: "RSA with unsupported hash",
signer: rsaKey,
hash: crypto.MD4,
want: x509.UnknownSignatureAlgorithm,
wantErr: true,
},

{
name: "ECDSA with SHA256",
signer: ecdsaKey,
hash: crypto.SHA256,
want: x509.ECDSAWithSHA256,
wantErr: false,
},
{
name: "ECDSA with SHA384",
signer: ecdsaKey,
hash: crypto.SHA384,
want: x509.ECDSAWithSHA384,
wantErr: false,
},
{
name: "ECDSA with SHA512",
signer: ecdsaKey,
hash: crypto.SHA512,
want: x509.ECDSAWithSHA512,
wantErr: false,
},
{
name: "ECDSA with SHA1",
signer: ecdsaKey,
hash: crypto.SHA1,
want: x509.ECDSAWithSHA1,
wantErr: false,
},
{
name: "ECDSA with unsupported hash",
signer: ecdsaKey,
hash: crypto.MD5,
want: x509.UnknownSignatureAlgorithm,
wantErr: true,
},

{
name: "Ed25519 with any hash",
signer: ed25519Key,
hash: crypto.SHA256,
want: x509.PureEd25519,
wantErr: false,
},
{
name: "Ed25519 with different hash",
signer: ed25519Key,
hash: crypto.SHA512,
want: x509.PureEd25519,
wantErr: false,
},

{
name: "nil signer",
signer: nil,
hash: crypto.SHA256,
want: x509.UnknownSignatureAlgorithm,
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ToSignatureAlgorithm(tt.signer, tt.hash)
if (err != nil) != tt.wantErr {
t.Errorf("ToSignatureAlgorithm() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("ToSignatureAlgorithm() = %v, want %v", got, tt.want)
}
})
}
}
Loading