diff --git a/jose/parse.go b/jose/parse.go index 760c4f16..1236cfaf 100644 --- a/jose/parse.go +++ b/jose/parse.go @@ -358,6 +358,19 @@ func guessJWKAlgorithm(ctx *context, jwk *JSONWebKey) { } } +func guessOpaqueSigner(key crypto.PrivateKey) crypto.PrivateKey { + switch k := key.(type) { + case []byte, *ecdsa.PrivateKey, *rsa.PrivateKey, ed25519.PrivateKey: + return key + case x25519.PrivateKey: + return X25519Signer(k) + case crypto.Signer: + return NewOpaqueSigner(k) + default: + return key + } +} + // guessSignatureAlgorithm returns the signature algorithm for a given private key. func guessSignatureAlgorithm(key crypto.PrivateKey) SignatureAlgorithm { switch k := key.(type) { @@ -371,6 +384,29 @@ func guessSignatureAlgorithm(key crypto.PrivateKey) SignatureAlgorithm { return EdDSA case x25519.PrivateKey, X25519Signer: return XEdDSA + case crypto.Signer: + return guessSignatureAlgorithmFromPublicKey(k.Public()) + case OpaqueSigner: + if algs := k.Algs(); len(algs) > 0 { + return algs[0] + } + if pub := k.Public(); pub != nil && pub.Key != nil { + return guessSignatureAlgorithmFromPublicKey(pub.Key) + } + } + return "" +} + +func guessSignatureAlgorithmFromPublicKey(key crypto.PublicKey) SignatureAlgorithm { + switch k := key.(type) { + case *ecdsa.PublicKey: + return SignatureAlgorithm(getECAlgorithm(k.Curve)) + case *rsa.PublicKey: + return DefaultRSASigAlgorithm + case ed25519.PublicKey: + return EdDSA + case x25519.PublicKey: + return XEdDSA default: return "" } diff --git a/jose/parse_test.go b/jose/parse_test.go index 66997320..7df4f7b5 100644 --- a/jose/parse_test.go +++ b/jose/parse_test.go @@ -10,7 +10,9 @@ import ( "crypto/x509" "encoding/base64" "encoding/json" + "errors" "fmt" + "io" "net/http" "net/http/httptest" "os" @@ -19,6 +21,7 @@ import ( "testing" "github.com/smallstep/assert" + "github.com/stretchr/testify/require" "go.step.sm/crypto/pemutil" "go.step.sm/crypto/x25519" ) @@ -33,6 +36,24 @@ const ( octKey ) +type wrapSigner struct { + crypto.Signer +} + +func (w wrapSigner) Public() crypto.PublicKey { + if w.Signer == nil { + return nil + } + return w.Signer.Public() +} + +func (w wrapSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { + if w.Signer == nil { + return nil, errors.New("not implemented") + } + return w.Signer.Sign(rand, digest, opts) +} + type testdata struct { typ keyType encrypted bool @@ -744,10 +765,18 @@ func Test_guessSignatureAlgorithm(t *testing.T) { return args[last-1] } + p256, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + p384, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) + require.NoError(t, err) + p521, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) + require.NoError(t, err) + rsaKey, err := rsa.GenerateKey(rand.Reader, 2048) + require.NoError(t, err) + _, edKey, err := ed25519.GenerateKey(rand.Reader) + require.NoError(t, err) _, x25519Key, err := x25519.GenerateKey(rand.Reader) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) type args struct { key crypto.PrivateKey @@ -758,14 +787,28 @@ func Test_guessSignatureAlgorithm(t *testing.T) { want SignatureAlgorithm }{ {"byte", args{[]byte("the-key")}, HS256}, - {"ES256", args{must(ecdsa.GenerateKey(elliptic.P256(), rand.Reader))}, ES256}, - {"ES384", args{must(ecdsa.GenerateKey(elliptic.P384(), rand.Reader))}, ES384}, - {"ES512", args{must(ecdsa.GenerateKey(elliptic.P521(), rand.Reader))}, ES512}, - {"RS256", args{must(rsa.GenerateKey(rand.Reader, 2048))}, RS256}, - {"EdDSA", args{must(ed25519.GenerateKey(rand.Reader))}, EdDSA}, + {"ES256", args{p256}, ES256}, + {"ES384", args{p384}, ES384}, + {"ES512", args{p521}, ES512}, + {"RS256", args{rsaKey}, RS256}, + {"EdDSA", args{edKey}, EdDSA}, {"XEdDSA", args{x25519Key}, XEdDSA}, {"XEdDSA with X25519Signer", args{X25519Signer(x25519Key)}, XEdDSA}, + {"signer ES256", args{wrapSigner{p256}}, ES256}, + {"signer ES384", args{wrapSigner{p384}}, ES384}, + {"signer ES512", args{wrapSigner{p521}}, ES512}, + {"signer RS256", args{wrapSigner{rsaKey}}, RS256}, + {"signer EdDSA", args{wrapSigner{edKey}}, EdDSA}, + {"signer XEdDSA", args{wrapSigner{x25519Key}}, XEdDSA}, + {"opaque ES256", args{NewOpaqueSigner(p256)}, ES256}, + {"opaque ES384", args{NewOpaqueSigner(p384)}, ES384}, + {"opaque ES512", args{NewOpaqueSigner(p521)}, ES512}, + {"opaque RS256", args{NewOpaqueSigner(rsaKey)}, RS256}, + {"opaque EdDSA", args{NewOpaqueSigner(edKey)}, EdDSA}, + {"opaque XEdDSA", args{NewOpaqueSigner(x25519Key)}, XEdDSA}, {"empty", args{must(ecdsa.GenerateKey(elliptic.P224(), rand.Reader))}, ""}, + {"signer empty", args{wrapSigner{}}, ""}, + {"opaque empty", args{NewOpaqueSigner(wrapSigner{})}, ""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/jose/types.go b/jose/types.go index f0347638..4d7af244 100644 --- a/jose/types.go +++ b/jose/types.go @@ -248,9 +248,7 @@ func UnixNumericDate(s int64) *NumericDate { // NewSigner creates an appropriate signer based on the key type func NewSigner(sig SigningKey, opts *SignerOptions) (Signer, error) { - if k, ok := sig.Key.(x25519.PrivateKey); ok { - sig.Key = X25519Signer(k) - } + sig.Key = guessOpaqueSigner(sig.Key) if sig.Algorithm == "" { sig.Algorithm = guessSignatureAlgorithm(sig.Key) } diff --git a/jose/types_test.go b/jose/types_test.go index 3edd56d6..e9a4604c 100644 --- a/jose/types_test.go +++ b/jose/types_test.go @@ -139,6 +139,8 @@ func TestSignVerify(t *testing.T) { {"rsa2048", args{SigningKey{Key: rsa2048}, nil}, false}, {"ed", args{SigningKey{Key: edKey}, nil}, false}, {"x25519", args{SigningKey{Key: xKey}, nil}, false}, + {"signer", args{SigningKey{Key: wrapSigner{edKey}}, nil}, false}, + {"opaque", args{SigningKey{Key: NewOpaqueSigner(edKey)}, nil}, false}, {"fail P224", args{SigningKey{Key: p224}, nil}, true}, } for _, tt := range tests {