auth/vendor/github.com/lestrrat-go/jwx/v2/jws/jws.go

857 lines
25 KiB
Go

//go:generate ../tools/cmd/genjws.sh
// Package jws implements the digital signature on JSON based data
// structures as described in https://tools.ietf.org/html/rfc7515
//
// If you do not care about the details, the only things that you
// would need to use are the following functions:
//
// jws.Sign(payload, jws.WithKey(algorithm, key))
// jws.Verify(serialized, jws.WithKey(algorithm, key))
//
// To sign, simply use `jws.Sign`. `payload` is a []byte buffer that
// contains whatever data you want to sign. `alg` is one of the
// jwa.SignatureAlgorithm constants from package jwa. For RSA and
// ECDSA family of algorithms, you will need to prepare a private key.
// For HMAC family, you just need a []byte value. The `jws.Sign`
// function will return the encoded JWS message on success.
//
// To verify, use `jws.Verify`. It will parse the `encodedjws` buffer
// and verify the result using `algorithm` and `key`. Upon successful
// verification, the original payload is returned, so you can work on it.
package jws
import (
"bufio"
"bytes"
"context"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"errors"
"fmt"
"io"
"reflect"
"strings"
"sync"
"unicode"
"unicode/utf8"
"github.com/lestrrat-go/blackmagic"
"github.com/lestrrat-go/jwx/v2/internal/base64"
"github.com/lestrrat-go/jwx/v2/internal/json"
"github.com/lestrrat-go/jwx/v2/internal/pool"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/lestrrat-go/jwx/v2/x25519"
)
var registry = json.NewRegistry()
type payloadSigner struct {
signer Signer
key interface{}
protected Headers
public Headers
}
func (s *payloadSigner) Sign(payload []byte) ([]byte, error) {
return s.signer.Sign(payload, s.key)
}
func (s *payloadSigner) Algorithm() jwa.SignatureAlgorithm {
return s.signer.Algorithm()
}
func (s *payloadSigner) ProtectedHeader() Headers {
return s.protected
}
func (s *payloadSigner) PublicHeader() Headers {
return s.public
}
var signers = make(map[jwa.SignatureAlgorithm]Signer)
var muSigner = &sync.Mutex{}
func makeSigner(alg jwa.SignatureAlgorithm, key interface{}, public, protected Headers) (*payloadSigner, error) {
muSigner.Lock()
signer, ok := signers[alg]
if !ok {
v, err := NewSigner(alg)
if err != nil {
muSigner.Unlock()
return nil, fmt.Errorf(`failed to create payload signer: %w`, err)
}
signers[alg] = v
signer = v
}
muSigner.Unlock()
return &payloadSigner{
signer: signer,
key: key,
public: public,
protected: protected,
}, nil
}
const (
fmtInvalid = iota
fmtCompact
fmtJSON
fmtJSONPretty
fmtMax
)
// silence linters
var _ = fmtInvalid
var _ = fmtMax
func validateKeyBeforeUse(key interface{}) error {
jwkKey, ok := key.(jwk.Key)
if !ok {
converted, err := jwk.FromRaw(key)
if err != nil {
return fmt.Errorf(`could not convert key of type %T to jwk.Key for validation: %w`, key, err)
}
jwkKey = converted
}
return jwkKey.Validate()
}
// Sign generates a JWS message for the given payload and returns
// it in serialized form, which can be in either compact or
// JSON format. Default is compact.
//
// You must pass at least one key to `jws.Sign()` by using `jws.WithKey()`
// option.
//
// jws.Sign(payload, jws.WithKey(alg, key))
// jws.Sign(payload, jws.WithJSON(), jws.WithKey(alg1, key1), jws.WithKey(alg2, key2))
//
// Note that in the second example the `jws.WithJSON()` option is
// specified as well. This is because the compact serialization
// format does not support multiple signatures, and users must
// specifically ask for the JSON serialization format.
//
// Read the documentation for `jws.WithKey()` to learn more about the
// possible values that can be used for `alg` and `key`.
//
// You may create JWS messages with the "none" (jwa.NoSignature) algorithm
// if you use the `jws.WithInsecureNoSignature()` option. This option
// can be combined with one or more signature keys, as well as the
// `jws.WithJSON()` option to generate multiple signatures (though
// the usefulness of such constructs is highly debatable)
//
// Note that this library does not allow you to successfully call `jws.Verify()` on
// signatures with the "none" algorithm. To parse these, use `jws.Parse()` instead.
//
// If you want to use a detached payload, use `jws.WithDetachedPayload()` as
// one of the options. When you use this option, you must always set the
// first parameter (`payload`) to `nil`, or the function will return an error
//
// You may also want to look at how to pass protected headers to the
// signing process, as you will likely be required to set the `b64` field
// when using detached payload.
//
// Look for options that return `jws.SignOption` or `jws.SignVerifyOption`
// for a complete list of options that can be passed to this function.
func Sign(payload []byte, options ...SignOption) ([]byte, error) {
format := fmtCompact
var signers []*payloadSigner
var detached bool
var noneSignature *payloadSigner
var validateKey bool
for _, option := range options {
//nolint:forcetypeassert
switch option.Ident() {
case identSerialization{}:
format = option.Value().(int)
case identInsecureNoSignature{}:
data := option.Value().(*withInsecureNoSignature)
// only the last one is used (we overwrite previous values)
noneSignature = &payloadSigner{
signer: noneSigner{},
protected: data.protected,
}
case identKey{}:
data := option.Value().(*withKey)
alg, ok := data.alg.(jwa.SignatureAlgorithm)
if !ok {
return nil, fmt.Errorf(`jws.Sign: expected algorithm to be of type jwa.SignatureAlgorithm but got (%[1]q, %[1]T)`, data.alg)
}
// No, we don't accept "none" here.
if alg == jwa.NoSignature {
return nil, fmt.Errorf(`jws.Sign: "none" (jwa.NoSignature) cannot be used with jws.WithKey`)
}
signer, err := makeSigner(alg, data.key, data.public, data.protected)
if err != nil {
return nil, fmt.Errorf(`jws.Sign: failed to create signer: %w`, err)
}
signers = append(signers, signer)
case identDetachedPayload{}:
detached = true
if payload != nil {
return nil, fmt.Errorf(`jws.Sign: payload must be nil when jws.WithDetachedPayload() is specified`)
}
payload = option.Value().([]byte)
case identValidateKey{}:
validateKey = option.Value().(bool)
}
}
if noneSignature != nil {
signers = append(signers, noneSignature)
}
lsigner := len(signers)
if lsigner == 0 {
return nil, fmt.Errorf(`jws.Sign: no signers available. Specify an alogirthm and akey using jws.WithKey()`)
}
// Design note: while we could have easily set format = fmtJSON when
// lsigner > 1, I believe the decision to change serialization formats
// must be explicitly stated by the caller. Otherwise I'm pretty sure
// there would be people filing issues saying "I get JSON when I expcted
// compact serialization".
//
// Therefore, instead of making implicit format conversions, we force the
// user to spell it out as `jws.Sign(..., jws.WithJSON(), jws.WithKey(...), jws.WithKey(...))`
if format == fmtCompact && lsigner != 1 {
return nil, fmt.Errorf(`jws.Sign: cannot have multiple signers (keys) specified for compact serialization. Use only one jws.WithKey()`)
}
// Create a Message object with all the bits and bobs, and we'll
// serialize it in the end
var result Message
result.payload = payload
result.signatures = make([]*Signature, 0, len(signers))
for i, signer := range signers {
protected := signer.ProtectedHeader()
if protected == nil {
protected = NewHeaders()
}
if err := protected.Set(AlgorithmKey, signer.Algorithm()); err != nil {
return nil, fmt.Errorf(`failed to set "alg" header: %w`, err)
}
if key, ok := signer.key.(jwk.Key); ok {
if kid := key.KeyID(); kid != "" {
if err := protected.Set(KeyIDKey, kid); err != nil {
return nil, fmt.Errorf(`failed to set "kid" header: %w`, err)
}
}
}
sig := &Signature{
headers: signer.PublicHeader(),
protected: protected,
// cheat. FIXXXXXXMEEEEEE
detached: detached,
}
if validateKey {
if err := validateKeyBeforeUse(signer.key); err != nil {
return nil, fmt.Errorf(`jws.Verify: %w`, err)
}
}
_, _, err := sig.Sign(payload, signer.signer, signer.key)
if err != nil {
return nil, fmt.Errorf(`failed to generate signature for signer #%d (alg=%s): %w`, i, signer.Algorithm(), err)
}
result.signatures = append(result.signatures, sig)
}
switch format {
case fmtJSON:
return json.Marshal(result)
case fmtJSONPretty:
return json.MarshalIndent(result, "", " ")
case fmtCompact:
// Take the only signature object, and convert it into a Compact
// serialization format
var compactOpts []CompactOption
if detached {
compactOpts = append(compactOpts, WithDetached(detached))
}
return Compact(&result, compactOpts...)
default:
return nil, fmt.Errorf(`jws.Sign: invalid serialization format`)
}
}
var allowNoneWhitelist = jwk.WhitelistFunc(func(string) bool {
return false
})
// Verify checks if the given JWS message is verifiable using `alg` and `key`.
// `key` may be a "raw" key (e.g. rsa.PublicKey) or a jwk.Key
//
// If the verification is successful, `err` is nil, and the content of the
// payload that was signed is returned. If you need more fine-grained
// control of the verification process, manually generate a
// `Verifier` in `verify` subpackage, and call `Verify` method on it.
// If you need to access signatures and JOSE headers in a JWS message,
// use `Parse` function to get `Message` object.
//
// Because the use of "none" (jwa.NoSignature) algorithm is strongly discouraged,
// this function DOES NOT consider it a success when `{"alg":"none"}` is
// encountered in the message (it would also be counter intuitive when the code says
// you _verified_ something when in fact it did no such thing). If you want to
// accept messages with "none" signature algorithm, use `jws.Parse` to get the
// raw JWS message.
func Verify(buf []byte, options ...VerifyOption) ([]byte, error) {
var dst *Message
var detachedPayload []byte
var keyProviders []KeyProvider
var keyUsed interface{}
var validateKey bool
ctx := context.Background()
//nolint:forcetypeassert
for _, option := range options {
switch option.Ident() {
case identMessage{}:
dst = option.Value().(*Message)
case identDetachedPayload{}:
detachedPayload = option.Value().([]byte)
case identKey{}:
pair := option.Value().(*withKey)
alg, ok := pair.alg.(jwa.SignatureAlgorithm)
if !ok {
return nil, fmt.Errorf(`WithKey() option must be specified using jwa.SignatureAlgorithm (got %T)`, pair.alg)
}
keyProviders = append(keyProviders, &staticKeyProvider{
alg: alg,
key: pair.key,
})
case identKeyProvider{}:
keyProviders = append(keyProviders, option.Value().(KeyProvider))
case identKeyUsed{}:
keyUsed = option.Value()
case identContext{}:
ctx = option.Value().(context.Context)
case identValidateKey{}:
validateKey = option.Value().(bool)
default:
return nil, fmt.Errorf(`invalid jws.VerifyOption %q passed`, `With`+strings.TrimPrefix(fmt.Sprintf(`%T`, option.Ident()), `jws.ident`))
}
}
if len(keyProviders) < 1 {
return nil, fmt.Errorf(`jws.Verify: no key providers have been provided (see jws.WithKey(), jws.WithKeySet(), jws.WithVerifyAuto(), and jws.WithKeyProvider()`)
}
msg, err := Parse(buf)
if err != nil {
return nil, fmt.Errorf(`failed to parse jws: %w`, err)
}
defer msg.clearRaw()
if detachedPayload != nil {
if len(msg.payload) != 0 {
return nil, fmt.Errorf(`can't specify detached payload for JWS with payload`)
}
msg.payload = detachedPayload
}
// Pre-compute the base64 encoded version of payload
var payload string
if msg.b64 {
payload = base64.EncodeToString(msg.payload)
} else {
payload = string(msg.payload)
}
verifyBuf := pool.GetBytesBuffer()
defer pool.ReleaseBytesBuffer(verifyBuf)
var errs []error
for i, sig := range msg.signatures {
verifyBuf.Reset()
var encodedProtectedHeader string
if rbp, ok := sig.protected.(interface{ rawBuffer() []byte }); ok {
if raw := rbp.rawBuffer(); raw != nil {
encodedProtectedHeader = base64.EncodeToString(raw)
}
}
if encodedProtectedHeader == "" {
protected, err := json.Marshal(sig.protected)
if err != nil {
return nil, fmt.Errorf(`failed to marshal "protected" for signature #%d: %w`, i+1, err)
}
encodedProtectedHeader = base64.EncodeToString(protected)
}
verifyBuf.WriteString(encodedProtectedHeader)
verifyBuf.WriteByte('.')
verifyBuf.WriteString(payload)
for i, kp := range keyProviders {
var sink algKeySink
if err := kp.FetchKeys(ctx, &sink, sig, msg); err != nil {
return nil, fmt.Errorf(`key provider %d failed: %w`, i, err)
}
for _, pair := range sink.list {
// alg is converted here because pair.alg is of type jwa.KeyAlgorithm.
// this may seem ugly, but we're trying to avoid declaring separate
// structs for `alg jwa.KeyAlgorithm` and `alg jwa.SignatureAlgorithm`
//nolint:forcetypeassert
alg := pair.alg.(jwa.SignatureAlgorithm)
key := pair.key
if validateKey {
if err := validateKeyBeforeUse(key); err != nil {
return nil, fmt.Errorf(`jws.Verify: %w`, err)
}
}
verifier, err := NewVerifier(alg)
if err != nil {
return nil, fmt.Errorf(`failed to create verifier for algorithm %q: %w`, alg, err)
}
if err := verifier.Verify(verifyBuf.Bytes(), sig.signature, key); err != nil {
errs = append(errs, err)
continue
}
if keyUsed != nil {
if err := blackmagic.AssignIfCompatible(keyUsed, key); err != nil {
return nil, fmt.Errorf(`failed to assign used key (%T) to %T: %w`, key, keyUsed, err)
}
}
if dst != nil {
*(dst) = *msg
}
return msg.payload, nil
}
}
}
return nil, &verifyError{errs: errs}
}
type verifyError struct {
// Note: when/if we can ditch Go < 1.20, we can change this to a simple
// `err error`, where the value is the result of `errors.Join()`
//
// We also need to implement Unwrap:
// func (e *verifyError) Unwrap() error {
// return e.err
//}
//
// And finally, As() can go away
errs []error
}
func (e *verifyError) Error() string {
return `could not verify message using any of the signatures or keys`
}
func (e *verifyError) As(target interface{}) bool {
for _, err := range e.errs {
if errors.As(err, target) {
return true
}
}
return false
}
// get the value of b64 header field.
// If the field does not exist, returns true (default)
// Otherwise return the value specified by the header field.
func getB64Value(hdr Headers) bool {
b64raw, ok := hdr.Get("b64")
if !ok {
return true // default
}
b64, ok := b64raw.(bool) // default
if !ok {
return false
}
return b64
}
// This is an "optimized" io.ReadAll(). It will attempt to read
// all of the contents from the reader IF the reader is of a certain
// concrete type.
func readAll(rdr io.Reader) ([]byte, bool) {
switch rdr.(type) {
case *bytes.Reader, *bytes.Buffer, *strings.Reader:
data, err := io.ReadAll(rdr)
if err != nil {
return nil, false
}
return data, true
default:
return nil, false
}
}
// Parse parses contents from the given source and creates a jws.Message
// struct. The input can be in either compact or full JSON serialization.
//
// Parse() currently does not take any options, but the API accepts it
// in anticipation of future addition.
func Parse(src []byte, _ ...ParseOption) (*Message, error) {
for i := 0; i < len(src); i++ {
r := rune(src[i])
if r >= utf8.RuneSelf {
r, _ = utf8.DecodeRune(src)
}
if !unicode.IsSpace(r) {
if r == '{' {
return parseJSON(src)
}
return parseCompact(src)
}
}
return nil, fmt.Errorf(`invalid byte sequence`)
}
// Parse parses contents from the given source and creates a jws.Message
// struct. The input can be in either compact or full JSON serialization.
func ParseString(src string) (*Message, error) {
return Parse([]byte(src))
}
// Parse parses contents from the given source and creates a jws.Message
// struct. The input can be in either compact or full JSON serialization.
func ParseReader(src io.Reader) (*Message, error) {
if data, ok := readAll(src); ok {
return Parse(data)
}
rdr := bufio.NewReader(src)
var first rune
for {
r, _, err := rdr.ReadRune()
if err != nil {
return nil, fmt.Errorf(`failed to read rune: %w`, err)
}
if !unicode.IsSpace(r) {
first = r
if err := rdr.UnreadRune(); err != nil {
return nil, fmt.Errorf(`failed to unread rune: %w`, err)
}
break
}
}
var parser func(io.Reader) (*Message, error)
if first == '{' {
parser = parseJSONReader
} else {
parser = parseCompactReader
}
m, err := parser(rdr)
if err != nil {
return nil, fmt.Errorf(`failed to parse jws message: %w`, err)
}
return m, nil
}
func parseJSONReader(src io.Reader) (result *Message, err error) {
var m Message
if err := json.NewDecoder(src).Decode(&m); err != nil {
return nil, fmt.Errorf(`failed to unmarshal jws message: %w`, err)
}
return &m, nil
}
func parseJSON(data []byte) (result *Message, err error) {
var m Message
if err := json.Unmarshal(data, &m); err != nil {
return nil, fmt.Errorf(`failed to unmarshal jws message: %w`, err)
}
return &m, nil
}
// SplitCompact splits a JWT and returns its three parts
// separately: protected headers, payload and signature.
func SplitCompact(src []byte) ([]byte, []byte, []byte, error) {
parts := bytes.Split(src, []byte("."))
if len(parts) < 3 {
return nil, nil, nil, fmt.Errorf(`invalid number of segments`)
}
return parts[0], parts[1], parts[2], nil
}
// SplitCompactString splits a JWT and returns its three parts
// separately: protected headers, payload and signature.
func SplitCompactString(src string) ([]byte, []byte, []byte, error) {
parts := strings.Split(src, ".")
if len(parts) < 3 {
return nil, nil, nil, fmt.Errorf(`invalid number of segments`)
}
return []byte(parts[0]), []byte(parts[1]), []byte(parts[2]), nil
}
// SplitCompactReader splits a JWT and returns its three parts
// separately: protected headers, payload and signature.
func SplitCompactReader(rdr io.Reader) ([]byte, []byte, []byte, error) {
if data, ok := readAll(rdr); ok {
return SplitCompact(data)
}
var protected []byte
var payload []byte
var signature []byte
var periods int
var state int
buf := make([]byte, 4096)
var sofar []byte
for {
// read next bytes
n, err := rdr.Read(buf)
// return on unexpected read error
if err != nil && err != io.EOF {
return nil, nil, nil, fmt.Errorf(`unexpected end of input: %w`, err)
}
// append to current buffer
sofar = append(sofar, buf[:n]...)
// loop to capture multiple '.' in current buffer
for loop := true; loop; {
var i = bytes.IndexByte(sofar, '.')
if i == -1 && err != io.EOF {
// no '.' found -> exit and read next bytes (outer loop)
loop = false
continue
} else if i == -1 && err == io.EOF {
// no '.' found -> process rest and exit
i = len(sofar)
loop = false
} else {
// '.' found
periods++
}
// Reaching this point means we have found a '.' or EOF and process the rest of the buffer
switch state {
case 0:
protected = sofar[:i]
state++
case 1:
payload = sofar[:i]
state++
case 2:
signature = sofar[:i]
}
// Shorten current buffer
if len(sofar) > i {
sofar = sofar[i+1:]
}
}
// Exit on EOF
if err == io.EOF {
break
}
}
if periods != 2 {
return nil, nil, nil, fmt.Errorf(`invalid number of segments`)
}
return protected, payload, signature, nil
}
// parseCompactReader parses a JWS value serialized via compact serialization.
func parseCompactReader(rdr io.Reader) (m *Message, err error) {
protected, payload, signature, err := SplitCompactReader(rdr)
if err != nil {
return nil, fmt.Errorf(`invalid compact serialization format: %w`, err)
}
return parse(protected, payload, signature)
}
func parseCompact(data []byte) (m *Message, err error) {
protected, payload, signature, err := SplitCompact(data)
if err != nil {
return nil, fmt.Errorf(`invalid compact serialization format: %w`, err)
}
return parse(protected, payload, signature)
}
func parse(protected, payload, signature []byte) (*Message, error) {
decodedHeader, err := base64.Decode(protected)
if err != nil {
return nil, fmt.Errorf(`failed to decode protected headers: %w`, err)
}
hdr := NewHeaders()
if err := json.Unmarshal(decodedHeader, hdr); err != nil {
return nil, fmt.Errorf(`failed to parse JOSE headers: %w`, err)
}
var decodedPayload []byte
b64 := getB64Value(hdr)
if !b64 {
decodedPayload = payload
} else {
v, err := base64.Decode(payload)
if err != nil {
return nil, fmt.Errorf(`failed to decode payload: %w`, err)
}
decodedPayload = v
}
decodedSignature, err := base64.Decode(signature)
if err != nil {
return nil, fmt.Errorf(`failed to decode signature: %w`, err)
}
var msg Message
msg.payload = decodedPayload
msg.signatures = append(msg.signatures, &Signature{
protected: hdr,
signature: decodedSignature,
})
msg.b64 = b64
return &msg, nil
}
// RegisterCustomField allows users to specify that a private field
// be decoded as an instance of the specified type. This option has
// a global effect.
//
// For example, suppose you have a custom field `x-birthday`, which
// you want to represent as a string formatted in RFC3339 in JSON,
// but want it back as `time.Time`.
//
// In that case you would register a custom field as follows
//
// jwe.RegisterCustomField(`x-birthday`, timeT)
//
// Then `hdr.Get("x-birthday")` will still return an `interface{}`,
// but you can convert its type to `time.Time`
//
// bdayif, _ := hdr.Get(`x-birthday`)
// bday := bdayif.(time.Time)
func RegisterCustomField(name string, object interface{}) {
registry.Register(name, object)
}
// Helpers for signature verification
var rawKeyToKeyType = make(map[reflect.Type]jwa.KeyType)
var keyTypeToAlgorithms = make(map[jwa.KeyType][]jwa.SignatureAlgorithm)
func init() {
rawKeyToKeyType[reflect.TypeOf([]byte(nil))] = jwa.OctetSeq
rawKeyToKeyType[reflect.TypeOf(ed25519.PublicKey(nil))] = jwa.OKP
rawKeyToKeyType[reflect.TypeOf(rsa.PublicKey{})] = jwa.RSA
rawKeyToKeyType[reflect.TypeOf((*rsa.PublicKey)(nil))] = jwa.RSA
rawKeyToKeyType[reflect.TypeOf(ecdsa.PublicKey{})] = jwa.EC
rawKeyToKeyType[reflect.TypeOf((*ecdsa.PublicKey)(nil))] = jwa.EC
addAlgorithmForKeyType(jwa.OKP, jwa.EdDSA)
for _, alg := range []jwa.SignatureAlgorithm{jwa.HS256, jwa.HS384, jwa.HS512} {
addAlgorithmForKeyType(jwa.OctetSeq, alg)
}
for _, alg := range []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512} {
addAlgorithmForKeyType(jwa.RSA, alg)
}
for _, alg := range []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512} {
addAlgorithmForKeyType(jwa.EC, alg)
}
}
func addAlgorithmForKeyType(kty jwa.KeyType, alg jwa.SignatureAlgorithm) {
keyTypeToAlgorithms[kty] = append(keyTypeToAlgorithms[kty], alg)
}
// AlgorithmsForKey returns the possible signature algorithms that can
// be used for a given key. It only takes in consideration keys/algorithms
// for verification purposes, as this is the only usage where one may need
// dynamically figure out which method to use.
func AlgorithmsForKey(key interface{}) ([]jwa.SignatureAlgorithm, error) {
var kty jwa.KeyType
switch key := key.(type) {
case jwk.Key:
kty = key.KeyType()
case rsa.PublicKey, *rsa.PublicKey, rsa.PrivateKey, *rsa.PrivateKey:
kty = jwa.RSA
case ecdsa.PublicKey, *ecdsa.PublicKey, ecdsa.PrivateKey, *ecdsa.PrivateKey:
kty = jwa.EC
case ed25519.PublicKey, ed25519.PrivateKey, x25519.PublicKey, x25519.PrivateKey:
kty = jwa.OKP
case []byte:
kty = jwa.OctetSeq
default:
return nil, fmt.Errorf(`invalid key %T`, key)
}
algs, ok := keyTypeToAlgorithms[kty]
if !ok {
return nil, fmt.Errorf(`invalid key type %q`, kty)
}
return algs, nil
}
// Because the keys defined in github.com/lestrrat-go/jwx/jwk may also implement
// crypto.Signer, it would be possible for to mix up key types when signing/verifying
// for example, when we specify jws.WithKey(jwa.RSA256, cryptoSigner), the cryptoSigner
// can be for RSA, or any other type that implements crypto.Signer... even if it's for the
// wrong algorithm.
//
// These functions are there to differentiate between the valid KNOWN key types.
// For any other key type that is outside of the Go std library and our own code,
// we must rely on the user to be vigilant.
//
// Notes: symmetric keys are obviously not part of this. for v2 OKP keys,
// x25519 does not implement Sign()
func isValidRSAKey(key interface{}) bool {
switch key.(type) {
case
ecdsa.PrivateKey, *ecdsa.PrivateKey,
ed25519.PrivateKey,
jwk.ECDSAPrivateKey, jwk.OKPPrivateKey:
// these are NOT ok
return false
}
return true
}
func isValidECDSAKey(key interface{}) bool {
switch key.(type) {
case
ed25519.PrivateKey,
rsa.PrivateKey, *rsa.PrivateKey,
jwk.RSAPrivateKey, jwk.OKPPrivateKey:
// these are NOT ok
return false
}
return true
}
func isValidEDDSAKey(key interface{}) bool {
switch key.(type) {
case
ecdsa.PrivateKey, *ecdsa.PrivateKey,
rsa.PrivateKey, *rsa.PrivateKey,
jwk.RSAPrivateKey, jwk.ECDSAPrivateKey:
// these are NOT ok
return false
}
return true
}