diff --git a/go.mod b/go.mod index 2c92da27891..86eeb022793 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/s3 v1.107.3 github.com/aws/smithy-go v1.27.9 github.com/eggsampler/acme/v3 v3.8.1 - github.com/go-jose/go-jose/v4 v4.1.5 + github.com/go-jose/go-jose/v4 v4.1.6 github.com/go-logr/stdr v1.2.2 github.com/go-sql-driver/mysql v1.9.1 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da diff --git a/go.sum b/go.sum index 435dd4ab1ac..6ee31b9aba5 100644 --- a/go.sum +++ b/go.sum @@ -76,8 +76,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-jose/go-jose/v4 v4.1.5 h1:RjgjO2LOtWOJKUC5wpwY9LR3B3vwVAz6JS2YHfYU6eA= -github.com/go-jose/go-jose/v4 v4.1.5/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= +github.com/go-jose/go-jose/v4 v4.1.6 h1:sNRkftxFJxwsILiQukqp5z83QBSmntVwmgRY7AqBDO4= +github.com/go-jose/go-jose/v4 v4.1.6/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= diff --git a/vendor/github.com/go-jose/go-jose/v4/crypter.go b/vendor/github.com/go-jose/go-jose/v4/crypter.go index e3df79c2dc5..53636c57b33 100644 --- a/vendor/github.com/go-jose/go-jose/v4/crypter.go +++ b/vendor/github.com/go-jose/go-jose/v4/crypter.go @@ -428,6 +428,13 @@ func (ctx *genericEncrypter) Options() EncrypterOptions { } } +func validateContentEncryptionKeySize(cek []byte, cipher contentCipher) error { + if len(cek) != cipher.keySize() { + return ErrInvalidKeySize + } + return nil +} + // Decrypt and validate the object and return the plaintext. This // function does not support multi-recipient. If you desire multi-recipient // decryption use DecryptMulti instead. @@ -495,6 +502,10 @@ func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error) if err != nil { return nil, ErrCryptoFailure } + err = validateContentEncryptionKeySize(cek, cipher) + if err != nil { + return nil, ErrCryptoFailure + } // Found a valid CEK -- let's try to decrypt. plaintext, err = cipher.decrypt(cek, authData, parts) @@ -572,6 +583,10 @@ func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Heade if err != nil { continue } + err = validateContentEncryptionKeySize(cek, cipher) + if err != nil { + continue + } // Found a valid CEK -- let's try to decrypt. plaintext, err = cipher.decrypt(cek, authData, parts) diff --git a/vendor/github.com/go-jose/go-jose/v4/jwe.go b/vendor/github.com/go-jose/go-jose/v4/jwe.go index ae173461b70..a5169010787 100644 --- a/vendor/github.com/go-jose/go-jose/v4/jwe.go +++ b/vendor/github.com/go-jose/go-jose/v4/jwe.go @@ -202,15 +202,6 @@ func (parsed *rawJSONWebEncryption) sanitized( } } - // Note: this must be called _after_ we parse the protected header, - // otherwise fields from the protected header will not get picked up. - var err error - mergedHeaders := obj.mergedHeaders(nil) - obj.Header, err = mergedHeaders.sanitized() - if err != nil { - return nil, fmt.Errorf("go-jose/go-jose: cannot sanitize merged headers: %v (%v)", err, mergedHeaders) - } - if len(parsed.Recipients) == 0 { obj.recipients = []recipientInfo{ { @@ -264,6 +255,15 @@ func (parsed *rawJSONWebEncryption) sanitized( } } + // Note: this must be called _after_ we parse the protected header, + // otherwise fields from the protected header will not get picked up. + mergedHeaders := obj.mergedHeaders(nil) + var err error + obj.Header, err = mergedHeaders.sanitized() + if err != nil { + return nil, fmt.Errorf("go-jose/go-jose: cannot sanitize merged headers: %w", err) + } + obj.iv = parsed.Iv.bytes() obj.ciphertext = parsed.Ciphertext.bytes() obj.tag = parsed.Tag.bytes() diff --git a/vendor/github.com/go-jose/go-jose/v4/jwk.go b/vendor/github.com/go-jose/go-jose/v4/jwk.go index f712ec7ec8f..8b7aa05ec2b 100644 --- a/vendor/github.com/go-jose/go-jose/v4/jwk.go +++ b/vendor/github.com/go-jose/go-jose/v4/jwk.go @@ -177,13 +177,29 @@ func (k JSONWebKey) MarshalJSON() ([]byte, error) { // UnmarshalJSON reads a key from its JSON representation. // // Returns ErrUnsupportedKeyType for unrecognized or unsupported "kty" header values. -func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) { +// +// Directly unmarshaling a JWK is not safe for untrusted input. +func (k *JSONWebKey) UnmarshalJSON(data []byte) error { + return k.unmarshalJSON(data, true) +} + +// unmarshalJSON reads a key from its JSON representation. +// +// If allowPrivate is false, a JWK with private key material is rejected and the +// function is safe for use against untrusted input. +func (k *JSONWebKey) unmarshalJSON(data []byte, allowPrivate bool) (err error) { var raw rawJSONWebKey err = json.Unmarshal(data, &raw) if err != nil { return err } + if !allowPrivate && + (raw.K != nil || raw.D != nil || raw.P != nil || raw.Q != nil || + raw.Dp != nil || raw.Dq != nil || raw.Qi != nil) { + return errors.New("go-jose/go-jose: JWK contains private parameters") + } + certs, err := parseCertificateChain(raw.X5c) if err != nil { return fmt.Errorf("go-jose/go-jose: failed to unmarshal x5c field: %s", err) @@ -505,13 +521,25 @@ func (k *JSONWebKey) Valid() bool { return true } +// MaxRSAModulusBits is the largest RSA modulus size, in bits, that will be +// accepted when parsing a JWK. Applications that need larger keys can raise +// this limit. +// +// If modifying MaxRSAModulusBits, do it before any parsing occurs. +var MaxRSAModulusBits = 4096 + func (key rawJSONWebKey) rsaPublicKey() (*rsa.PublicKey, error) { if key.N == nil || key.E == nil { return nil, fmt.Errorf("go-jose/go-jose: invalid RSA key, missing n/e values") } + n := key.N.bigInt() + if n.BitLen() > MaxRSAModulusBits { + return nil, fmt.Errorf("go-jose/go-jose: invalid RSA key, modulus too large") + } + return &rsa.PublicKey{ - N: key.N.bigInt(), + N: n, E: key.E.toInt(), }, nil } @@ -673,16 +701,23 @@ func (key rawJSONWebKey) rsaPrivateKey() (*rsa.PrivateKey, error) { return nil, fmt.Errorf("go-jose/go-jose: invalid RSA private key, missing %s value(s)", strings.Join(missing, ", ")) } + n := key.N.bigInt() + if n.BitLen() > MaxRSAModulusBits { + return nil, fmt.Errorf("go-jose/go-jose: invalid RSA key, modulus too large") + } + + p, q := key.P.bigInt(), key.Q.bigInt() + if p.BitLen()+q.BitLen() > n.BitLen()+1 || new(big.Int).Mul(p, q).Cmp(n) != 0 { + return nil, errors.New("go-jose/go-jose: invalid RSA private key, p * q != n") + } + rv := &rsa.PrivateKey{ PublicKey: rsa.PublicKey{ - N: key.N.bigInt(), + N: n, E: key.E.toInt(), }, - D: key.D.bigInt(), - Primes: []*big.Int{ - key.P.bigInt(), - key.Q.bigInt(), - }, + D: key.D.bigInt(), + Primes: []*big.Int{p, q}, } if key.Dp != nil { diff --git a/vendor/github.com/go-jose/go-jose/v4/jws.go b/vendor/github.com/go-jose/go-jose/v4/jws.go index 0444a5da05c..18224df7d92 100644 --- a/vendor/github.com/go-jose/go-jose/v4/jws.go +++ b/vendor/github.com/go-jose/go-jose/v4/jws.go @@ -275,17 +275,18 @@ func (parsed *rawJSONWebSignature) sanitized(signatureAlgorithms []SignatureAlgo Signature: parsed.Signature, } + mergedHeaders := signature.mergedHeaders() + alg := mergedHeaders.getSignatureAlgorithm() + if !containsSignatureAlgorithm(signatureAlgorithms, alg) { + return nil, newErrUnexpectedSignatureAlgorithm(alg, signatureAlgorithms) + } + var err error - signature.Header, err = signature.mergedHeaders().sanitized() + signature.Header, err = mergedHeaders.sanitized() if err != nil { return nil, err } - alg := SignatureAlgorithm(signature.Header.Algorithm) - if !containsSignatureAlgorithm(signatureAlgorithms, alg) { - return nil, newErrUnexpectedSignatureAlgorithm(alg, signatureAlgorithms) - } - if signature.header != nil { signature.Unprotected, err = signature.header.sanitized() if err != nil { @@ -323,17 +324,20 @@ func (parsed *rawJSONWebSignature) sanitized(signatureAlgorithms []SignatureAlgo return nil, ErrUnprotectedNonce } - var err error - obj.Signatures[i].Header, err = obj.Signatures[i].mergedHeaders().sanitized() - if err != nil { - return nil, err - } + obj.Signatures[i].header = sig.Header - alg := SignatureAlgorithm(obj.Signatures[i].Header.Algorithm) + mergedHeaders := obj.Signatures[i].mergedHeaders() + alg := mergedHeaders.getSignatureAlgorithm() if !containsSignatureAlgorithm(signatureAlgorithms, alg) { return nil, newErrUnexpectedSignatureAlgorithm(alg, signatureAlgorithms) } + var err error + obj.Signatures[i].Header, err = mergedHeaders.sanitized() + if err != nil { + return nil, err + } + if obj.Signatures[i].header != nil { obj.Signatures[i].Unprotected, err = obj.Signatures[i].header.sanitized() if err != nil { @@ -359,7 +363,6 @@ func (parsed *rawJSONWebSignature) sanitized(signatureAlgorithms []SignatureAlgo // Copy value of sig original := sig - obj.Signatures[i].header = sig.Header obj.Signatures[i].original = &original } diff --git a/vendor/github.com/go-jose/go-jose/v4/jwt/errors.go b/vendor/github.com/go-jose/go-jose/v4/jwt/errors.go index 27388e5449a..b7e1e4bfbb1 100644 --- a/vendor/github.com/go-jose/go-jose/v4/jwt/errors.go +++ b/vendor/github.com/go-jose/go-jose/v4/jwt/errors.go @@ -47,7 +47,7 @@ var ErrNotValidYet = errors.New("go-jose/go-jose/jwt: validation failed, token n var ErrExpired = errors.New("go-jose/go-jose/jwt: validation failed, token is expired (exp)") // ErrIssuedInTheFuture indicates that the iat field is in the future. -var ErrIssuedInTheFuture = errors.New("go-jose/go-jose/jwt: validation field, token issued in the future (iat)") +var ErrIssuedInTheFuture = errors.New("go-jose/go-jose/jwt: validation failed, token issued in the future (iat)") // ErrInvalidContentType indicates that token requires JWT cty header. var ErrInvalidContentType = errors.New("go-jose/go-jose/jwt: expected content type to be JWT (cty header)") diff --git a/vendor/github.com/go-jose/go-jose/v4/shared.go b/vendor/github.com/go-jose/go-jose/v4/shared.go index 3df242ca6a4..1e8bf88210e 100644 --- a/vendor/github.com/go-jose/go-jose/v4/shared.go +++ b/vendor/github.com/go-jose/go-jose/v4/shared.go @@ -297,9 +297,10 @@ func (parsed rawHeader) getEPK() (*JSONWebKey, error) { if v == nil { return nil, nil } - var epk *JSONWebKey - err := json.Unmarshal(*v, &epk) - if err != nil { + // RFC 7518 Section 4.6.1.1: the "epk" header is the ephemeral public key + // of the originator. Reject private key material before parsing it. + epk := new(JSONWebKey) + if err := epk.unmarshalJSON(*v, false); err != nil { return nil, err } return epk, nil @@ -325,20 +326,6 @@ func (parsed rawHeader) getTag() (*byteBuffer, error) { return parsed.getByteBuffer(headerTag) } -// getJWK extracts parsed "jwk" from the raw JSON. -func (parsed rawHeader) getJWK() (*JSONWebKey, error) { - v := parsed[headerJWK] - if v == nil { - return nil, nil - } - var jwk *JSONWebKey - err := json.Unmarshal(*v, &jwk) - if err != nil { - return nil, err - } - return jwk, nil -} - // getCritical extracts parsed "crit" from the raw JSON. If omitted, it // returns an empty slice. func (parsed rawHeader) getCritical() ([]string, error) { @@ -424,10 +411,14 @@ func (parsed rawHeader) sanitized() (h Header, err error) { } switch k { case headerJWK: - var jwk *JSONWebKey - err = json.Unmarshal(*v, &jwk) + // RFC 7515 Section 4.1.3 and RFC 7516 Section 4.1.5: the "jwk" + // header is the public key that corresponds to the key used to + // sign the JWS or encrypt the JWE. Reject private key material + // before parsing it. + jwk := new(JSONWebKey) + err = jwk.unmarshalJSON(*v, false) if err != nil { - err = fmt.Errorf("failed to unmarshal JWK: %v: %#v", err, string(*v)) + err = fmt.Errorf("failed to unmarshal JWK: %w", err) return } h.JSONWebKey = jwk diff --git a/vendor/github.com/go-jose/go-jose/v4/tests.Containerfile b/vendor/github.com/go-jose/go-jose/v4/tests.Containerfile new file mode 100644 index 00000000000..b45da99bd2a --- /dev/null +++ b/vendor/github.com/go-jose/go-jose/v4/tests.Containerfile @@ -0,0 +1,13 @@ +# This containerfile can be used to run the go-jose tests and cli + +FROM golang:1.27-trixie +ENV GOTOOLCHAIN=local + +COPY . /src +WORKDIR /src + +# Install the jose-util cli, which will also build the library +RUN go build -o /usr/local/bin/jose-util ./jose-util + +# By default, run the tests +CMD ["go", "test", "./..."] diff --git a/vendor/github.com/go-jose/go-jose/v4/threat_model.md b/vendor/github.com/go-jose/go-jose/v4/threat_model.md new file mode 100644 index 00000000000..c9e66d4c205 --- /dev/null +++ b/vendor/github.com/go-jose/go-jose/v4/threat_model.md @@ -0,0 +1,25 @@ +# Threat model + +## What this project does and where untrusted input enters +go-jose is a Go implementation of JOSE family of algorithms. + +It is a library which will parse attacker-controlled data in network-reachable +services. + +## Components that matter most / least +The primary attack surface of interest is parsing and validating JWS, JWE, JWK, +etc when this library is used to implement authentication and authorization. + +## How to exercise it +Testing is primarily done by Golang unit tests, which can be run with the +tests.Container file's default CMD. + +## How you rate severity +Any authentication bypass enabled by a go-jose issue is a high+ severity. + +Denial-of-service attacks are of interest when the functions may be used in a protocol like OIDC, ACME, etc +which could have a server processing attacker-controlled data. + +## Anything to leave alone +Any bug which only affects the jose-util CLI tool, but it can be used to exercise the library. + diff --git a/vendor/modules.txt b/vendor/modules.txt index 5f816e38e53..7e280fabb17 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -152,7 +152,7 @@ github.com/felixge/httpsnoop # github.com/fsnotify/fsnotify v1.6.0 ## explicit; go 1.16 github.com/fsnotify/fsnotify -# github.com/go-jose/go-jose/v4 v4.1.5 +# github.com/go-jose/go-jose/v4 v4.1.6 ## explicit; go 1.24.0 github.com/go-jose/go-jose/v4 github.com/go-jose/go-jose/v4/cipher