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

790 lines
22 KiB
Go

//go:generate ../tools/cmd/genjwk.sh
// Package jwk implements JWK as described in https://tools.ietf.org/html/rfc7517
package jwk
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io"
"math/big"
"github.com/lestrrat-go/jwx/v2/internal/base64"
"github.com/lestrrat-go/jwx/v2/internal/ecutil"
"github.com/lestrrat-go/jwx/v2/internal/json"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/x25519"
)
var registry = json.NewRegistry()
func bigIntToBytes(n *big.Int) ([]byte, error) {
if n == nil {
return nil, fmt.Errorf(`invalid *big.Int value`)
}
return n.Bytes(), nil
}
// FromRaw creates a jwk.Key from the given key (RSA/ECDSA/symmetric keys).
//
// The constructor auto-detects the type of key to be instantiated
// based on the input type:
//
// - "crypto/rsa".PrivateKey and "crypto/rsa".PublicKey creates an RSA based key
// - "crypto/ecdsa".PrivateKey and "crypto/ecdsa".PublicKey creates an EC based key
// - "crypto/ed25519".PrivateKey and "crypto/ed25519".PublicKey creates an OKP based key
// - []byte creates a symmetric key
func FromRaw(key interface{}) (Key, error) {
if key == nil {
return nil, fmt.Errorf(`jwk.FromRaw requires a non-nil key`)
}
var ptr interface{}
switch v := key.(type) {
case rsa.PrivateKey:
ptr = &v
case rsa.PublicKey:
ptr = &v
case ecdsa.PrivateKey:
ptr = &v
case ecdsa.PublicKey:
ptr = &v
default:
ptr = v
}
switch rawKey := ptr.(type) {
case *rsa.PrivateKey:
k := newRSAPrivateKey()
if err := k.FromRaw(rawKey); err != nil {
return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err)
}
return k, nil
case *rsa.PublicKey:
k := newRSAPublicKey()
if err := k.FromRaw(rawKey); err != nil {
return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err)
}
return k, nil
case *ecdsa.PrivateKey:
k := newECDSAPrivateKey()
if err := k.FromRaw(rawKey); err != nil {
return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err)
}
return k, nil
case *ecdsa.PublicKey:
k := newECDSAPublicKey()
if err := k.FromRaw(rawKey); err != nil {
return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err)
}
return k, nil
case ed25519.PrivateKey:
k := newOKPPrivateKey()
if err := k.FromRaw(rawKey); err != nil {
return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err)
}
return k, nil
case ed25519.PublicKey:
k := newOKPPublicKey()
if err := k.FromRaw(rawKey); err != nil {
return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err)
}
return k, nil
case x25519.PrivateKey:
k := newOKPPrivateKey()
if err := k.FromRaw(rawKey); err != nil {
return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err)
}
return k, nil
case x25519.PublicKey:
k := newOKPPublicKey()
if err := k.FromRaw(rawKey); err != nil {
return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err)
}
return k, nil
case []byte:
k := newSymmetricKey()
if err := k.FromRaw(rawKey); err != nil {
return nil, fmt.Errorf(`failed to initialize %T from %T: %w`, k, rawKey, err)
}
return k, nil
default:
return nil, fmt.Errorf(`invalid key type '%T' for jwk.New`, key)
}
}
// PublicSetOf returns a new jwk.Set consisting of
// public keys of the keys contained in the set.
//
// This is useful when you are generating a set of private keys, and
// you want to generate the corresponding public versions for the
// users to verify with.
//
// Be aware that all fields will be copied onto the new public key. It is the caller's
// responsibility to remove any fields, if necessary.
func PublicSetOf(v Set) (Set, error) {
newSet := NewSet()
n := v.Len()
for i := 0; i < n; i++ {
k, ok := v.Key(i)
if !ok {
return nil, fmt.Errorf(`key not found`)
}
pubKey, err := PublicKeyOf(k)
if err != nil {
return nil, fmt.Errorf(`failed to get public key of %T: %w`, k, err)
}
if err := newSet.AddKey(pubKey); err != nil {
return nil, fmt.Errorf(`failed to add key to public key set: %w`, err)
}
}
return newSet, nil
}
// PublicKeyOf returns the corresponding public version of the jwk.Key.
// If `v` is a SymmetricKey, then the same value is returned.
// If `v` is already a public key, the key itself is returned.
//
// If `v` is a private key type that has a `PublicKey()` method, be aware
// that all fields will be copied onto the new public key. It is the caller's
// responsibility to remove any fields, if necessary
//
// If `v` is a raw key, the key is first converted to a `jwk.Key`
func PublicKeyOf(v interface{}) (Key, error) {
// This should catch all jwk.Key instances
if pk, ok := v.(PublicKeyer); ok {
return pk.PublicKey()
}
jk, err := FromRaw(v)
if err != nil {
return nil, fmt.Errorf(`failed to convert key into JWK: %w`, err)
}
return jk.PublicKey()
}
// PublicRawKeyOf returns the corresponding public key of the given
// value `v` (e.g. given *rsa.PrivateKey, *rsa.PublicKey is returned)
// If `v` is already a public key, the key itself is returned.
//
// The returned value will always be a pointer to the public key,
// except when a []byte (e.g. symmetric key, ed25519 key) is passed to `v`.
// In this case, the same []byte value is returned.
func PublicRawKeyOf(v interface{}) (interface{}, error) {
if pk, ok := v.(PublicKeyer); ok {
pubk, err := pk.PublicKey()
if err != nil {
return nil, fmt.Errorf(`failed to obtain public key from %T: %w`, v, err)
}
var raw interface{}
if err := pubk.Raw(&raw); err != nil {
return nil, fmt.Errorf(`failed to obtain raw key from %T: %w`, pubk, err)
}
return raw, nil
}
// This may be a silly idea, but if the user gave us a non-pointer value...
var ptr interface{}
switch v := v.(type) {
case rsa.PrivateKey:
ptr = &v
case rsa.PublicKey:
ptr = &v
case ecdsa.PrivateKey:
ptr = &v
case ecdsa.PublicKey:
ptr = &v
default:
ptr = v
}
switch x := ptr.(type) {
case *rsa.PrivateKey:
return &x.PublicKey, nil
case *rsa.PublicKey:
return x, nil
case *ecdsa.PrivateKey:
return &x.PublicKey, nil
case *ecdsa.PublicKey:
return x, nil
case ed25519.PrivateKey:
return x.Public(), nil
case ed25519.PublicKey:
return x, nil
case x25519.PrivateKey:
return x.Public(), nil
case x25519.PublicKey:
return x, nil
case []byte:
return x, nil
default:
return nil, fmt.Errorf(`invalid key type passed to PublicKeyOf (%T)`, v)
}
}
const (
pmPrivateKey = `PRIVATE KEY`
pmPublicKey = `PUBLIC KEY`
pmECPrivateKey = `EC PRIVATE KEY`
pmRSAPublicKey = `RSA PUBLIC KEY`
pmRSAPrivateKey = `RSA PRIVATE KEY`
)
// EncodeX509 encodes the key into a byte sequence in ASN.1 DER format
// suitable for to be PEM encoded. The key can be a jwk.Key or a raw key
// instance, but it must be one of the types supported by `x509` package.
//
// This function will try to do the right thing depending on the key type
// (i.e. switch between `x509.MarshalPKCS1PRivateKey` and `x509.MarshalECPrivateKey`),
// but for public keys, it will always use `x509.MarshalPKIXPublicKey`.
// Please manually perform the encoding if you need more fine grained control
//
// The first return value is the name that can be used for `(pem.Block).Type`.
// The second return value is the encoded byte sequence.
func EncodeX509(v interface{}) (string, []byte, error) {
// we can't import jwk, so just use the interface
if key, ok := v.(interface{ Raw(interface{}) error }); ok {
var raw interface{}
if err := key.Raw(&raw); err != nil {
return "", nil, fmt.Errorf(`failed to get raw key out of %T: %w`, key, err)
}
v = raw
}
// Try to convert it into a certificate
switch v := v.(type) {
case *rsa.PrivateKey:
return pmRSAPrivateKey, x509.MarshalPKCS1PrivateKey(v), nil
case *ecdsa.PrivateKey:
marshaled, err := x509.MarshalECPrivateKey(v)
if err != nil {
return "", nil, err
}
return pmECPrivateKey, marshaled, nil
case ed25519.PrivateKey:
marshaled, err := x509.MarshalPKCS8PrivateKey(v)
if err != nil {
return "", nil, err
}
return pmPrivateKey, marshaled, nil
case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey:
marshaled, err := x509.MarshalPKIXPublicKey(v)
if err != nil {
return "", nil, err
}
return pmPublicKey, marshaled, nil
default:
return "", nil, fmt.Errorf(`unsupported type %T for ASN.1 DER encoding`, v)
}
}
// EncodePEM encodes the key into a PEM encoded ASN.1 DER format.
// The key can be a jwk.Key or a raw key instance, but it must be one of
// the types supported by `x509` package.
//
// Internally, it uses the same routine as `jwk.EncodeX509()`, and therefore
// the same caveats apply
func EncodePEM(v interface{}) ([]byte, error) {
typ, marshaled, err := EncodeX509(v)
if err != nil {
return nil, fmt.Errorf(`failed to encode key in x509: %w`, err)
}
block := &pem.Block{
Type: typ,
Bytes: marshaled,
}
return pem.EncodeToMemory(block), nil
}
// DecodePEM decodes a key in PEM encoded ASN.1 DER format.
// and returns a raw key
func DecodePEM(src []byte) (interface{}, []byte, error) {
block, rest := pem.Decode(src)
if block == nil {
return nil, nil, fmt.Errorf(`failed to decode PEM data`)
}
switch block.Type {
// Handle the semi-obvious cases
case pmRSAPrivateKey:
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, nil, fmt.Errorf(`failed to parse PKCS1 private key: %w`, err)
}
return key, rest, nil
case pmRSAPublicKey:
key, err := x509.ParsePKCS1PublicKey(block.Bytes)
if err != nil {
return nil, nil, fmt.Errorf(`failed to parse PKCS1 public key: %w`, err)
}
return key, rest, nil
case pmECPrivateKey:
key, err := x509.ParseECPrivateKey(block.Bytes)
if err != nil {
return nil, nil, fmt.Errorf(`failed to parse EC private key: %w`, err)
}
return key, rest, nil
case pmPublicKey:
// XXX *could* return dsa.PublicKey
key, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, nil, fmt.Errorf(`failed to parse PKIX public key: %w`, err)
}
return key, rest, nil
case pmPrivateKey:
key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, nil, fmt.Errorf(`failed to parse PKCS8 private key: %w`, err)
}
return key, rest, nil
case "CERTIFICATE":
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, nil, fmt.Errorf(`failed to parse certificate: %w`, err)
}
return cert.PublicKey, rest, nil
default:
return nil, nil, fmt.Errorf(`invalid PEM block type %s`, block.Type)
}
}
// ParseRawKey is a combination of ParseKey and Raw. It parses a single JWK key,
// and assigns the "raw" key to the given parameter. The key must either be
// a pointer to an empty interface, or a pointer to the actual raw key type
// such as *rsa.PrivateKey, *ecdsa.PublicKey, *[]byte, etc.
func ParseRawKey(data []byte, rawkey interface{}) error {
key, err := ParseKey(data)
if err != nil {
return fmt.Errorf(`failed to parse key: %w`, err)
}
if err := key.Raw(rawkey); err != nil {
return fmt.Errorf(`failed to assign to raw key variable: %w`, err)
}
return nil
}
type setDecodeCtx struct {
json.DecodeCtx
ignoreParseError bool
}
func (ctx *setDecodeCtx) IgnoreParseError() bool {
return ctx.ignoreParseError
}
// ParseKey parses a single key JWK. Unlike `jwk.Parse` this method will
// report failure if you attempt to pass a JWK set. Only use this function
// when you know that the data is a single JWK.
//
// Given a WithPEM(true) option, this function assumes that the given input
// is PEM encoded ASN.1 DER format key.
//
// Note that a successful parsing of any type of key does NOT necessarily
// guarantee a valid key. For example, no checks against expiration dates
// are performed for certificate expiration, no checks against missing
// parameters are performed, etc.
func ParseKey(data []byte, options ...ParseOption) (Key, error) {
var parsePEM bool
var localReg *json.Registry
for _, option := range options {
//nolint:forcetypeassert
switch option.Ident() {
case identPEM{}:
parsePEM = option.Value().(bool)
case identLocalRegistry{}:
// in reality you can only pass either withLocalRegistry or
// WithTypedField, but since withLocalRegistry is used only by us,
// we skip checking
localReg = option.Value().(*json.Registry)
case identTypedField{}:
pair := option.Value().(typedFieldPair)
if localReg == nil {
localReg = json.NewRegistry()
}
localReg.Register(pair.Name, pair.Value)
case identIgnoreParseError{}:
return nil, fmt.Errorf(`jwk.WithIgnoreParseError() cannot be used for ParseKey()`)
}
}
if parsePEM {
raw, _, err := DecodePEM(data)
if err != nil {
return nil, fmt.Errorf(`failed to parse PEM encoded key: %w`, err)
}
return FromRaw(raw)
}
var hint struct {
Kty string `json:"kty"`
D json.RawMessage `json:"d"`
}
if err := json.Unmarshal(data, &hint); err != nil {
return nil, fmt.Errorf(`failed to unmarshal JSON into key hint: %w`, err)
}
var key Key
switch jwa.KeyType(hint.Kty) {
case jwa.RSA:
if len(hint.D) > 0 {
key = newRSAPrivateKey()
} else {
key = newRSAPublicKey()
}
case jwa.EC:
if len(hint.D) > 0 {
key = newECDSAPrivateKey()
} else {
key = newECDSAPublicKey()
}
case jwa.OctetSeq:
key = newSymmetricKey()
case jwa.OKP:
if len(hint.D) > 0 {
key = newOKPPrivateKey()
} else {
key = newOKPPublicKey()
}
default:
return nil, fmt.Errorf(`invalid key type from JSON (%s)`, hint.Kty)
}
if localReg != nil {
dcKey, ok := key.(json.DecodeCtxContainer)
if !ok {
return nil, fmt.Errorf(`typed field was requested, but the key (%T) does not support DecodeCtx`, key)
}
dc := json.NewDecodeCtx(localReg)
dcKey.SetDecodeCtx(dc)
defer func() { dcKey.SetDecodeCtx(nil) }()
}
if err := json.Unmarshal(data, key); err != nil {
return nil, fmt.Errorf(`failed to unmarshal JSON into key (%T): %w`, key, err)
}
return key, nil
}
// Parse parses JWK from the incoming []byte.
//
// For JWK sets, this is a convenience function. You could just as well
// call `json.Unmarshal` against an empty set created by `jwk.NewSet()`
// to parse a JSON buffer into a `jwk.Set`.
//
// This function exists because many times the user does not know before hand
// if a JWK(s) resource at a remote location contains a single JWK key or
// a JWK set, and `jwk.Parse()` can handle either case, returning a JWK Set
// even if the data only contains a single JWK key
//
// If you are looking for more information on how JWKs are parsed, or if
// you know for sure that you have a single key, please see the documentation
// for `jwk.ParseKey()`.
func Parse(src []byte, options ...ParseOption) (Set, error) {
var parsePEM bool
var localReg *json.Registry
var ignoreParseError bool
for _, option := range options {
//nolint:forcetypeassert
switch option.Ident() {
case identPEM{}:
parsePEM = option.Value().(bool)
case identIgnoreParseError{}:
ignoreParseError = option.Value().(bool)
case identTypedField{}:
pair := option.Value().(typedFieldPair)
if localReg == nil {
localReg = json.NewRegistry()
}
localReg.Register(pair.Name, pair.Value)
}
}
s := NewSet()
if parsePEM {
src = bytes.TrimSpace(src)
for len(src) > 0 {
raw, rest, err := DecodePEM(src)
if err != nil {
return nil, fmt.Errorf(`failed to parse PEM encoded key: %w`, err)
}
key, err := FromRaw(raw)
if err != nil {
return nil, fmt.Errorf(`failed to create jwk.Key from %T: %w`, raw, err)
}
if err := s.AddKey(key); err != nil {
return nil, fmt.Errorf(`failed to add jwk.Key to set: %w`, err)
}
src = bytes.TrimSpace(rest)
}
return s, nil
}
if localReg != nil || ignoreParseError {
dcKs, ok := s.(KeyWithDecodeCtx)
if !ok {
return nil, fmt.Errorf(`typed field was requested, but the key set (%T) does not support DecodeCtx`, s)
}
dc := &setDecodeCtx{
DecodeCtx: json.NewDecodeCtx(localReg),
ignoreParseError: ignoreParseError,
}
dcKs.SetDecodeCtx(dc)
defer func() { dcKs.SetDecodeCtx(nil) }()
}
if err := json.Unmarshal(src, s); err != nil {
return nil, fmt.Errorf(`failed to unmarshal JWK set: %w`, err)
}
return s, nil
}
// ParseReader parses a JWK set from the incoming byte buffer.
func ParseReader(src io.Reader, options ...ParseOption) (Set, error) {
// meh, there's no way to tell if a stream has "ended" a single
// JWKs except when we encounter an EOF, so just... ReadAll
buf, err := io.ReadAll(src)
if err != nil {
return nil, fmt.Errorf(`failed to read from io.Reader: %w`, err)
}
return Parse(buf, options...)
}
// ParseString parses a JWK set from the incoming string.
func ParseString(s string, options ...ParseOption) (Set, error) {
return Parse([]byte(s), options...)
}
// AssignKeyID is a convenience function to automatically assign the "kid"
// section of the key, if it already doesn't have one. It uses Key.Thumbprint
// method with crypto.SHA256 as the default hashing algorithm
func AssignKeyID(key Key, options ...AssignKeyIDOption) error {
if _, ok := key.Get(KeyIDKey); ok {
return nil
}
hash := crypto.SHA256
for _, option := range options {
//nolint:forcetypeassert
switch option.Ident() {
case identThumbprintHash{}:
hash = option.Value().(crypto.Hash)
}
}
h, err := key.Thumbprint(hash)
if err != nil {
return fmt.Errorf(`failed to generate thumbprint: %w`, err)
}
if err := key.Set(KeyIDKey, base64.EncodeToString(h)); err != nil {
return fmt.Errorf(`failed to set "kid": %w`, err)
}
return nil
}
func cloneKey(src Key) (Key, error) {
var dst Key
switch src.(type) {
case RSAPrivateKey:
dst = newRSAPrivateKey()
case RSAPublicKey:
dst = newRSAPublicKey()
case ECDSAPrivateKey:
dst = newECDSAPrivateKey()
case ECDSAPublicKey:
dst = newECDSAPublicKey()
case OKPPrivateKey:
dst = newOKPPrivateKey()
case OKPPublicKey:
dst = newOKPPublicKey()
case SymmetricKey:
dst = newSymmetricKey()
default:
return nil, fmt.Errorf(`unknown key type %T`, src)
}
for _, pair := range src.makePairs() {
//nolint:forcetypeassert
key := pair.Key.(string)
if err := dst.Set(key, pair.Value); err != nil {
return nil, fmt.Errorf(`failed to set %q: %w`, key, err)
}
}
return dst, nil
}
// Pem serializes the given jwk.Key in PEM encoded ASN.1 DER format,
// using either PKCS8 for private keys and PKIX for public keys.
// If you need to encode using PKCS1 or SEC1, you must do it yourself.
//
// # Argument must be of type jwk.Key or jwk.Set
//
// Currently only EC (including Ed25519) and RSA keys (and jwk.Set
// comprised of these key types) are supported.
func Pem(v interface{}) ([]byte, error) {
var set Set
switch v := v.(type) {
case Key:
set = NewSet()
if err := set.AddKey(v); err != nil {
return nil, fmt.Errorf(`failed to add key to set: %w`, err)
}
case Set:
set = v
default:
return nil, fmt.Errorf(`argument to Pem must be either jwk.Key or jwk.Set: %T`, v)
}
var ret []byte
for i := 0; i < set.Len(); i++ {
key, _ := set.Key(i)
typ, buf, err := asnEncode(key)
if err != nil {
return nil, fmt.Errorf(`failed to encode content for key #%d: %w`, i, err)
}
var block pem.Block
block.Type = typ
block.Bytes = buf
ret = append(ret, pem.EncodeToMemory(&block)...)
}
return ret, nil
}
func asnEncode(key Key) (string, []byte, error) {
switch key := key.(type) {
case RSAPrivateKey, ECDSAPrivateKey, OKPPrivateKey:
var rawkey interface{}
if err := key.Raw(&rawkey); err != nil {
return "", nil, fmt.Errorf(`failed to get raw key from jwk.Key: %w`, err)
}
buf, err := x509.MarshalPKCS8PrivateKey(rawkey)
if err != nil {
return "", nil, fmt.Errorf(`failed to marshal PKCS8: %w`, err)
}
return pmPrivateKey, buf, nil
case RSAPublicKey, ECDSAPublicKey, OKPPublicKey:
var rawkey interface{}
if err := key.Raw(&rawkey); err != nil {
return "", nil, fmt.Errorf(`failed to get raw key from jwk.Key: %w`, err)
}
buf, err := x509.MarshalPKIXPublicKey(rawkey)
if err != nil {
return "", nil, fmt.Errorf(`failed to marshal PKIX: %w`, err)
}
return pmPublicKey, buf, nil
default:
return "", nil, fmt.Errorf(`unsupported key type %T`, key)
}
}
// 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
//
// jwk.RegisterCustomField(`x-birthday`, timeT)
//
// Then `key.Get("x-birthday")` will still return an `interface{}`,
// but you can convert its type to `time.Time`
//
// bdayif, _ := key.Get(`x-birthday`)
// bday := bdayif.(time.Time)
func RegisterCustomField(name string, object interface{}) {
registry.Register(name, object)
}
func AvailableCurves() []elliptic.Curve {
return ecutil.AvailableCurves()
}
func CurveForAlgorithm(alg jwa.EllipticCurveAlgorithm) (elliptic.Curve, bool) {
return ecutil.CurveForAlgorithm(alg)
}
// Equal compares two keys and returns true if they are equal. The comparison
// is solely done against the thumbprints of k1 and k2. It is possible for keys
// that have, for example, different key IDs, key usage, etc, to be considered equal.
func Equal(k1, k2 Key) bool {
h := crypto.SHA256
tp1, err := k1.Thumbprint(h)
if err != nil {
return false // can't report error
}
tp2, err := k2.Thumbprint(h)
if err != nil {
return false // can't report error
}
return bytes.Equal(tp1, tp2)
}
// IsPrivateKey returns true if the supplied key is a private key of an
// asymmetric key pair. The argument `k` must implement the `AsymmetricKey`
// interface.
//
// An error is returned if the supplied key is not an `AsymmetricKey`.
func IsPrivateKey(k Key) (bool, error) {
asymmetric, ok := k.(AsymmetricKey)
if ok {
return asymmetric.IsPrivate(), nil
}
return false, fmt.Errorf("jwk.IsPrivateKey: %T is not an asymmetric key", k)
}
type keyValidationError struct {
err error
}
func (e *keyValidationError) Error() string {
return fmt.Sprintf(`key validation failed: %s`, e.err)
}
func (e *keyValidationError) Unwrap() error {
return e.err
}
func (e *keyValidationError) Is(target error) bool {
_, ok := target.(*keyValidationError)
return ok
}
// NewKeyValidationError wraps the given error with an error that denotes
// `key.Validate()` has failed. This error type should ONLY be used as
// return value from the `Validate()` method.
func NewKeyValidationError(err error) error {
return &keyValidationError{err: err}
}
func IsKeyValidationError(err error) bool {
var kve keyValidationError
return errors.Is(err, &kve)
}