Skip to content

Commit

Permalink
Extend jose.NewSigner support for other types
Browse files Browse the repository at this point in the history
This commit adds support for custom crypto.Signer types for signing
tokens. With this, one can use the signer implemented in a KMS or even
 step + step-kms-plugin to sign JWTs.
  • Loading branch information
maraino committed Jan 28, 2025
1 parent 83f6f48 commit 10eba47
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 11 deletions.
36 changes: 36 additions & 0 deletions jose/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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 ""
}
Expand Down
59 changes: 51 additions & 8 deletions jose/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import (
"crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/http/httptest"
"os"
Expand All @@ -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"
)
Expand All @@ -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) {

Check failure on line 50 in jose/parse_test.go

View workflow job for this annotation

GitHub Actions / ci / lint / lint

importShadow: shadow of imported package 'rand' (gocritic)
if w.Signer == nil {
return nil, errors.New("not implemented")
}
return w.Signer.Sign(rand, digest, opts)
}

type testdata struct {
typ keyType
encrypted bool
Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand Down
4 changes: 1 addition & 3 deletions jose/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
2 changes: 2 additions & 0 deletions jose/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 10eba47

Please sign in to comment.