Merge pull request #150 from caos/key-selection

fix: handle keys without `use` in FindMatchingKey
This commit is contained in:
Fabi 2022-01-28 09:53:29 +01:00 committed by GitHub
commit 219ba4e038
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 130 additions and 8 deletions

View file

@ -53,7 +53,7 @@ func FindKey(keyID, use, expectedAlg string, keys ...jose.JSONWebKey) (jose.JSON
return key, err == nil return key, err == nil
} }
//FindMatchingKey searches the given JSON Web Keys for the requested key ID, usage and key type //FindMatchingKey searches the given JSON Web Keys for the requested key ID, usage and alg type
// //
//will return the key immediately if matches exact (id, usage, type) //will return the key immediately if matches exact (id, usage, type)
// //
@ -61,15 +61,27 @@ func FindKey(keyID, use, expectedAlg string, keys ...jose.JSONWebKey) (jose.JSON
func FindMatchingKey(keyID, use, expectedAlg string, keys ...jose.JSONWebKey) (key jose.JSONWebKey, err error) { func FindMatchingKey(keyID, use, expectedAlg string, keys ...jose.JSONWebKey) (key jose.JSONWebKey, err error) {
var validKeys []jose.JSONWebKey var validKeys []jose.JSONWebKey
for _, k := range keys { for _, k := range keys {
if k.Use == use && algToKeyType(k.Key, expectedAlg) { //ignore all keys with wrong use (let empty use of published key pass)
if k.Use != use && k.Use != "" {
continue
}
//ignore all keys with wrong algorithm type
if !algToKeyType(k.Key, expectedAlg) {
continue
}
//if we get here, use and alg match, so an equal (not empty) keyID is an exact match
if k.KeyID == keyID && keyID != "" { if k.KeyID == keyID && keyID != "" {
return k, nil return k, nil
} }
//keyIDs did not match or at least one was empty (if later, then it could be a match)
if k.KeyID == "" || keyID == "" { if k.KeyID == "" || keyID == "" {
validKeys = append(validKeys, k) validKeys = append(validKeys, k)
} }
} }
} //if we get here, no match was possible at all (use / alg) or no exact match due to
//the signed JWT and / or the published keys didn't have a kid
//if later applies and only one key could be found, we'll return it
//otherwise a corresponding error will be thrown
if len(validKeys) == 1 { if len(validKeys) == 1 {
return validKeys[0], nil return validKeys[0], nil
} }

View file

@ -1,6 +1,7 @@
package oidc package oidc
import ( import (
"crypto/ecdsa"
"crypto/rsa" "crypto/rsa"
"errors" "errors"
"reflect" "reflect"
@ -139,6 +140,27 @@ func TestFindKey(t *testing.T) {
err: nil, err: nil,
}, },
}, },
{
"single key no use, jwt with kid, match",
args{
keyID: "id",
use: KeyUseSignature,
expectedAlg: "RS256",
keys: []jose.JSONWebKey{
{
KeyID: "id",
Key: &rsa.PublicKey{},
},
},
},
res{
key: jose.JSONWebKey{
KeyID: "id",
Key: &rsa.PublicKey{},
},
err: nil,
},
},
{ {
"single key wrong kid, ErrKeyNone", "single key wrong kid, ErrKeyNone",
args{ args{
@ -304,6 +326,94 @@ func TestFindKey(t *testing.T) {
err: nil, err: nil,
}, },
}, },
{
"multiple keys, no use, jwt with kid, match",
args{
keyID: "id1",
use: KeyUseSignature,
expectedAlg: "RS256",
keys: []jose.JSONWebKey{
{
KeyID: "id1",
Key: &rsa.PublicKey{},
},
{
KeyID: "id2",
Key: &rsa.PublicKey{},
},
},
},
res{
key: jose.JSONWebKey{
KeyID: "id1",
Key: &rsa.PublicKey{},
},
err: nil,
},
},
{
"multiple keys, no use, jwt without kid, ErrKeyMultiple",
args{
use: KeyUseSignature,
expectedAlg: "RS256",
keys: []jose.JSONWebKey{
{
KeyID: "id1",
Key: &rsa.PublicKey{},
},
{
KeyID: "id2",
Key: &rsa.PublicKey{},
},
},
},
res{
key: jose.JSONWebKey{},
err: ErrKeyMultiple,
},
},
{
"multiple keys, no use or id, jwt with kid, ErrKeyMultiple",
args{
use: KeyUseSignature,
expectedAlg: "RS256",
keyID: "id1",
keys: []jose.JSONWebKey{
{
Key: &rsa.PublicKey{},
},
{
Key: &rsa.PublicKey{},
},
},
},
res{
key: jose.JSONWebKey{},
err: ErrKeyMultiple,
},
},
{
"multiple keys (only one matching alg), jwt with kid, match",
args{
use: KeyUseSignature,
expectedAlg: "RS256",
keyID: "id1",
keys: []jose.JSONWebKey{
{
Key: &rsa.PublicKey{},
},
{
Key: &ecdsa.PublicKey{},
},
},
},
res{
key: jose.JSONWebKey{
Key: &rsa.PublicKey{},
},
err: nil,
},
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {