--- a/go.mod +++ b/go.mod @@ -176,7 +176,7 @@ github.com/foxboron/go-tpm-keyfiles v0.0.0-20251226215517-609e4778396f // indirect github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect - github.com/go-jose/go-jose/v4 v4.1.4 // indirect + github.com/go-jose/go-jose/v4 v4.1.6 // indirect github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect --- a/go.sum +++ b/go.sum @@ -247,8 +247,8 @@ github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= -github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= -github.com/go-jose/go-jose/v4 v4.1.4/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-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= --- a/vendor/github.com/go-jose/go-jose/v4/asymmetric.go +++ b/vendor/github.com/go-jose/go-jose/v4/asymmetric.go @@ -21,6 +21,7 @@ "crypto/aes" "crypto/ecdsa" "crypto/ed25519" + "crypto/elliptic" "crypto/rand" "crypto/rsa" "crypto/sha1" @@ -195,11 +196,11 @@ func (ctx rsaEncrypterVerifier) encrypt(cek []byte, alg KeyAlgorithm) ([]byte, error) { switch alg { case RSA1_5: - return rsa.EncryptPKCS1v15(RandReader, ctx.publicKey, cek) + return rsa.EncryptPKCS1v15(randReader, ctx.publicKey, cek) case RSA_OAEP: - return rsa.EncryptOAEP(sha1.New(), RandReader, ctx.publicKey, cek, []byte{}) + return rsa.EncryptOAEP(sha1.New(), randReader, ctx.publicKey, cek, []byte{}) case RSA_OAEP_256: - return rsa.EncryptOAEP(sha256.New(), RandReader, ctx.publicKey, cek, []byte{}) + return rsa.EncryptOAEP(sha256.New(), randReader, ctx.publicKey, cek, []byte{}) } return nil, ErrUnsupportedAlgorithm @@ -288,9 +289,9 @@ // TODO(https://github.com/go-jose/go-jose/issues/40): As of go1.20, the // random parameter is legacy and ignored, and it can be nil. // https://cs.opensource.google/go/go/+/refs/tags/go1.20:src/crypto/rsa/pkcs1v15.go;l=263;bpv=0;bpt=1 - out, err = rsa.SignPKCS1v15(RandReader, ctx.privateKey, hash, hashed) + out, err = rsa.SignPKCS1v15(randReader, ctx.privateKey, hash, hashed) case PS256, PS384, PS512: - out, err = rsa.SignPSS(RandReader, ctx.privateKey, hash, hashed, &rsa.PSSOptions{ + out, err = rsa.SignPSS(randReader, ctx.privateKey, hash, hashed, &rsa.PSSOptions{ SaltLength: rsa.PSSSaltLengthEqualsHash, }) } @@ -391,7 +392,7 @@ // Get a content encryption key for ECDH-ES func (ctx ecKeyGenerator) genKey() ([]byte, rawHeader, error) { - priv, err := ecdsa.GenerateKey(ctx.publicKey.Curve, RandReader) + priv, err := ecdsa.GenerateKey(ctx.publicKey.Curve, randReader) if err != nil { return nil, rawHeader{}, err } @@ -483,7 +484,7 @@ return Signature{}, ErrUnsupportedAlgorithm } - sig, err := ctx.privateKey.Sign(RandReader, payload, crypto.Hash(0)) + sig, err := ctx.privateKey.Sign(randReader, payload, crypto.Hash(0)) if err != nil { return Signature{}, err } @@ -533,7 +534,7 @@ _, _ = hasher.Write(payload) hashed := hasher.Sum(nil) - r, s, err := ecdsa.Sign(RandReader, ctx.privateKey, hashed) + r, s, err := ecdsa.Sign(randReader, ctx.privateKey, hashed) if err != nil { return Signature{}, err } @@ -571,12 +572,21 @@ case ES256: keySize = 32 hash = crypto.SHA256 + if ctx.publicKey.Curve != elliptic.P256() { + return fmt.Errorf("go-jose/go-jose: signature uses different algorithm than public key") + } case ES384: keySize = 48 hash = crypto.SHA384 + if ctx.publicKey.Curve != elliptic.P384() { + return fmt.Errorf("go-jose/go-jose: signature uses different algorithm than public key") + } case ES512: keySize = 66 hash = crypto.SHA512 + if ctx.publicKey.Curve != elliptic.P521() { + return fmt.Errorf("go-jose/go-jose: signature uses different algorithm than public key") + } default: return ErrUnsupportedAlgorithm } --- a/vendor/github.com/go-jose/go-jose/v4/cipher/cbc_hmac.go +++ b/vendor/github.com/go-jose/go-jose/v4/cipher/cbc_hmac.go @@ -51,6 +51,8 @@ hash = sha512.New384 case 32: hash = sha512.New + default: + return nil, errors.New("go-jose/go-jose: invalid key size for CBC-HMAC") } return &cbcAEAD{ @@ -176,7 +178,9 @@ // Remove padding func unpadBuffer(buffer []byte, blockSize int) ([]byte, error) { - if len(buffer)%blockSize != 0 { + // A padded buffer can't be empty because an empty input is padded with + // `blockSize` bytes, resulting in a non-empty ciphertext. + if len(buffer) == 0 || len(buffer)%blockSize != 0 { return nil, errors.New("go-jose/go-jose: invalid padding") } --- a/vendor/github.com/go-jose/go-jose/v4/crypter.go +++ b/vendor/github.com/go-jose/go-jose/v4/crypter.go @@ -258,6 +258,10 @@ } recipientInfo, err = makeJWERecipient(recipient.Algorithm, recipient.Key) + if err != nil { + return err + } + if recipient.KeyID != "" { recipientInfo.keyID = recipient.KeyID } @@ -270,10 +274,8 @@ } } - if err == nil { - ctx.recipients = append(ctx.recipients, recipientInfo) - } - return err + ctx.recipients = append(ctx.recipients, recipientInfo) + return nil } func makeJWERecipient(alg KeyAlgorithm, encryptionKey interface{}) (recipientKeyInfo, error) { @@ -426,6 +428,13 @@ } } +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. @@ -490,21 +499,23 @@ recipientHeaders := obj.mergedHeaders(&recipient) cek, err := decrypter.decryptKey(recipientHeaders, &recipient, generator) - if err == nil { - // Found a valid CEK -- let's try to decrypt. - plaintext, err = cipher.decrypt(cek, authData, parts) + if err != nil { + return nil, ErrCryptoFailure + } + err = validateContentEncryptionKeySize(cek, cipher) + if err != nil { + return nil, ErrCryptoFailure } - if plaintext == nil { + // Found a valid CEK -- let's try to decrypt. + plaintext, err = cipher.decrypt(cek, authData, parts) + if err != nil { return nil, ErrCryptoFailure } - // The "zip" header parameter may only be present in the protected header. - if comp := obj.protected.getCompression(); comp != "" { - plaintext, err = decompress(comp, plaintext) - if err != nil { - return nil, fmt.Errorf("go-jose/go-jose: failed to decompress plaintext: %v", err) - } + plaintext, err = obj.decompress(plaintext) + if err != nil { + return nil, err } return plaintext, nil @@ -559,31 +570,42 @@ var plaintext []byte var headers rawHeader + if len(obj.recipients) == 0 { + return -1, Header{}, nil, errors.New("go-jose/go-jose: no recipients") + } + + // Loop sets `err` in the function scope; don't shadow it. for i, recipient := range obj.recipients { recipientHeaders := obj.mergedHeaders(&recipient) - cek, err := decrypter.decryptKey(recipientHeaders, &recipient, generator) - if err == nil { - // Found a valid CEK -- let's try to decrypt. - plaintext, err = cipher.decrypt(cek, authData, parts) - if err == nil { - index = i - headers = recipientHeaders - break - } + var cek []byte + cek, err = decrypter.decryptKey(recipientHeaders, &recipient, generator) + 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) + if err != nil { + continue + } + + index = i + headers = recipientHeaders + break } - if plaintext == nil { + if err != nil { return -1, Header{}, nil, ErrCryptoFailure } - // The "zip" header parameter may only be present in the protected header. - if comp := obj.protected.getCompression(); comp != "" { - plaintext, err = decompress(comp, plaintext) - if err != nil { - return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: failed to decompress plaintext: %v", err) - } + plaintext, err = obj.decompress(plaintext) + if err != nil { + return -1, Header{}, nil, err } sanitized, err := headers.sanitized() @@ -593,3 +615,22 @@ return index, sanitized, plaintext, err } + +// decompress decompresses plaintext using the protected "zip" header, if present. +// It returns plaintext unchanged when there is no protected header or "zip" value. +func (obj JSONWebEncryption) decompress(plaintext []byte) ([]byte, error) { + if obj.protected == nil { + return plaintext, nil + } + + comp := obj.protected.getCompression() + if comp == "" { + return plaintext, nil + } + + plaintext, err := decompress(comp, plaintext) + if err != nil { + return nil, fmt.Errorf("go-jose/go-jose: failed to decompress plaintext: %v", err) + } + return plaintext, nil +} --- a/vendor/github.com/go-jose/go-jose/v4/json/scanner.go +++ b/vendor/github.com/go-jose/go-jose/v4/json/scanner.go @@ -140,6 +140,10 @@ parseArrayValue // parsing array value ) +// This limits the max nesting depth to prevent stack overflow. +// This is permitted by https://tools.ietf.org/html/rfc7159#section-9 +const maxNestingDepth = 10000 + // reset prepares the scanner for use. // It must be called before calling s.step. func (s *scanner) reset() { @@ -170,8 +174,13 @@ } // pushParseState pushes a new parse state p onto the parse stack. -func (s *scanner) pushParseState(p int) { - s.parseState = append(s.parseState, p) +// an error state is returned if maxNestingDepth was exceeded, otherwise successState is returned. +func (s *scanner) pushParseState(c byte, newParseState int, successState int) int { + s.parseState = append(s.parseState, newParseState) + if len(s.parseState) <= maxNestingDepth { + return successState + } + return s.error(c, "exceeded max depth") } // popParseState pops a parse state (already obtained) off the stack @@ -211,12 +220,10 @@ switch c { case '{': s.step = stateBeginStringOrEmpty - s.pushParseState(parseObjectKey) - return scanBeginObject + return s.pushParseState(c, parseObjectKey, scanBeginObject) case '[': s.step = stateBeginValueOrEmpty - s.pushParseState(parseArrayValue) - return scanBeginArray + return s.pushParseState(c, parseArrayValue, scanBeginArray) case '"': s.step = stateInString return scanBeginLiteral --- a/vendor/github.com/go-jose/go-jose/v4/jwe.go +++ b/vendor/github.com/go-jose/go-jose/v4/jwe.go @@ -153,6 +153,10 @@ keyEncryptionAlgorithms []KeyAlgorithm, contentEncryption []ContentEncryption, ) (*JSONWebEncryption, error) { + if len(input) == 0 { + return nil, errEmptyInput + } + var parsed rawJSONWebEncryption err := json.Unmarshal([]byte(input), &parsed) if err != nil { @@ -198,15 +202,6 @@ } } - // 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{ { @@ -260,6 +255,15 @@ } } + // 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() @@ -291,6 +295,10 @@ var parts [5]string var ok bool + if len(input) == 0 { + return nil, errEmptyInput + } + for i := range 4 { parts[i], input, ok = strings.Cut(input, ".") if !ok { --- a/vendor/github.com/go-jose/go-jose/v4/jwk.go +++ b/vendor/github.com/go-jose/go-jose/v4/jwk.go @@ -107,7 +107,7 @@ switch key := k.Key.(type) { case ed25519.PublicKey: - raw = fromEdPublicKey(key) + raw, err = fromEdPublicKey(key) case *ecdsa.PublicKey: raw, err = fromEcPublicKey(key) case *rsa.PublicKey: @@ -177,13 +177,29 @@ // 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) @@ -207,21 +223,29 @@ case "EC": if raw.D != nil { key, err = raw.ecPrivateKey() - if err == nil { - keyPub = key.(*ecdsa.PrivateKey).Public() + if err != nil { + return err } + keyPub = key.(*ecdsa.PrivateKey).Public() } else { key, err = raw.ecPublicKey() + if err != nil { + return err + } keyPub = key } case "RSA": if raw.D != nil { key, err = raw.rsaPrivateKey() - if err == nil { - keyPub = key.(*rsa.PrivateKey).Public() + if err != nil { + return err } + keyPub = key.(*rsa.PrivateKey).Public() } else { key, err = raw.rsaPublicKey() + if err != nil { + return err + } keyPub = key } case "oct": @@ -229,25 +253,28 @@ return errors.New("go-jose/go-jose: invalid JWK, found 'oct' (symmetric) key with cert chain") } key, err = raw.symmetricKey() + if err != nil { + return err + } case "OKP": if raw.Crv == "Ed25519" { if raw.D != nil { key, err = raw.edPrivateKey() - if err == nil { - keyPub = key.(ed25519.PrivateKey).Public() + if err != nil { + return err } + keyPub = key.(ed25519.PrivateKey).Public() } else { key, err = raw.edPublicKey() + if err != nil { + return err + } keyPub = key } } case "": // kty MUST be present - err = fmt.Errorf("go-jose/go-jose: missing json web key type") - } - - if err != nil { - return + return fmt.Errorf("go-jose/go-jose: missing json web key type") } if key == nil { @@ -388,11 +415,12 @@ func edThumbprintInput(ed ed25519.PublicKey) (string, error) { crv := "Ed25519" - if len(ed) > 32 { - return "", errors.New("go-jose/go-jose: invalid elliptic key (too large)") + err := validateEd25519PublicKey(ed) + if err != nil { + return "", err } return fmt.Sprintf(edThumbprintTemplate, crv, - newFixedSizeBuffer(ed, 32).base64()), nil + newFixedSizeBuffer(ed, ed25519.PublicKeySize).base64()), nil } // Thumbprint computes the JWK Thumbprint of a key using the @@ -493,23 +521,39 @@ 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 } -func fromEdPublicKey(pub ed25519.PublicKey) *rawJSONWebKey { +func fromEdPublicKey(pub ed25519.PublicKey) (*rawJSONWebKey, error) { + err := validateEd25519PublicKey(pub) + if err != nil { + return nil, err + } return &rawJSONWebKey{ Kty: "OKP", Crv: "Ed25519", X: newBuffer(pub), - } + }, nil } func fromRsaPublicKey(pub *rsa.PublicKey) *rawJSONWebKey { @@ -604,21 +648,36 @@ return nil, fmt.Errorf("go-jose/go-jose: invalid Ed25519 private key, missing %s value(s)", strings.Join(missing, ", ")) } - privateKey := make([]byte, ed25519.PrivateKeySize) - copy(privateKey[0:32], key.D.bytes()) - copy(privateKey[32:], key.X.bytes()) - rv := ed25519.PrivateKey(privateKey) - return rv, nil + publicKey := key.X.bytes() + err := validateEd25519PublicKey(publicKey) + if err != nil { + return nil, err + } + + seed := key.D.bytes() + if len(seed) != ed25519.SeedSize { + return nil, fmt.Errorf("go-jose/go-jose: invalid Ed25519 private key, wrong length for d") + } + + privateKey := ed25519.NewKeyFromSeed(seed) + derivedPublicKey := privateKey.Public().(ed25519.PublicKey) + if !bytes.Equal(derivedPublicKey, publicKey) { + return nil, errors.New("go-jose/go-jose: invalid Ed25519 private key, x does not match d") + } + + return privateKey, nil } func (key rawJSONWebKey) edPublicKey() (ed25519.PublicKey, error) { if key.X == nil { return nil, fmt.Errorf("go-jose/go-jose: invalid Ed key, missing x value") } - publicKey := make([]byte, ed25519.PublicKeySize) - copy(publicKey[0:32], key.X.bytes()) - rv := ed25519.PublicKey(publicKey) - return rv, nil + publicKey := key.X.bytes() + err := validateEd25519PublicKey(publicKey) + if err != nil { + return nil, err + } + return ed25519.PublicKey(bytes.Clone(publicKey)), nil } func (key rawJSONWebKey) rsaPrivateKey() (*rsa.PrivateKey, error) { @@ -642,16 +701,23 @@ 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 { @@ -669,9 +735,17 @@ } func fromEdPrivateKey(ed ed25519.PrivateKey) (*rawJSONWebKey, error) { - raw := fromEdPublicKey(ed25519.PublicKey(ed[32:])) + if len(ed) != ed25519.PrivateKeySize { + return nil, errors.New("go-jose/go-jose: invalid Ed25519 private key length") + } - raw.D = newBuffer(ed[0:32]) + publicKey := ed.Public().(ed25519.PublicKey) + raw, err := fromEdPublicKey(publicKey) + if err != nil { + return nil, err + } + + raw.D = newBuffer(ed.Seed()) return raw, nil } @@ -810,7 +884,7 @@ ErrJWKSKidNotFound = errors.New("go-jose/go-jose: JWK with matching kid not found in JWK Set") ) -func tryJWKS(key interface{}, headers ...Header) (interface{}, error) { +func tryJWKS(key interface{}, header Header) (interface{}, error) { var jwks JSONWebKeySet switch jwksType := key.(type) { @@ -823,16 +897,8 @@ return key, nil } - // Determine the KID to search for from the headers. - var kid string - for _, header := range headers { - if header.KeyID != "" { - kid = header.KeyID - break - } - } - - // If no KID is specified in the headers, reject. + // If no KID is specified in the header, reject. + kid := header.KeyID if kid == "" { return nil, ErrJWKSKidNotFound } @@ -846,3 +912,107 @@ return keys[0].Key, nil } + +// weakEd25519PublicKeys contains the low-order Ed25519 encodings accepted by +// Go's verifier that must not be accepted as JOSE verification keys. +var weakEd25519PublicKeys = map[[ed25519.PublicKeySize]byte]struct{}{ + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }: {}, + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + }: {}, + { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }: {}, + { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + }: {}, + { + 0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, + 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0, + 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39, + 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x05, + }: {}, + { + 0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, + 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0, + 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39, + 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x85, + }: {}, + { + 0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, + 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f, + 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6, + 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0x7a, + }: {}, + { + 0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, + 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f, + 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6, + 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0xfa, + }: {}, + { + 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + }: {}, + { + 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }: {}, + { + 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + }: {}, + { + 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }: {}, + { + 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + }: {}, + { + 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }: {}, +} + +func validateEd25519PublicKey(publicKey ed25519.PublicKey) error { + if len(publicKey) != ed25519.PublicKeySize { + return fmt.Errorf("go-jose/go-jose: invalid Ed25519 public key, wrong length for x") + } + + var encoded [ed25519.PublicKeySize]byte + copy(encoded[:], publicKey) + _, ok := weakEd25519PublicKeys[encoded] + if ok { + return errors.New("go-jose/go-jose: invalid Ed25519 public key, low-order point") + } + + return nil +} --- a/vendor/github.com/go-jose/go-jose/v4/jws.go +++ b/vendor/github.com/go-jose/go-jose/v4/jws.go @@ -22,6 +22,7 @@ "errors" "fmt" "strings" + "unicode" "github.com/go-jose/go-jose/v4/json" ) @@ -89,12 +90,12 @@ signature string, signatureAlgorithms []SignatureAlgorithm, ) (*JSONWebSignature, error) { - signature = stripWhitespace(signature) - if strings.HasPrefix(signature, "{") { + trimmed := strings.TrimLeftFunc(signature, unicode.IsSpace) + if strings.HasPrefix(trimmed, "{") { return ParseSignedJSON(signature, signatureAlgorithms) } - return parseSignedCompact(signature, nil, signatureAlgorithms) + return parseSignedCompact(stripWhitespace(signature), nil, signatureAlgorithms) } // ParseSignedCompact parses a message in JWS Compact Serialization. Validation fails if the JWS is @@ -186,6 +187,10 @@ input string, signatureAlgorithms []SignatureAlgorithm, ) (*JSONWebSignature, error) { + if len(input) == 0 { + return nil, errEmptyInput + } + var parsed rawJSONWebSignature err := json.Unmarshal([]byte(input), &parsed) if err != nil { @@ -270,17 +275,18 @@ 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 { @@ -318,17 +324,20 @@ 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 { @@ -354,7 +363,6 @@ // Copy value of sig original := sig - obj.Signatures[i].header = sig.Header obj.Signatures[i].original = &original } @@ -369,6 +377,10 @@ payload []byte, signatureAlgorithms []SignatureAlgorithm, ) (*JSONWebSignature, error) { + if len(input) == 0 { + return nil, errEmptyInput + } + protected, s, ok := strings.Cut(input, tokenDelim) if !ok { // no period found return nil, fmt.Errorf("go-jose/go-jose: compact JWS format must have three parts") --- a/vendor/github.com/go-jose/go-jose/v4/opaque.go +++ b/vendor/github.com/go-jose/go-jose/v4/opaque.go @@ -46,6 +46,11 @@ return recipientSigInfo{}, ErrUnsupportedAlgorithm } + pk := signer.Public() + if pk != nil && !pk.IsPublic() { + return recipientSigInfo{}, ErrNotPublic + } + return recipientSigInfo{ sigAlg: alg, publicKey: signer.Public, --- a/vendor/github.com/go-jose/go-jose/v4/shared.go +++ b/vendor/github.com/go-jose/go-jose/v4/shared.go @@ -80,6 +80,12 @@ // ErrUnsupportedCriticalHeader is returned when a header is marked critical but not supported by go-jose. ErrUnsupportedCriticalHeader = errors.New("go-jose/go-jose: unsupported critical header") + + // errEmptyInput is returned when go-jose was asked to parse an empty string. + errEmptyInput = errors.New("go-jose/go-jose: empty input") + + // ErrNotPublic indicates a private key was passed where a public key was expected + ErrNotPublic = errors.New("go-jose/go-jose: public key was unexpectedly not public") ) // Key management algorithms @@ -291,9 +297,10 @@ 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 @@ -319,20 +326,6 @@ 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) { @@ -418,10 +411,14 @@ } 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 --- a/vendor/github.com/go-jose/go-jose/v4/signing.go +++ b/vendor/github.com/go-jose/go-jose/v4/signing.go @@ -130,6 +130,8 @@ verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error } +var errInvalidVerificationKey = errors.New("go-jose/go-jose: invalid verification key") + type genericSigner struct { recipients []recipientSigInfo nonceSource NonceSource @@ -138,11 +140,21 @@ } type recipientSigInfo struct { - sigAlg SignatureAlgorithm + sigAlg SignatureAlgorithm + // publicKey returns a synthetic JSONWebKey for the signer. + // For opaque signers, it calls OpaqueSigner.Public(). publicKey func() *JSONWebKey signer payloadSigner } +// getPublicKey gets the public key, with a nil check on the func. +func (r recipientSigInfo) getPublicKey() *JSONWebKey { + if r.publicKey == nil { + return nil + } + return r.publicKey() +} + func staticPublicKey(jwk *JSONWebKey) func() *JSONWebKey { return func() *JSONWebKey { return jwk @@ -178,14 +190,23 @@ func newVerifier(verificationKey interface{}) (payloadVerifier, error) { switch verificationKey := verificationKey.(type) { case ed25519.PublicKey: + if len(verificationKey) == 0 { + return nil, errInvalidVerificationKey + } return &edEncrypterVerifier{ publicKey: verificationKey, }, nil case *rsa.PublicKey: + if verificationKey == nil { + return nil, errInvalidVerificationKey + } return &rsaEncrypterVerifier{ publicKey: verificationKey, }, nil case *ecdsa.PublicKey: + if verificationKey == nil { + return nil, errInvalidVerificationKey + } return &ecEncrypterVerifier{ publicKey: verificationKey, }, nil @@ -196,6 +217,9 @@ case JSONWebKey: return newVerifier(verificationKey.Key) case *JSONWebKey: + if verificationKey == nil { + return nil, errInvalidVerificationKey + } return newVerifier(verificationKey.Key) case OpaqueVerifier: return &opaqueVerifier{verifier: verificationKey}, nil @@ -240,18 +264,18 @@ if err != nil { return recipientSigInfo{}, err } - if recipient.publicKey != nil && recipient.publicKey() != nil { + if recipientPubKey := recipient.getPublicKey(); recipientPubKey != nil { + // This should be impossible, but let's check anyway. + if !recipientPubKey.IsPublic() { + return recipientSigInfo{}, ErrNotPublic + } + // recipient.publicKey is a JWK synthesized for embedding when recipientSigInfo // was created for the inner key (such as a RSA or ECDSA public key). It contains // the pub key for embedding, but doesn't have extra params like key id. publicKey := signingKey - publicKey.Key = recipient.publicKey().Key + publicKey.Key = recipientPubKey.Key recipient.publicKey = staticPublicKey(&publicKey) - - // This should be impossible, but let's check anyway. - if !recipient.publicKey().IsPublic() { - return recipientSigInfo{}, errors.New("go-jose/go-jose: public key was unexpectedly not public") - } } return recipient, nil } @@ -266,7 +290,7 @@ headerAlgorithm: string(recipient.sigAlg), } - if recipient.publicKey != nil && recipient.publicKey() != nil { + if recipientPubKey := recipient.getPublicKey(); recipientPubKey != nil { // We want to embed the JWK or set the kid header, but not both. Having a protected // header that contains an embedded JWK while also simultaneously containing the kid // header is confusing, and at least in ACME the two are considered to be mutually @@ -274,11 +298,11 @@ // result of the JOSE spec. We've decided that this library will only include one or // the other to avoid this confusion. // - // See https://github.com/go-jose/go-jose/issues/157 for more context. + // See https://github.com/square/go-jose/issues/157 for more context. if ctx.embedJWK { - protected[headerJWK] = recipient.publicKey() + protected[headerJWK] = recipientPubKey } else { - keyID := recipient.publicKey().KeyID + keyID := recipientPubKey.KeyID if keyID != "" { protected[headerKeyID] = keyID } @@ -390,7 +414,13 @@ // The verificationKey argument must have one of the types allowed for the // verificationKey argument of JSONWebSignature.Verify(). func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey interface{}) error { - key, err := tryJWKS(verificationKey, obj.headers()...) + if len(obj.Signatures) > 1 { + return errors.New("go-jose/go-jose: too many signatures in payload; expecting only one") + } + + signature := obj.Signatures[0] + + key, err := tryJWKS(verificationKey, signature.Header) if err != nil { return err } @@ -399,12 +429,6 @@ return err } - if len(obj.Signatures) > 1 { - return errors.New("go-jose/go-jose: too many signatures in payload; expecting only one") - } - - signature := obj.Signatures[0] - if signature.header != nil { // Per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11, // 4.1.11. "crit" (Critical) Header Parameter @@ -432,11 +456,11 @@ headers := signature.mergedHeaders() alg := headers.getSignatureAlgorithm() err = verifier.verifyPayload(input, signature.Signature, alg) - if err == nil { - return nil + if err != nil { + return ErrCryptoFailure } - return ErrCryptoFailure + return nil } // VerifyMulti validates (one of the multiple) signatures on the object and @@ -467,16 +491,6 @@ // The verificationKey argument must have one of the types allowed for the // verificationKey argument of JSONWebSignature.Verify(). func (obj JSONWebSignature) DetachedVerifyMulti(payload []byte, verificationKey interface{}) (int, Signature, error) { - key, err := tryJWKS(verificationKey, obj.headers()...) - if err != nil { - return -1, Signature{}, err - } - verifier, err := newVerifier(key) - if err != nil { - return -1, Signature{}, err - } - -outer: for i, signature := range obj.Signatures { if signature.header != nil { // Per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11, @@ -484,20 +498,31 @@ // "When used, this Header Parameter MUST be integrity // protected; therefore, it MUST occur only within the JWS // Protected Header." - err = signature.header.checkNoCritical() + err := signature.header.checkNoCritical() if err != nil { - continue outer + continue } } if signature.protected != nil { // Check for only supported critical headers - err = signature.protected.checkSupportedCritical(supportedCritical) + err := signature.protected.checkSupportedCritical(supportedCritical) if err != nil { - continue outer + continue } } + // If the verification key is a JWK Set, pick a key based on this signature's + // "kid" header. If no match, skip this signature. + key, err := tryJWKS(verificationKey, signature.Header) + if err != nil { + continue + } + verifier, err := newVerifier(key) + if err != nil { + continue + } + input, err := obj.computeAuthData(payload, &signature) if err != nil { continue @@ -506,18 +531,12 @@ headers := signature.mergedHeaders() alg := headers.getSignatureAlgorithm() err = verifier.verifyPayload(input, signature.Signature, alg) - if err == nil { - return i, signature, nil + if err != nil { + continue } + + return i, signature, nil } return -1, Signature{}, ErrCryptoFailure } - -func (obj JSONWebSignature) headers() []Header { - headers := make([]Header, len(obj.Signatures)) - for i, sig := range obj.Signatures { - headers[i] = sig.Header - } - return headers -} --- a/vendor/github.com/go-jose/go-jose/v4/symmetric.go +++ b/vendor/github.com/go-jose/go-jose/v4/symmetric.go @@ -34,8 +34,8 @@ josecipher "github.com/go-jose/go-jose/v4/cipher" ) -// RandReader is a cryptographically secure random number generator (stubbed out in tests). -var RandReader = rand.Reader +// randReader is a cryptographically secure random number generator (stubbed out in tests). +var randReader = rand.Reader const ( // RFC7518 recommends a minimum of 1,000 iterations: @@ -153,7 +153,7 @@ // getRandomSalt generates a new salt of the given size. func getRandomSalt(size int) ([]byte, error) { salt := make([]byte, size) - _, err := io.ReadFull(RandReader, salt) + _, err := io.ReadFull(randReader, salt) if err != nil { return nil, err } @@ -198,7 +198,7 @@ // Generate a random key for the given content cipher func (ctx randomKeyGenerator) genKey() ([]byte, rawHeader, error) { key := make([]byte, ctx.size) - _, err := io.ReadFull(RandReader, key) + _, err := io.ReadFull(randReader, key) if err != nil { return nil, rawHeader{}, err } @@ -238,7 +238,7 @@ // Initialize a new nonce iv := make([]byte, aead.NonceSize()) - _, err = io.ReadFull(RandReader, iv) + _, err = io.ReadFull(randReader, iv) if err != nil { return nil, err } --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -531,7 +531,7 @@ # github.com/fxamacker/cbor/v2 v2.9.0 ## explicit; go 1.20 github.com/fxamacker/cbor/v2 -# github.com/go-jose/go-jose/v4 v4.1.4 +# 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