Skip to content

Commit feacf31

Browse files
JSONWebKeySet: ignore unsupported key types (#130)
Support unmarshaling JSONWebKeySet containing unsupported key types. Return ErrUnsupportedKeyType when unmarshaling JSONWebKey with unsupported key type. Co-authored-by: samiponkanenssh <112563010+samiponkanenssh@users.noreply.github.com>
1 parent ab072bd commit feacf31

3 files changed

Lines changed: 113 additions & 8 deletions

File tree

jwk.go

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,11 @@ func (k JSONWebKey) MarshalJSON() ([]byte, error) {
174174
return json.Marshal(raw)
175175
}
176176

177+
var errUnsupportedJWK = errors.New("go-jose/go-jose: unsupported json web key")
178+
177179
// UnmarshalJSON reads a key from its JSON representation.
180+
//
181+
// Returns ErrUnsupportedKeyType for unrecognized or unsupported "kty" header values.
178182
func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
179183
var raw rawJSONWebKey
180184
err = json.Unmarshal(data, &raw)
@@ -228,7 +232,7 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
228232
}
229233
key, err = raw.symmetricKey()
230234
case "OKP":
231-
if raw.Crv == "Ed25519" && raw.X != nil {
235+
if raw.Crv == "Ed25519" {
232236
if raw.D != nil {
233237
key, err = raw.edPrivateKey()
234238
if err == nil {
@@ -238,17 +242,27 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
238242
key, err = raw.edPublicKey()
239243
keyPub = key
240244
}
241-
} else {
242-
err = fmt.Errorf("go-jose/go-jose: unknown curve %s'", raw.Crv)
243245
}
244-
default:
245-
err = fmt.Errorf("go-jose/go-jose: unknown json web key type '%s'", raw.Kty)
246+
case "":
247+
// kty MUST be present
248+
err = fmt.Errorf("go-jose/go-jose: missing json web key type")
246249
}
247250

248251
if err != nil {
249252
return
250253
}
251254

255+
if key == nil {
256+
// RFC 7517:
257+
// 5. JWK Set Format
258+
// ...
259+
// Implementations SHOULD ignore JWKs within a JWK Set that use "kty"
260+
// (key type) values that are not understood by them, that are missing
261+
// required members, or for which values are out of the supported
262+
// ranges.
263+
return ErrUnsupportedKeyType
264+
}
265+
252266
if certPub != nil && keyPub != nil {
253267
if !reflect.DeepEqual(certPub, keyPub) {
254268
return errors.New("go-jose/go-jose: invalid JWK, public keys in key and x5c fields do not match")
@@ -348,6 +362,35 @@ func (s *JSONWebKeySet) Key(kid string) []JSONWebKey {
348362
return keys
349363
}
350364

365+
func (s *JSONWebKeySet) UnmarshalJSON(data []byte) (err error) {
366+
s.Keys = nil
367+
368+
type rawJSONWebKeySet struct {
369+
Keys []json.RawMessage `json:"keys"`
370+
}
371+
372+
var rs rawJSONWebKeySet
373+
err = json.Unmarshal(data, &rs)
374+
if err != nil {
375+
return err
376+
}
377+
378+
for _, rk := range rs.Keys {
379+
var k JSONWebKey
380+
err = json.Unmarshal(rk, &k)
381+
if err != nil {
382+
// Skip key and continue unmarshalling the rest of the JWK Set
383+
if !errors.Is(err, ErrUnsupportedKeyType) {
384+
return err
385+
}
386+
} else {
387+
s.Keys = append(s.Keys, k)
388+
}
389+
}
390+
391+
return nil
392+
}
393+
351394
const rsaThumbprintTemplate = `{"e":"%s","kty":"RSA","n":"%s"}`
352395
const ecThumbprintTemplate = `{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`
353396
const edThumbprintTemplate = `{"crv":"%s","kty":"OKP","x":"%s"}`

jwk_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import (
2727
"crypto/sha256"
2828
"crypto/x509"
2929
"encoding/hex"
30+
"errors"
31+
"fmt"
3032
"math/big"
3133
"reflect"
3234
"strings"
@@ -663,6 +665,12 @@ func TestWebKeyVectorsInvalid(t *testing.T) {
663665
`{"kty":"EC","crv":"P-256","d":"XXX"}`,
664666
`{"kty":"EC","crv":"ABC","d":"dGVzdA","x":"dGVzdA"}`,
665667
`{"kty":"EC","crv":"P-256","d":"dGVzdA","x":"dGVzdA"}`,
668+
// Invalid oct key
669+
`{"kty":"oct"}`,
670+
`{"kty":"oct","k":"%not-base64url-encoded*"}`,
671+
// Invalid OKP key
672+
`{"kty":"OKP","crv":"Ed25519"}`,
673+
`{"kty":"OKP","crv":"Ed25519","x":"%not-base64url-encoded*"}`,
666674
}
667675

668676
for _, key := range keys {
@@ -674,6 +682,49 @@ func TestWebKeyVectorsInvalid(t *testing.T) {
674682
}
675683
}
676684

685+
// TestJWKUnsupported checks for an error when parsing a JWK with an unsupported key type.
686+
func TestJWKUnsupported(t *testing.T) {
687+
var jwk JSONWebKey
688+
err := jwk.UnmarshalJSON([]byte(`{"kty": "XXX"}`))
689+
if !errors.Is(err, ErrUnsupportedKeyType) {
690+
t.Error("expected ErrUnsupportedKeyType, got:", err)
691+
}
692+
}
693+
694+
// TestJWKUnsupported checks that unmarshaling into a JSONWebKeySet succeeds even if some JWKs have an unsupported key type.
695+
func TestJWKSetIgnoresUnsupported(t *testing.T) {
696+
var jwkSet JSONWebKeySet
697+
err := jwkSet.UnmarshalJSON([]byte(fmt.Sprintf(`
698+
{
699+
"keys": [
700+
%s,
701+
{"kty": "XXX"}
702+
]
703+
}
704+
`, cookbookJWKs[0])))
705+
if err != nil {
706+
t.Error("parsing JWK Set with one unsupported and one supported key:", err)
707+
}
708+
if len(jwkSet.Keys) != 1 {
709+
t.Error("expected one key to be parsed, got:", len(jwkSet.Keys))
710+
}
711+
712+
err = jwkSet.UnmarshalJSON([]byte(`
713+
{
714+
"keys": [
715+
{"kty": "ABC"},
716+
{"kty": "XXX"}
717+
]
718+
}
719+
`))
720+
if err != nil {
721+
t.Error("parsing JWK Set with two unsupported keys:", err)
722+
}
723+
if len(jwkSet.Keys) != 0 {
724+
t.Error("expected zero keys to be parsed, got:", len(jwkSet.Keys))
725+
}
726+
}
727+
677728
// Test vectors from RFC 7520
678729
var cookbookJWKs = []string{
679730
// EC Public
@@ -900,6 +951,16 @@ func TestMarshalUnmarshalJWKSet(t *testing.T) {
900951
if !bytes.Equal(jsonbar, jsonbar2) {
901952
t.Error("roundtrip should not lose information")
902953
}
954+
955+
set3 := JSONWebKeySet{Keys: []JSONWebKey{jwk1}}
956+
err = json.Unmarshal(jsonbar, &set3)
957+
if err != nil {
958+
t.Fatal("problem unmarshalling set", err)
959+
}
960+
961+
if len(set3.Keys) != 2 {
962+
t.Error("unmarshaling should clear any existing keys")
963+
}
903964
}
904965

905966
func TestJWKSetKey(t *testing.T) {

shared.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,10 @@ var (
5353
ErrUnsupportedAlgorithm = errors.New("go-jose/go-jose: unknown/unsupported algorithm")
5454

5555
// ErrUnsupportedKeyType indicates that the given key type/format is not
56-
// supported. This occurs when trying to instantiate an encrypter and passing
57-
// it a key of an unrecognized type or with unsupported parameters, such as
58-
// an RSA private key with more than two primes.
56+
// supported. This occurs when parsing a JWK with an unsupported key type (kty),
57+
// or instantiating a signer or encrypter with an unsupported key type in Go
58+
// (e.g. *dsa.PrivateKey), or a key type in Go that doesn't match the requested
59+
// algorithm.
5960
ErrUnsupportedKeyType = errors.New("go-jose/go-jose: unsupported key type/format")
6061

6162
// ErrInvalidKeySize indicates that the given key is not the correct size

0 commit comments

Comments
 (0)