@@ -18,50 +18,89 @@ import (
1818 "context"
1919 "crypto"
2020 "crypto/ecdsa"
21- "crypto/elliptic "
21+ "crypto/ed25519 "
2222 "crypto/rand"
23+ "crypto/rsa"
2324 "crypto/sha256"
2425 _ "crypto/sha512" // if user chooses SHA2-384 or SHA2-512 for hash
2526 "crypto/x509"
2627 "encoding/base64"
27- "errors "
28+ "fmt "
2829
2930 protocommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1"
3031 "github.com/sigstore/sigstore/pkg/cryptoutils"
32+ "github.com/sigstore/sigstore/pkg/signature"
3133)
3234
3335type Keypair interface {
3436 GetHashAlgorithm () protocommon.HashAlgorithm
37+ GetSigningAlgorithm () protocommon.PublicKeyDetails
3538 GetHint () []byte
3639 GetKeyAlgorithm () string
40+ GetPublicKey () crypto.PublicKey
3741 GetPublicKeyPem () (string , error )
3842 SignData (ctx context.Context , data []byte ) ([]byte , []byte , error )
3943}
4044
4145type EphemeralKeypairOptions struct {
42- // Optional hint of for signing key
46+ // Optional fingerprint for public key
4347 Hint []byte
44- // TODO: support additional key algorithms
48+ // Optional algorithm for generating signing key
49+ Algorithm protocommon.PublicKeyDetails
4550}
4651
4752type EphemeralKeypair struct {
48- options * EphemeralKeypairOptions
49- privateKey * ecdsa. PrivateKey
50- hashAlgorithm protocommon. HashAlgorithm
53+ options * EphemeralKeypairOptions
54+ privKey crypto. Signer
55+ algDetails signature. AlgorithmDetails
5156}
5257
58+ // NewEphemeralKeypair generates a signing key to be used for a single signature generation.
59+ // Defaults to ECDSA P-256 SHA-256 with a SHA-256 key hint.
5360func NewEphemeralKeypair (opts * EphemeralKeypairOptions ) (* EphemeralKeypair , error ) {
5461 if opts == nil {
5562 opts = & EphemeralKeypairOptions {}
5663 }
5764
58- privateKey , err := ecdsa .GenerateKey (elliptic .P256 (), rand .Reader )
65+ // Default signing algorithm is ECDSA P-256 SHA-256
66+ if opts .Algorithm == protocommon .PublicKeyDetails_PUBLIC_KEY_DETAILS_UNSPECIFIED {
67+ opts .Algorithm = protocommon .PublicKeyDetails_PKIX_ECDSA_P256_SHA_256
68+ }
69+ algDetails , err := signature .GetAlgorithmDetails (opts .Algorithm )
5970 if err != nil {
6071 return nil , err
6172 }
73+ var privKey crypto.Signer
74+ switch kt := algDetails .GetKeyType (); kt {
75+ case signature .ECDSA :
76+ curve , err := algDetails .GetECDSACurve ()
77+ if err != nil {
78+ return nil , err
79+ }
80+ privKey , err = ecdsa .GenerateKey (* curve , rand .Reader )
81+ if err != nil {
82+ return nil , err
83+ }
84+ case signature .RSA :
85+ bitSize , err := algDetails .GetRSAKeySize ()
86+ if err != nil {
87+ return nil , err
88+ }
89+ privKey , err = rsa .GenerateKey (rand .Reader , int (bitSize ))
90+ if err != nil {
91+ return nil , err
92+ }
93+ case signature .ED25519 :
94+ _ , privKey , err = ed25519 .GenerateKey (rand .Reader )
95+ if err != nil {
96+ return nil , err
97+ }
98+ default :
99+ return nil , fmt .Errorf ("unsupported key type: %T" , kt )
100+ }
62101
63102 if opts .Hint == nil {
64- pubKeyBytes , err := x509 .MarshalPKIXPublicKey (privateKey .Public ())
103+ pubKeyBytes , err := x509 .MarshalPKIXPublicKey (privKey .Public ())
65104 if err != nil {
66105 return nil , err
67106 }
@@ -70,63 +109,73 @@ func NewEphemeralKeypair(opts *EphemeralKeypairOptions) (*EphemeralKeypair, erro
70109 }
71110
72111 ephemeralKeypair := EphemeralKeypair {
73- options : opts ,
74- privateKey : privateKey ,
75- hashAlgorithm : protocommon . HashAlgorithm_SHA2_256 ,
112+ options : opts ,
113+ privKey : privKey ,
114+ algDetails : algDetails ,
76115 }
77116
78117 return & ephemeralKeypair , nil
79118}
80119
120+ // GetHashAlgorithm returns the hash algorithm to compute the digest to sign.
81121func (e * EphemeralKeypair ) GetHashAlgorithm () protocommon.HashAlgorithm {
82- return e .hashAlgorithm
122+ return e .algDetails .GetProtoHashType ()
123+ }
124+
125+ // GetSigningAlgorithm returns the signing algorithm of the key.
126+ func (e * EphemeralKeypair ) GetSigningAlgorithm () protocommon.PublicKeyDetails {
127+ return e .algDetails .GetSignatureAlgorithm ()
83128}
84129
130+ // GetHint returns the fingerprint of the public key.
85131func (e * EphemeralKeypair ) GetHint () []byte {
86132 return e .options .Hint
87133}
88134
135+ // GetKeyAlgorithm returns the top-level key algorithm, used as part of requests
136+ // to Fulcio. Prefer PublicKeyDetails for a more precise algorithm.
89137func (e * EphemeralKeypair ) GetKeyAlgorithm () string {
90- return "ECDSA"
138+ switch e .algDetails .GetKeyType () {
139+ case signature .ECDSA :
140+ return "ECDSA"
141+ case signature .RSA :
142+ return "RSA"
143+ case signature .ED25519 :
144+ return "ED25519"
145+ default :
146+ return ""
147+ }
148+ }
149+
150+ // GetPublicKey returns the public key.
151+ func (e * EphemeralKeypair ) GetPublicKey () crypto.PublicKey {
152+ return e .privKey .Public ()
91153}
92154
155+ // GetPublicKeyPem returns the public key in PEM format.
93156func (e * EphemeralKeypair ) GetPublicKeyPem () (string , error ) {
94- pubKeyBytes , err := cryptoutils .MarshalPublicKeyToPEM (e .privateKey .Public ())
157+ pubKeyBytes , err := cryptoutils .MarshalPublicKeyToPEM (e .privKey .Public ())
95158 if err != nil {
96159 return "" , err
97160 }
98161
99162 return string (pubKeyBytes ), nil
100163}
101164
102- func getHashFunc (hashAlgorithm protocommon.HashAlgorithm ) (crypto.Hash , error ) {
103- switch hashAlgorithm {
104- case protocommon .HashAlgorithm_SHA2_256 :
105- return crypto .Hash (crypto .SHA256 ), nil
106- case protocommon .HashAlgorithm_SHA2_384 :
107- return crypto .Hash (crypto .SHA384 ), nil
108- case protocommon .HashAlgorithm_SHA2_512 :
109- return crypto .Hash (crypto .SHA512 ), nil
110- default :
111- var hash crypto.Hash
112- return hash , errors .New ("unsupported hash algorithm" )
113- }
114- }
115-
165+ // SignData returns the signature and the data to sign, which is a digest except when
166+ // signing with Ed25519.
116167func (e * EphemeralKeypair ) SignData (_ context.Context , data []byte ) ([]byte , []byte , error ) {
117- hashFunc , err := getHashFunc (e .hashAlgorithm )
118- if err != nil {
119- return nil , nil , err
168+ hf := e .algDetails .GetHashType ()
169+ dataToSign := data
170+ // RSA, ECDSA, and Ed25519ph sign a digest, while pure Ed25519's interface takes data and hashes during signing
171+ if hf != crypto .Hash (0 ) {
172+ hasher := hf .New ()
173+ hasher .Write (data )
174+ dataToSign = hasher .Sum (nil )
120175 }
121-
122- hasher := hashFunc .New ()
123- hasher .Write (data )
124- digest := hasher .Sum (nil )
125-
126- signature , err := e .privateKey .Sign (rand .Reader , digest , hashFunc )
176+ signature , err := e .privKey .Sign (rand .Reader , dataToSign , hf )
127177 if err != nil {
128178 return nil , nil , err
129179 }
130-
131- return signature , digest , nil
180+ return signature , dataToSign , nil
132181}
0 commit comments