Skip to content

Commit e19adc5

Browse files
authored
Merge pull request #689 from smallstep/mariano/jose-wrapped-signer
Extend jose.NewSigner support for other types
2 parents 83f6f48 + ca63c5e commit e19adc5

File tree

4 files changed

+97
-15
lines changed

4 files changed

+97
-15
lines changed

jose/parse.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,19 @@ func guessJWKAlgorithm(ctx *context, jwk *JSONWebKey) {
358358
}
359359
}
360360

361+
func guessOpaqueSigner(key crypto.PrivateKey) crypto.PrivateKey {
362+
switch k := key.(type) {
363+
case []byte, *ecdsa.PrivateKey, *rsa.PrivateKey, ed25519.PrivateKey:
364+
return key
365+
case x25519.PrivateKey:
366+
return X25519Signer(k)
367+
case crypto.Signer:
368+
return NewOpaqueSigner(k)
369+
default:
370+
return key
371+
}
372+
}
373+
361374
// guessSignatureAlgorithm returns the signature algorithm for a given private key.
362375
func guessSignatureAlgorithm(key crypto.PrivateKey) SignatureAlgorithm {
363376
switch k := key.(type) {
@@ -371,6 +384,29 @@ func guessSignatureAlgorithm(key crypto.PrivateKey) SignatureAlgorithm {
371384
return EdDSA
372385
case x25519.PrivateKey, X25519Signer:
373386
return XEdDSA
387+
case crypto.Signer:
388+
return guessSignatureAlgorithmFromPublicKey(k.Public())
389+
case OpaqueSigner:
390+
if algs := k.Algs(); len(algs) > 0 {
391+
return algs[0]
392+
}
393+
if pub := k.Public(); pub != nil && pub.Key != nil {
394+
return guessSignatureAlgorithmFromPublicKey(pub.Key)
395+
}
396+
}
397+
return ""
398+
}
399+
400+
func guessSignatureAlgorithmFromPublicKey(key crypto.PublicKey) SignatureAlgorithm {
401+
switch k := key.(type) {
402+
case *ecdsa.PublicKey:
403+
return SignatureAlgorithm(getECAlgorithm(k.Curve))
404+
case *rsa.PublicKey:
405+
return DefaultRSASigAlgorithm
406+
case ed25519.PublicKey:
407+
return EdDSA
408+
case x25519.PublicKey:
409+
return XEdDSA
374410
default:
375411
return ""
376412
}

jose/parse_test.go

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import (
1010
"crypto/x509"
1111
"encoding/base64"
1212
"encoding/json"
13+
"errors"
1314
"fmt"
15+
"io"
1416
"net/http"
1517
"net/http/httptest"
1618
"os"
@@ -19,6 +21,7 @@ import (
1921
"testing"
2022

2123
"github.com/smallstep/assert"
24+
"github.com/stretchr/testify/require"
2225
"go.step.sm/crypto/pemutil"
2326
"go.step.sm/crypto/x25519"
2427
)
@@ -33,6 +36,24 @@ const (
3336
octKey
3437
)
3538

39+
type wrapSigner struct {
40+
crypto.Signer
41+
}
42+
43+
func (w wrapSigner) Public() crypto.PublicKey {
44+
if w.Signer == nil {
45+
return nil
46+
}
47+
return w.Signer.Public()
48+
}
49+
50+
func (w wrapSigner) Sign(r io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
51+
if w.Signer == nil {
52+
return nil, errors.New("not implemented")
53+
}
54+
return w.Signer.Sign(r, digest, opts)
55+
}
56+
3657
type testdata struct {
3758
typ keyType
3859
encrypted bool
@@ -744,10 +765,18 @@ func Test_guessSignatureAlgorithm(t *testing.T) {
744765
return args[last-1]
745766
}
746767

768+
p256, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
769+
require.NoError(t, err)
770+
p384, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
771+
require.NoError(t, err)
772+
p521, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
773+
require.NoError(t, err)
774+
rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
775+
require.NoError(t, err)
776+
_, edKey, err := ed25519.GenerateKey(rand.Reader)
777+
require.NoError(t, err)
747778
_, x25519Key, err := x25519.GenerateKey(rand.Reader)
748-
if err != nil {
749-
t.Fatal(err)
750-
}
779+
require.NoError(t, err)
751780

752781
type args struct {
753782
key crypto.PrivateKey
@@ -758,14 +787,28 @@ func Test_guessSignatureAlgorithm(t *testing.T) {
758787
want SignatureAlgorithm
759788
}{
760789
{"byte", args{[]byte("the-key")}, HS256},
761-
{"ES256", args{must(ecdsa.GenerateKey(elliptic.P256(), rand.Reader))}, ES256},
762-
{"ES384", args{must(ecdsa.GenerateKey(elliptic.P384(), rand.Reader))}, ES384},
763-
{"ES512", args{must(ecdsa.GenerateKey(elliptic.P521(), rand.Reader))}, ES512},
764-
{"RS256", args{must(rsa.GenerateKey(rand.Reader, 2048))}, RS256},
765-
{"EdDSA", args{must(ed25519.GenerateKey(rand.Reader))}, EdDSA},
790+
{"ES256", args{p256}, ES256},
791+
{"ES384", args{p384}, ES384},
792+
{"ES512", args{p521}, ES512},
793+
{"RS256", args{rsaKey}, RS256},
794+
{"EdDSA", args{edKey}, EdDSA},
766795
{"XEdDSA", args{x25519Key}, XEdDSA},
767796
{"XEdDSA with X25519Signer", args{X25519Signer(x25519Key)}, XEdDSA},
797+
{"signer ES256", args{wrapSigner{p256}}, ES256},
798+
{"signer ES384", args{wrapSigner{p384}}, ES384},
799+
{"signer ES512", args{wrapSigner{p521}}, ES512},
800+
{"signer RS256", args{wrapSigner{rsaKey}}, RS256},
801+
{"signer EdDSA", args{wrapSigner{edKey}}, EdDSA},
802+
{"signer XEdDSA", args{wrapSigner{x25519Key}}, XEdDSA},
803+
{"opaque ES256", args{NewOpaqueSigner(p256)}, ES256},
804+
{"opaque ES384", args{NewOpaqueSigner(p384)}, ES384},
805+
{"opaque ES512", args{NewOpaqueSigner(p521)}, ES512},
806+
{"opaque RS256", args{NewOpaqueSigner(rsaKey)}, RS256},
807+
{"opaque EdDSA", args{NewOpaqueSigner(edKey)}, EdDSA},
808+
{"opaque XEdDSA", args{NewOpaqueSigner(x25519Key)}, XEdDSA},
768809
{"empty", args{must(ecdsa.GenerateKey(elliptic.P224(), rand.Reader))}, ""},
810+
{"signer empty", args{wrapSigner{}}, ""},
811+
{"opaque empty", args{NewOpaqueSigner(wrapSigner{})}, ""},
769812
}
770813
for _, tt := range tests {
771814
t.Run(tt.name, func(t *testing.T) {

jose/types.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,7 @@ func UnixNumericDate(s int64) *NumericDate {
248248

249249
// NewSigner creates an appropriate signer based on the key type
250250
func NewSigner(sig SigningKey, opts *SignerOptions) (Signer, error) {
251-
if k, ok := sig.Key.(x25519.PrivateKey); ok {
252-
sig.Key = X25519Signer(k)
253-
}
251+
sig.Key = guessOpaqueSigner(sig.Key)
254252
if sig.Algorithm == "" {
255253
sig.Algorithm = guessSignatureAlgorithm(sig.Key)
256254
}

jose/types_test.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ func TestSignVerify(t *testing.T) {
139139
{"rsa2048", args{SigningKey{Key: rsa2048}, nil}, false},
140140
{"ed", args{SigningKey{Key: edKey}, nil}, false},
141141
{"x25519", args{SigningKey{Key: xKey}, nil}, false},
142+
{"signer", args{SigningKey{Key: wrapSigner{edKey}}, nil}, false},
143+
{"opaque", args{SigningKey{Key: NewOpaqueSigner(edKey)}, nil}, false},
142144
{"fail P224", args{SigningKey{Key: p224}, nil}, true},
143145
}
144146
for _, tt := range tests {
@@ -162,10 +164,13 @@ func TestSignVerify(t *testing.T) {
162164
}
163165

164166
var claims Claims
165-
if signer, ok := tt.args.sig.Key.(crypto.Signer); ok {
166-
err = Verify(jwt, signer.Public(), &claims)
167-
} else {
168-
err = Verify(jwt, tt.args.sig.Key, &claims)
167+
switch k := tt.args.sig.Key.(type) {
168+
case crypto.Signer:
169+
err = Verify(jwt, k.Public(), &claims)
170+
case OpaqueSigner:
171+
err = Verify(jwt, k.Public(), &claims)
172+
default:
173+
err = Verify(jwt, k, &claims)
169174
}
170175
if err != nil {
171176
t.Errorf("JSONWebSignature.Verify() error = %v", err)

0 commit comments

Comments
 (0)