Initial QSfera import
This commit is contained in:
+244
@@ -0,0 +1,244 @@
|
||||
package jwk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/lestrrat-go/blackmagic"
|
||||
"github.com/lestrrat-go/jwx/v3/internal/json"
|
||||
"github.com/lestrrat-go/jwx/v3/jwa"
|
||||
)
|
||||
|
||||
// KeyParser represents a type that can parse a JSON representation of a JWK into
|
||||
// a jwk.Key.
|
||||
// See KeyConvertor for a type that can convert a raw key into a jwk.Key
|
||||
type KeyParser interface {
|
||||
// ParseKey parses a JSON payload to a `jwk.Key` object. The first
|
||||
// argument is an object that contains some hints as to what kind of
|
||||
// key the JSON payload contains.
|
||||
//
|
||||
// If your KeyParser decides that the payload is not something
|
||||
// you can parse, and you would like to continue parsing with
|
||||
// the remaining KeyParser instances that are registered,
|
||||
// return a `jwk.ContinueParseError`. Any other errors will immediately
|
||||
// halt the parsing process.
|
||||
//
|
||||
// When unmarshaling JSON, use the unmarshaler object supplied as
|
||||
// the second argument. This will ensure that the JSON is unmarshaled
|
||||
// in a way that is compatible with the rest of the library.
|
||||
ParseKey(probe *KeyProbe, unmarshaler KeyUnmarshaler, payload []byte) (Key, error)
|
||||
}
|
||||
|
||||
// KeyParseFunc is a type of KeyParser that is based on a function/closure
|
||||
type KeyParseFunc func(probe *KeyProbe, unmarshaler KeyUnmarshaler, payload []byte) (Key, error)
|
||||
|
||||
func (f KeyParseFunc) ParseKey(probe *KeyProbe, unmarshaler KeyUnmarshaler, payload []byte) (Key, error) {
|
||||
return f(probe, unmarshaler, payload)
|
||||
}
|
||||
|
||||
// protects keyParsers
|
||||
var muKeyParser sync.RWMutex
|
||||
|
||||
// list of parsers
|
||||
var keyParsers = []KeyParser{KeyParseFunc(defaultParseKey)}
|
||||
|
||||
// RegisterKeyParser adds a new KeyParser. Parsers are called in FILO order.
|
||||
// That is, the last parser to be registered is called first. There is no
|
||||
// check for duplicate entries.
|
||||
func RegisterKeyParser(kp KeyParser) {
|
||||
muKeyParser.Lock()
|
||||
defer muKeyParser.Unlock()
|
||||
keyParsers = append(keyParsers, kp)
|
||||
}
|
||||
|
||||
func defaultParseKey(probe *KeyProbe, unmarshaler KeyUnmarshaler, data []byte) (Key, error) {
|
||||
var key Key
|
||||
var kty string
|
||||
var d json.RawMessage
|
||||
if err := probe.Get("Kty", &kty); err != nil {
|
||||
return nil, fmt.Errorf(`jwk.Parse: failed to get "kty" hint: %w`, err)
|
||||
}
|
||||
// We ignore errors from this field, as it's optional
|
||||
_ = probe.Get("D", &d)
|
||||
switch v, _ := jwa.LookupKeyType(kty); v {
|
||||
case jwa.RSA():
|
||||
if d != nil {
|
||||
key = newRSAPrivateKey()
|
||||
} else {
|
||||
key = newRSAPublicKey()
|
||||
}
|
||||
case jwa.EC():
|
||||
if d != nil {
|
||||
key = newECDSAPrivateKey()
|
||||
} else {
|
||||
key = newECDSAPublicKey()
|
||||
}
|
||||
case jwa.OctetSeq():
|
||||
key = newSymmetricKey()
|
||||
case jwa.OKP():
|
||||
if d != nil {
|
||||
key = newOKPPrivateKey()
|
||||
} else {
|
||||
key = newOKPPublicKey()
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf(`invalid key type from JSON (%s)`, kty)
|
||||
}
|
||||
|
||||
if err := unmarshaler.UnmarshalKey(data, key); err != nil {
|
||||
return nil, fmt.Errorf(`failed to unmarshal JSON into key (%T): %w`, key, err)
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
type keyUnmarshaler struct {
|
||||
localReg *json.Registry
|
||||
}
|
||||
|
||||
func (ku *keyUnmarshaler) UnmarshalKey(data []byte, key any) error {
|
||||
if ku.localReg != nil {
|
||||
dcKey, ok := key.(json.DecodeCtxContainer)
|
||||
if !ok {
|
||||
return fmt.Errorf(`typed field was requested, but the key (%T) does not support DecodeCtx`, key)
|
||||
}
|
||||
dc := json.NewDecodeCtx(ku.localReg)
|
||||
dcKey.SetDecodeCtx(dc)
|
||||
defer func() { dcKey.SetDecodeCtx(nil) }()
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, key); err != nil {
|
||||
return fmt.Errorf(`failed to unmarshal JSON into key (%T): %w`, key, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// keyProber is the object that starts the probing. When Probe() is called,
|
||||
// it creates (possibly from a cached value) an object that is used to
|
||||
// hold hint values.
|
||||
type keyProber struct {
|
||||
mu sync.RWMutex
|
||||
pool *sync.Pool
|
||||
fields map[string]reflect.StructField
|
||||
typ reflect.Type
|
||||
}
|
||||
|
||||
func (kp *keyProber) AddField(field reflect.StructField) error {
|
||||
kp.mu.Lock()
|
||||
defer kp.mu.Unlock()
|
||||
|
||||
if _, ok := kp.fields[field.Name]; ok {
|
||||
return fmt.Errorf(`field name %s is already registered`, field.Name)
|
||||
}
|
||||
kp.fields[field.Name] = field
|
||||
kp.makeStructType()
|
||||
|
||||
// Update pool (note: the logic is the same, but we need to recreate it
|
||||
// so that we don't accidentally use old stored values)
|
||||
kp.pool = &sync.Pool{
|
||||
New: kp.makeStruct,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kp *keyProber) makeStructType() {
|
||||
// DOES NOT LOCK
|
||||
fields := make([]reflect.StructField, 0, len(kp.fields))
|
||||
for _, f := range kp.fields {
|
||||
fields = append(fields, f)
|
||||
}
|
||||
kp.typ = reflect.StructOf(fields)
|
||||
}
|
||||
|
||||
func (kp *keyProber) makeStruct() any {
|
||||
return reflect.New(kp.typ)
|
||||
}
|
||||
|
||||
func (kp *keyProber) Probe(data []byte) (*KeyProbe, error) {
|
||||
kp.mu.RLock()
|
||||
defer kp.mu.RUnlock()
|
||||
|
||||
// if the field list unchanged, so is the pool object, so effectively
|
||||
// we should be using the cached version
|
||||
v := kp.pool.Get()
|
||||
if v == nil {
|
||||
return nil, fmt.Errorf(`probe: failed to get object from pool`)
|
||||
}
|
||||
rv, ok := v.(reflect.Value)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(`probe: value returned from pool as of type %T, expected reflect.Value`, v)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, rv.Interface()); err != nil {
|
||||
return nil, fmt.Errorf(`probe: failed to unmarshal data: %w`, err)
|
||||
}
|
||||
|
||||
return &KeyProbe{data: rv}, nil
|
||||
}
|
||||
|
||||
// KeyProbe is the object that carries the hints when parsing a key.
|
||||
// The exact list of fields can vary depending on the types of key
|
||||
// that are registered.
|
||||
//
|
||||
// Use `Get()` to access the value of a field.
|
||||
//
|
||||
// The underlying data stored in a KeyProbe is recycled each
|
||||
// time a value is parsed, therefore you are not allowed to hold
|
||||
// onto this object after ParseKey() is done.
|
||||
type KeyProbe struct {
|
||||
data reflect.Value
|
||||
}
|
||||
|
||||
// Get returns the value of the field with the given `name“.
|
||||
// `dst` must be a pointer to a value that can hold the type of
|
||||
// the value of the field, which is determined by the
|
||||
// field type registered through `jwk.RegisterProbeField()`
|
||||
func (kp *KeyProbe) Get(name string, dst any) error {
|
||||
f := kp.data.Elem().FieldByName(name)
|
||||
if !f.IsValid() {
|
||||
return fmt.Errorf(`field %s not found`, name)
|
||||
}
|
||||
|
||||
if err := blackmagic.AssignIfCompatible(dst, f.Addr().Interface()); err != nil {
|
||||
return fmt.Errorf(`failed to assign value of field %q to %T: %w`, name, dst, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// We don't really need the object, we need to know its type
|
||||
var keyProbe = &keyProber{
|
||||
fields: make(map[string]reflect.StructField),
|
||||
}
|
||||
|
||||
// RegisterProbeField adds a new field to be probed during the initial
|
||||
// phase of parsing. This is done by partially parsing the JSON payload,
|
||||
// and we do this by calling `json.Unmarshal` using a dynamic type that
|
||||
// can possibly be modified during runtime. This function is used to
|
||||
// add a new field to this dynamic type.
|
||||
//
|
||||
// Note that the `Name` field for the given `reflect.StructField` must start
|
||||
// with an upper case alphabet, such that it is treated as an exported field.
|
||||
// So for example, if you want to probe the "my_hint" field, you should specify
|
||||
// the field name as "MyHint" or similar.
|
||||
//
|
||||
// Also the field name must be unique. If you believe that your field name may
|
||||
// collide with other packages that may want to add their own probes,
|
||||
// it is the responsibility of the caller
|
||||
// to ensure that the field name is unique (possibly by prefixing the field
|
||||
// name with a unique string). It is important to note that the field name
|
||||
// need not be the same as the JSON field name. For example, your field name
|
||||
// could be "MyPkg_MyHint", while the actual JSON field name could be "my_hint".
|
||||
//
|
||||
// If the field name is not unique, an error is returned.
|
||||
func RegisterProbeField(p reflect.StructField) error {
|
||||
// locking is done inside keyProbe
|
||||
return keyProbe.AddField(p)
|
||||
}
|
||||
|
||||
// KeyUnmarshaler is a thin wrapper around json.Unmarshal. It behaves almost
|
||||
// exactly like json.Unmarshal, but it allows us to add extra magic that
|
||||
// is specific to this library before calling the actual json.Unmarshal.
|
||||
type KeyUnmarshaler interface {
|
||||
UnmarshalKey(data []byte, key any) error
|
||||
}
|
||||
Reference in New Issue
Block a user