build(deps): bump github.com/open-policy-agent/opa from 1.10.1 to 1.11.0
Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 1.10.1 to 1.11.0. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v1.10.1...v1.11.0) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-version: 1.11.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
committed by
Ralf Haferkamp
parent
5ed944dfc3
commit
82c82f8ae2
+3
@@ -106,6 +106,9 @@ linters:
|
||||
- revive
|
||||
path: jwt/internal/types/
|
||||
text: "var-naming: avoid meaningless package names"
|
||||
- linters:
|
||||
- godoclint
|
||||
path: (^|/)internal/
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
|
||||
+43
@@ -4,6 +4,49 @@ Changes
|
||||
v3 has many incompatibilities with v2. To see the full list of differences between
|
||||
v2 and v3, please read the Changes-v3.md file (https://github.com/lestrrat-go/jwx/blob/develop/v3/Changes-v3.md)
|
||||
|
||||
v3.0.12 20 Oct 2025
|
||||
* [jwe] As part of the next change, now per-recipient headers that are empty
|
||||
are no longer serialized in flattened JSON serialization.
|
||||
|
||||
* [jwe] Introduce `jwe.WithLegacyHeaderMerging(bool)` option to control header
|
||||
merging behavior in during JWE encryption. This only applies to flattened
|
||||
JSON serialization.
|
||||
|
||||
Previously, when using flattened JSON serialization (i.e. you specified
|
||||
JSON serialization via `jwe.WithJSON()` and only supplied one key), per-recipient
|
||||
headers were merged into the protected headers during encryption, and then
|
||||
were left to be included in the final serialization as-is. This caused duplicate
|
||||
headers to be present in both the protected headers and the per-recipient headers.
|
||||
|
||||
Since there maybe users who rely on this behavior already, instead of changing the
|
||||
default behavior to fix this duplication, a new option to `jwe.Encrypt()` was added
|
||||
to allow clearing the per-recipient headers after merging to leave the `"headers"`
|
||||
field empty. This in effect makes the flattened JSON serialization more similar to
|
||||
the compact serialization, where there are no per-recipient headers present, and
|
||||
leaves the headers disjoint.
|
||||
|
||||
Note that in compact mode, there are no per-recipient headers and thus the
|
||||
headers need to be merged regardless. In full JSON serialization, we never
|
||||
merge the headers, so it is left up to the user to keep the headers disjoint.
|
||||
|
||||
* [jws] Calling the deprecated `jws.NewSigner()` function for the time will cause
|
||||
legacy signers to be loaded automatically. Previously, you had to explicitly
|
||||
call `jws.Settings(jws.WithLegacySigners(true))` to enable legacy signers.
|
||||
|
||||
We incorrectly assumed that users would not be using `jws.NewSigner()`, and thus
|
||||
disabled legacy signers by default. However, it turned out that some users
|
||||
were using `jws.NewSigner()` in their code, which lead to breakages in
|
||||
existing code. In hindsight we should have known that any API made public before will
|
||||
be used by _somebody_.
|
||||
|
||||
As a side effect, jws.Settings(jws.WithLegacySigners(...)) is now a no-op.
|
||||
|
||||
However, please do note that jws.Signer (and similar) objects were always intended to be
|
||||
used for _registering_ new signing/verifying algorithms, and not for end users to actually
|
||||
use them directly. If you are using them for other purposes, please consider changing
|
||||
your code, as it is more than likely that we will somehow deprecate/remove/discouraged
|
||||
their use in the future.
|
||||
|
||||
v3.0.11 14 Sep 2025
|
||||
* [jwk] Add `(jwk.Cache).Shutdown()` method that delegates to the httprc controller
|
||||
object, to shutdown the cache.
|
||||
|
||||
+2
-2
@@ -9,9 +9,9 @@ bazel_dep(name = "rules_go", version = "0.55.1")
|
||||
bazel_dep(name = "gazelle", version = "0.44.0")
|
||||
bazel_dep(name = "aspect_bazel_lib", version = "2.11.0")
|
||||
|
||||
# Go SDK setup - using Go 1.24.4 to match the toolchain in go.mod
|
||||
# Go SDK setup from go.mod
|
||||
go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")
|
||||
go_sdk.download(version = "1.24.4")
|
||||
go_sdk.from_file(go_mod = "//:go.mod")
|
||||
|
||||
# Go dependencies from go.mod
|
||||
go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
|
||||
|
||||
+3
-2
@@ -22,8 +22,9 @@ const _FormatKind_name = "InvalidFormatUnknownFormatJWEJWSJWKJWKSJWT"
|
||||
var _FormatKind_index = [...]uint8{0, 13, 26, 29, 32, 35, 39, 42}
|
||||
|
||||
func (i FormatKind) String() string {
|
||||
if i < 0 || i >= FormatKind(len(_FormatKind_index)-1) {
|
||||
idx := int(i) - 0
|
||||
if i < 0 || idx >= len(_FormatKind_index)-1 {
|
||||
return "FormatKind(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _FormatKind_name[_FormatKind_index[i]:_FormatKind_index[i+1]]
|
||||
return _FormatKind_name[_FormatKind_index[idx]:_FormatKind_index[idx+1]]
|
||||
}
|
||||
|
||||
+110
-28
@@ -99,15 +99,20 @@ func (b *recipientBuilder) Build(r Recipient, cek []byte, calg jwa.ContentEncryp
|
||||
rawKey = raw
|
||||
}
|
||||
|
||||
// Extract ECDH-ES specific parameters if needed
|
||||
// Extract ECDH-ES specific parameters if needed.
|
||||
var apu, apv []byte
|
||||
if b.headers != nil {
|
||||
if val, ok := b.headers.AgreementPartyUInfo(); ok {
|
||||
apu = val
|
||||
}
|
||||
if val, ok := b.headers.AgreementPartyVInfo(); ok {
|
||||
apv = val
|
||||
}
|
||||
|
||||
hdr := b.headers
|
||||
if hdr == nil {
|
||||
hdr = NewHeaders()
|
||||
}
|
||||
|
||||
if val, ok := hdr.AgreementPartyUInfo(); ok {
|
||||
apu = val
|
||||
}
|
||||
|
||||
if val, ok := hdr.AgreementPartyVInfo(); ok {
|
||||
apv = val
|
||||
}
|
||||
|
||||
// Create the encrypter using the new jwebb pattern
|
||||
@@ -116,20 +121,20 @@ func (b *recipientBuilder) Build(r Recipient, cek []byte, calg jwa.ContentEncryp
|
||||
return nil, fmt.Errorf(`jwe.Encrypt: recipientBuilder: failed to create encrypter: %w`, err)
|
||||
}
|
||||
|
||||
if hdrs := b.headers; hdrs != nil {
|
||||
_ = r.SetHeaders(hdrs)
|
||||
}
|
||||
_ = r.SetHeaders(hdr)
|
||||
|
||||
if err := r.Headers().Set(AlgorithmKey, b.alg); err != nil {
|
||||
// Populate headers with stuff that we automatically set
|
||||
if err := hdr.Set(AlgorithmKey, b.alg); err != nil {
|
||||
return nil, fmt.Errorf(`failed to set header: %w`, err)
|
||||
}
|
||||
|
||||
if keyID != "" {
|
||||
if err := r.Headers().Set(KeyIDKey, keyID); err != nil {
|
||||
if err := hdr.Set(KeyIDKey, keyID); err != nil {
|
||||
return nil, fmt.Errorf(`failed to set header: %w`, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the encrypted key
|
||||
var rawCEK []byte
|
||||
enckey, err := enc.EncryptKey(cek)
|
||||
if err != nil {
|
||||
@@ -143,8 +148,9 @@ func (b *recipientBuilder) Build(r Recipient, cek []byte, calg jwa.ContentEncryp
|
||||
}
|
||||
}
|
||||
|
||||
// finally, anything specific should go here
|
||||
if hp, ok := enckey.(populater); ok {
|
||||
if err := hp.Populate(r.Headers()); err != nil {
|
||||
if err := hp.Populate(hdr); err != nil {
|
||||
return nil, fmt.Errorf(`failed to populate: %w`, err)
|
||||
}
|
||||
}
|
||||
@@ -154,7 +160,9 @@ func (b *recipientBuilder) Build(r Recipient, cek []byte, calg jwa.ContentEncryp
|
||||
|
||||
// Encrypt generates a JWE message for the given payload and returns
|
||||
// it in serialized form, which can be in either compact or
|
||||
// JSON format. Default is compact.
|
||||
// JSON format. Default is compact. When JSON format is specified and
|
||||
// there is only one recipient, the resulting serialization is
|
||||
// automatically converted to flattened JSON serialization format.
|
||||
//
|
||||
// You must pass at least one key to `jwe.Encrypt()` by using `jwe.WithKey()`
|
||||
// option.
|
||||
@@ -172,6 +180,10 @@ func (b *recipientBuilder) Build(r Recipient, cek []byte, calg jwa.ContentEncryp
|
||||
//
|
||||
// Look for options that return `jwe.EncryptOption` or `jws.EncryptDecryptOption`
|
||||
// for a complete list of options that can be passed to this function.
|
||||
//
|
||||
// As of v3.0.12, users can specify `jwe.WithLegacyHeaderMerging()` to
|
||||
// disable header merging behavior that was the default prior to v3.0.12.
|
||||
// Read the documentation for `jwe.WithLegacyHeaderMerging()` for more information.
|
||||
func Encrypt(payload []byte, options ...EncryptOption) ([]byte, error) {
|
||||
ec := encryptContextPool.Get()
|
||||
defer encryptContextPool.Put(ec)
|
||||
@@ -410,10 +422,26 @@ func (dc *decryptContext) decryptContent(msg *Message, alg jwa.KeyEncryptionAlgo
|
||||
Tag(msg.tag).
|
||||
CEK(dc.cek)
|
||||
|
||||
if v, ok := recipient.Headers().Algorithm(); !ok || v != alg {
|
||||
// algorithms don't match
|
||||
// The "alg" header can be in either protected/unprotected headers.
|
||||
// prefer per-recipient headers (as it might be the case that the algorithm differs
|
||||
// by each recipient), then look at protected headers.
|
||||
var algMatched bool
|
||||
for _, hdr := range []Headers{recipient.Headers(), protectedHeaders} {
|
||||
v, ok := hdr.Algorithm()
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if v == alg {
|
||||
algMatched = true
|
||||
break
|
||||
}
|
||||
// if we found something but didn't match, it's a failure
|
||||
return nil, fmt.Errorf(`jwe.Decrypt: key (%q) and recipient (%q) algorithms do not match`, alg, v)
|
||||
}
|
||||
if !algMatched {
|
||||
return nil, fmt.Errorf(`jwe.Decrypt: failed to find "alg" header in either protected or per-recipient headers`)
|
||||
}
|
||||
|
||||
h2, err := protectedHeaders.Clone()
|
||||
if err != nil {
|
||||
@@ -534,11 +562,12 @@ func (dc *decryptContext) decryptContent(msg *Message, alg jwa.KeyEncryptionAlgo
|
||||
|
||||
// encryptContext holds the state during JWE encryption, similar to JWS signContext
|
||||
type encryptContext struct {
|
||||
calg jwa.ContentEncryptionAlgorithm
|
||||
compression jwa.CompressionAlgorithm
|
||||
format int
|
||||
builders []*recipientBuilder
|
||||
protected Headers
|
||||
calg jwa.ContentEncryptionAlgorithm
|
||||
compression jwa.CompressionAlgorithm
|
||||
format int
|
||||
builders []*recipientBuilder
|
||||
protected Headers
|
||||
legacyHeaderMerging bool
|
||||
}
|
||||
|
||||
var encryptContextPool = pool.New(allocEncryptContext, freeEncryptContext)
|
||||
@@ -561,6 +590,7 @@ func freeEncryptContext(ec *encryptContext) *encryptContext {
|
||||
}
|
||||
|
||||
func (ec *encryptContext) ProcessOptions(options []EncryptOption) error {
|
||||
ec.legacyHeaderMerging = true
|
||||
var mergeProtected bool
|
||||
var useRawCEK bool
|
||||
for _, option := range options {
|
||||
@@ -577,7 +607,11 @@ func (ec *encryptContext) ProcessOptions(options []EncryptOption) error {
|
||||
if v == jwa.DIRECT() || v == jwa.ECDH_ES() {
|
||||
useRawCEK = true
|
||||
}
|
||||
ec.builders = append(ec.builders, &recipientBuilder{alg: v, key: wk.key, headers: wk.headers})
|
||||
ec.builders = append(ec.builders, &recipientBuilder{
|
||||
alg: v,
|
||||
key: wk.key,
|
||||
headers: wk.headers,
|
||||
})
|
||||
case identContentEncryptionAlgorithm{}:
|
||||
var c jwa.ContentEncryptionAlgorithm
|
||||
if err := option.Value(&c); err != nil {
|
||||
@@ -616,6 +650,12 @@ func (ec *encryptContext) ProcessOptions(options []EncryptOption) error {
|
||||
return err
|
||||
}
|
||||
ec.format = fmtOpt
|
||||
case identLegacyHeaderMerging{}:
|
||||
var v bool
|
||||
if err := option.Value(&v); err != nil {
|
||||
return err
|
||||
}
|
||||
ec.legacyHeaderMerging = v
|
||||
}
|
||||
}
|
||||
|
||||
@@ -732,7 +772,8 @@ func (ec *encryptContext) EncryptMessage(payload []byte, cek []byte) ([]byte, er
|
||||
}
|
||||
}
|
||||
|
||||
recipients := recipientSlicePool.GetCapacity(len(ec.builders))
|
||||
lbuilders := len(ec.builders)
|
||||
recipients := recipientSlicePool.GetCapacity(lbuilders)
|
||||
defer recipientSlicePool.Put(recipients)
|
||||
|
||||
for i, builder := range ec.builders {
|
||||
@@ -767,14 +808,55 @@ func (ec *encryptContext) EncryptMessage(payload []byte, cek []byte) ([]byte, er
|
||||
}
|
||||
}
|
||||
|
||||
// If there's only one recipient, you want to include that in the
|
||||
// protected header
|
||||
if len(recipients) == 1 {
|
||||
// fmtCompact does not have per-recipient headers, nor a "header" field.
|
||||
// In this mode, we're going to have to merge everything to the protected
|
||||
// header.
|
||||
if ec.format == fmtCompact {
|
||||
// We have already established that the number of builders is 1 in
|
||||
// ec.ProcessOptions(). But we're going to be pedantic
|
||||
if lbuilders != 1 {
|
||||
return nil, fmt.Errorf(`internal error: expected exactly one recipient builder (got %d)`, lbuilders)
|
||||
}
|
||||
|
||||
// when we're using compact format, we can safely merge per-recipient
|
||||
// headers into the protected header, if any
|
||||
h, err := protected.Merge(recipients[0].Headers())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to merge protected headers: %w`, err)
|
||||
return nil, fmt.Errorf(`failed to merge protected headers for compact serialization: %w`, err)
|
||||
}
|
||||
protected = h
|
||||
// per-recipient headers, if any, will be ignored in compact format
|
||||
} else {
|
||||
// If it got here, it's JSON (could be pretty mode, too).
|
||||
if lbuilders == 1 {
|
||||
// If it got here, then we're doing flattened JSON serialization.
|
||||
// In this mode, we should merge per-recipient headers into the protected header,
|
||||
// but we also need to make sure that the "header" field is reset so that
|
||||
// it does not contain the same fields as the protected header.
|
||||
//
|
||||
// However, old behavior was to merge per-recipient headers into the
|
||||
// protected header when there was only one recipient, AND leave the
|
||||
// original "header" field as is, so we need to support that for backwards compatibility.
|
||||
//
|
||||
// The legacy merging only takes effect when there is exactly one recipient.
|
||||
//
|
||||
// This behavior can be disabled by passing jwe.WithLegacyHeaderMerging(false)
|
||||
// If the user has explicitly asked for merging, do it
|
||||
h, err := protected.Merge(recipients[0].Headers())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to merge protected headers for flattenend JSON format: %w`, err)
|
||||
}
|
||||
protected = h
|
||||
|
||||
if !ec.legacyHeaderMerging {
|
||||
// Clear per-recipient headers, since they have been merged.
|
||||
// But we only do it when legacy merging is disabled.
|
||||
// Note: we should probably introduce a Reset() method in v4
|
||||
if err := recipients[0].SetHeaders(NewHeaders()); err != nil {
|
||||
return nil, fmt.Errorf(`failed to clear per-recipient headers after merging: %w`, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aad, err := protected.Encode()
|
||||
|
||||
+27
-13
@@ -265,14 +265,23 @@ func (m *Message) MarshalJSON() ([]byte, error) {
|
||||
if recipients := m.Recipients(); len(recipients) > 0 {
|
||||
if len(recipients) == 1 { // Use flattened format
|
||||
if hdrs := recipients[0].Headers(); hdrs != nil {
|
||||
buf.Reset()
|
||||
if err := enc.Encode(hdrs); err != nil {
|
||||
return nil, fmt.Errorf(`failed to encode %s field: %w`, HeadersKey, err)
|
||||
var skipHeaders bool
|
||||
if zeroer, ok := hdrs.(isZeroer); ok {
|
||||
if zeroer.isZero() {
|
||||
skipHeaders = true
|
||||
}
|
||||
}
|
||||
|
||||
if !skipHeaders {
|
||||
buf.Reset()
|
||||
if err := enc.Encode(hdrs); err != nil {
|
||||
return nil, fmt.Errorf(`failed to encode %s field: %w`, HeadersKey, err)
|
||||
}
|
||||
fields = append(fields, jsonKV{
|
||||
Key: HeadersKey,
|
||||
Value: strings.TrimSpace(buf.String()),
|
||||
})
|
||||
}
|
||||
fields = append(fields, jsonKV{
|
||||
Key: HeadersKey,
|
||||
Value: strings.TrimSpace(buf.String()),
|
||||
})
|
||||
}
|
||||
|
||||
if ek := recipients[0].EncryptedKey(); len(ek) > 0 {
|
||||
@@ -369,13 +378,18 @@ func (m *Message) UnmarshalJSON(buf []byte) error {
|
||||
// field. TODO: do both of these conditions need to meet, or just one?
|
||||
if proxy.Headers != nil || len(proxy.EncryptedKey) > 0 {
|
||||
recipient := NewRecipient()
|
||||
hdrs := NewHeaders()
|
||||
if err := json.Unmarshal(proxy.Headers, hdrs); err != nil {
|
||||
return fmt.Errorf(`failed to decode headers field: %w`, err)
|
||||
}
|
||||
|
||||
if err := recipient.SetHeaders(hdrs); err != nil {
|
||||
return fmt.Errorf(`failed to set new headers: %w`, err)
|
||||
// `"heders"` could be empty. If that's the case, just skip the
|
||||
// following unmarshaling step
|
||||
if proxy.Headers != nil {
|
||||
hdrs := NewHeaders()
|
||||
if err := json.Unmarshal(proxy.Headers, hdrs); err != nil {
|
||||
return fmt.Errorf(`failed to decode headers field: %w`, err)
|
||||
}
|
||||
|
||||
if err := recipient.SetHeaders(hdrs); err != nil {
|
||||
return fmt.Errorf(`failed to set new headers: %w`, err)
|
||||
}
|
||||
}
|
||||
|
||||
if v := proxy.EncryptedKey; len(v) > 0 {
|
||||
|
||||
+3
-2
@@ -6,8 +6,9 @@ import (
|
||||
"github.com/lestrrat-go/option/v2"
|
||||
)
|
||||
|
||||
// Specify contents of the protected header. Some fields such as
|
||||
// "enc" and "zip" will be overwritten when encryption is performed.
|
||||
// WithProtectedHeaders is used to specify contents of the protected header.
|
||||
// Some fields such as "enc" and "zip" will be overwritten when encryption is
|
||||
// performed.
|
||||
//
|
||||
// There is no equivalent for unprotected headers in this implementation
|
||||
func WithProtectedHeaders(h Headers) EncryptOption {
|
||||
|
||||
+39
-1
@@ -169,4 +169,42 @@ options:
|
||||
If set to an invalid value, the default value is used.
|
||||
In v2, this option was called MaxBufferSize.
|
||||
|
||||
This option has a global effect.
|
||||
This option has a global effect.
|
||||
- ident: LegacyHeaderMerging
|
||||
interface: EncryptOption
|
||||
argument_type: bool
|
||||
option_name: WithLegacyHeaderMerging
|
||||
comment: |
|
||||
WithLegacyHeaderMerging specifies whether to perform legacy header merging
|
||||
when encrypting a JWE message in JSON serialization, when there is a single recipient.
|
||||
This behavior is enabled by default for backwards compatibility.
|
||||
|
||||
When a JWE message is encrypted in JSON serialization, and there is only
|
||||
one recipient, this library automatically serializes the message in
|
||||
flattened JSON serialization format. In older versions of this library,
|
||||
the protected headers and the per-recipient headers were merged together
|
||||
before computing the AAD (Additional Authenticated Data), but the per-recipient
|
||||
headers were kept as-is in the `header` field of the recipient object.
|
||||
|
||||
This behavior is not compliant with the JWE specification, which states that
|
||||
the headers must be disjoint.
|
||||
|
||||
Passing this option with a value of `false` disables this legacy behavior,
|
||||
and while the per-recipient headers and protected headers are still merged
|
||||
for the purpose of computing AAD, the per-recipient headers are cleared
|
||||
after merging, so that the resulting JWE message is compliant with the
|
||||
specification.
|
||||
|
||||
This option has no effect when there are multiple recipients, or when
|
||||
the serialization format is compact serialization. For multiple recipients
|
||||
(i.e. full JSON serialization), the protected headers and per-recipient
|
||||
headers are never merged, and it is the caller's responsibility to ensure
|
||||
that the headers are disjoint. In compact serialization, there are no per-recipient
|
||||
headers; in fact, the protected headers are the only headers that exist,
|
||||
and therefore there is no possibility of header collision after merging
|
||||
(note: while per-recipient headers do not make sense in compact serialization,
|
||||
this library does not prevent you from setting them -- they are all just
|
||||
merged into the protected headers).
|
||||
|
||||
In future versions, the new behavior will be the default. New users are
|
||||
encouraged to set this option to `false` now to avoid future issues.
|
||||
+42
@@ -147,6 +147,7 @@ type identFS struct{}
|
||||
type identKey struct{}
|
||||
type identKeyProvider struct{}
|
||||
type identKeyUsed struct{}
|
||||
type identLegacyHeaderMerging struct{}
|
||||
type identMaxDecompressBufferSize struct{}
|
||||
type identMaxPBES2Count struct{}
|
||||
type identMergeProtectedHeaders struct{}
|
||||
@@ -193,6 +194,10 @@ func (identKeyUsed) String() string {
|
||||
return "WithKeyUsed"
|
||||
}
|
||||
|
||||
func (identLegacyHeaderMerging) String() string {
|
||||
return "WithLegacyHeaderMerging"
|
||||
}
|
||||
|
||||
func (identMaxDecompressBufferSize) String() string {
|
||||
return "WithMaxDecompressBufferSize"
|
||||
}
|
||||
@@ -292,6 +297,43 @@ func WithKeyUsed(v any) DecryptOption {
|
||||
return &decryptOption{option.New(identKeyUsed{}, v)}
|
||||
}
|
||||
|
||||
// WithLegacyHeaderMerging specifies whether to perform legacy header merging
|
||||
// when encrypting a JWE message in JSON serialization, when there is a single recipient.
|
||||
// This behavior is enabled by default for backwards compatibility.
|
||||
//
|
||||
// When a JWE message is encrypted in JSON serialization, and there is only
|
||||
// one recipient, this library automatically serializes the message in
|
||||
// flattened JSON serialization format. In older versions of this library,
|
||||
// the protected headers and the per-recipient headers were merged together
|
||||
// before computing the AAD (Additional Authenticated Data), but the per-recipient
|
||||
// headers were kept as-is in the `header` field of the recipient object.
|
||||
//
|
||||
// This behavior is not compliant with the JWE specification, which states that
|
||||
// the headers must be disjoint.
|
||||
//
|
||||
// Passing this option with a value of `false` disables this legacy behavior,
|
||||
// and while the per-recipient headers and protected headers are still merged
|
||||
// for the purpose of computing AAD, the per-recipient headers are cleared
|
||||
// after merging, so that the resulting JWE message is compliant with the
|
||||
// specification.
|
||||
//
|
||||
// This option has no effect when there are multiple recipients, or when
|
||||
// the serialization format is compact serialization. For multiple recipients
|
||||
// (i.e. full JSON serialization), the protected headers and per-recipient
|
||||
// headers are never merged, and it is the caller's responsibility to ensure
|
||||
// that the headers are disjoint. In compact serialization, there are no per-recipient
|
||||
// headers; in fact, the protected headers are the only headers that exist,
|
||||
// and therefore there is no possibility of header collision after merging
|
||||
// (note: while per-recipient headers do not make sense in compact serialization,
|
||||
// this library does not prevent you from setting them -- they are all just
|
||||
// merged into the protected headers).
|
||||
//
|
||||
// In future versions, the new behavior will be the default. New users are
|
||||
// encouraged to set this option to `false` now to avoid future issues.
|
||||
func WithLegacyHeaderMerging(v bool) EncryptOption {
|
||||
return &encryptOption{option.New(identLegacyHeaderMerging{}, v)}
|
||||
}
|
||||
|
||||
// WithMaxDecompressBufferSize specifies the maximum buffer size for used when
|
||||
// decompressing the payload of a JWE message. If a compressed JWE payload
|
||||
// exceeds this amount when decompressed, jwe.Decrypt will return an error.
|
||||
|
||||
+1
-1
@@ -270,7 +270,7 @@ func (cs *cachedSet) cached() (Set, error) {
|
||||
return cs.r.Resource(), nil
|
||||
}
|
||||
|
||||
// Add is a no-op for `jwk.CachedSet`, as the `jwk.Set` should be treated read-only
|
||||
// AddKey is a no-op for `jwk.CachedSet`, as the `jwk.Set` should be treated read-only
|
||||
func (*cachedSet) AddKey(_ Key) error {
|
||||
return fmt.Errorf(`(jwk.Cachedset).AddKey: jwk.CachedSet is immutable`)
|
||||
}
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ type CachedFetcher struct {
|
||||
cache *Cache
|
||||
}
|
||||
|
||||
// Creates a new `jwk.CachedFetcher` object.
|
||||
// NewCachedFetcher creates a new `jwk.CachedFetcher` object.
|
||||
func NewCachedFetcher(cache *Cache) *CachedFetcher {
|
||||
return &CachedFetcher{cache}
|
||||
}
|
||||
|
||||
+1
-1
@@ -118,7 +118,7 @@ func NewPEMDecoder() PEMDecoder {
|
||||
|
||||
type pemDecoder struct{}
|
||||
|
||||
// DecodePEM decodes a key in PEM encoded ASN.1 DER format.
|
||||
// Decode decodes a key in PEM encoded ASN.1 DER format.
|
||||
// and returns a raw key.
|
||||
func (pemDecoder) Decode(src []byte) (any, []byte, error) {
|
||||
block, rest := pem.Decode(src)
|
||||
|
||||
+4
-1
@@ -586,11 +586,14 @@ func AlgorithmsForKey(key any) ([]jwa.SignatureAlgorithm, error) {
|
||||
return algs, nil
|
||||
}
|
||||
|
||||
// Settings allows you to set global settings for this JWS operations.
|
||||
//
|
||||
// Currently, the only setting available is `jws.WithLegacySigners()`,
|
||||
// which for various reason is now a no-op.
|
||||
func Settings(options ...GlobalOption) {
|
||||
for _, option := range options {
|
||||
switch option.Ident() {
|
||||
case identLegacySigners{}:
|
||||
enableLegacySigners()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ func (e headerNotFoundError) Is(target error) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// ErrHeaderdNotFound returns an error that can be passed to `errors.Is` to check if the error is
|
||||
// ErrHeaderNotFound returns an error that can be passed to `errors.Is` to check if the error is
|
||||
// the result of the field not being found
|
||||
func ErrHeaderNotFound() error {
|
||||
return headerNotFoundError{}
|
||||
|
||||
+4
-1
@@ -2,11 +2,14 @@ package jws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/lestrrat-go/jwx/v3/jwa"
|
||||
"github.com/lestrrat-go/jwx/v3/jws/legacy"
|
||||
)
|
||||
|
||||
var enableLegacySignersOnce = &sync.Once{}
|
||||
|
||||
func enableLegacySigners() {
|
||||
for _, alg := range []jwa.SignatureAlgorithm{jwa.HS256(), jwa.HS384(), jwa.HS512()} {
|
||||
if err := RegisterSigner(alg, func(alg jwa.SignatureAlgorithm) SignerFactory {
|
||||
@@ -74,7 +77,7 @@ func legacySignerFor(alg jwa.SignatureAlgorithm) (Signer, error) {
|
||||
muSigner.Lock()
|
||||
s, ok := signers[alg]
|
||||
if !ok {
|
||||
v, err := NewSigner(alg)
|
||||
v, err := newLegacySigner(alg)
|
||||
if err != nil {
|
||||
muSigner.Unlock()
|
||||
return nil, fmt.Errorf(`failed to create payload signer: %w`, err)
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ type Signer interface {
|
||||
Algorithm() jwa.SignatureAlgorithm
|
||||
}
|
||||
|
||||
// This is for legacy support only.
|
||||
// Verifier is for legacy support only.
|
||||
type Verifier interface {
|
||||
// Verify checks whether the payload and signature are valid for
|
||||
// the given key.
|
||||
|
||||
+2
-2
@@ -38,7 +38,7 @@ type withKey struct {
|
||||
public Headers
|
||||
}
|
||||
|
||||
// This exists as an escape hatch to modify the header values after the fact
|
||||
// Protected exists as an escape hatch to modify the header values after the fact
|
||||
func (w *withKey) Protected(v Headers) Headers {
|
||||
if w.protected == nil && v != nil {
|
||||
w.protected = v
|
||||
@@ -221,7 +221,7 @@ type withInsecureNoSignature struct {
|
||||
protected Headers
|
||||
}
|
||||
|
||||
// This exists as an escape hatch to modify the header values after the fact
|
||||
// Protected exists as an escape hatch to modify the header values after the fact
|
||||
func (w *withInsecureNoSignature) Protected(v Headers) Headers {
|
||||
if w.protected == nil && v != nil {
|
||||
w.protected = v
|
||||
|
||||
+1
-5
@@ -227,8 +227,4 @@ options:
|
||||
interface: GlobalOption
|
||||
constant_value: true
|
||||
comment: |
|
||||
WithLegacySigners specifies whether the JWS package should use legacy
|
||||
signers for signing JWS messages.
|
||||
|
||||
Usually there's no need to use this option, as the new signers and
|
||||
verifiers are loaded by default.
|
||||
WithLegacySigners is a no-op option that exists only for backwards compatibility.
|
||||
|
||||
+1
-5
@@ -356,11 +356,7 @@ func WithKeyUsed(v any) VerifyOption {
|
||||
return &verifyOption{option.New(identKeyUsed{}, v)}
|
||||
}
|
||||
|
||||
// WithLegacySigners specifies whether the JWS package should use legacy
|
||||
// signers for signing JWS messages.
|
||||
//
|
||||
// Usually there's no need to use this option, as the new signers and
|
||||
// verifiers are loaded by default.
|
||||
// WithLegacySigners is a no-op option that exists only for backwards compatibility.
|
||||
func WithLegacySigners() GlobalOption {
|
||||
return &globalOption{option.New(identLegacySigners{}, true)}
|
||||
}
|
||||
|
||||
+42
-15
@@ -2,6 +2,7 @@ package jws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/lestrrat-go/jwx/v3/jwa"
|
||||
@@ -33,6 +34,19 @@ func (fn SignerFactoryFn) Create() (Signer, error) {
|
||||
return fn()
|
||||
}
|
||||
|
||||
func init() {
|
||||
// register the signers using jwsbb. These will be used by default.
|
||||
for _, alg := range jwa.SignatureAlgorithms() {
|
||||
if alg == jwa.NoSignature() {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := RegisterSigner(alg, defaultSigner{alg: alg}); err != nil {
|
||||
panic(fmt.Sprintf("RegisterSigner failed: %v", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SignerFor returns a Signer2 for the given signature algorithm.
|
||||
//
|
||||
// Currently, this function will never fail. It will always return a
|
||||
@@ -43,6 +57,9 @@ func (fn SignerFactoryFn) Create() (Signer, error) {
|
||||
// 3. If no Signer2 or legacy Signer(Factory) is registered, it will return a
|
||||
// default signer that uses jwsbb.Sign.
|
||||
//
|
||||
// 1 and 2 will take care of 99% of the cases. The only time 3 will happen is
|
||||
// when you are using a custom algorithm that is not supported out of the box.
|
||||
//
|
||||
// jwsbb.Sign knows how to handle a static set of algorithms, so if the
|
||||
// algorithm is not supported, it will return an error when you call
|
||||
// `Sign` on the default signer.
|
||||
@@ -80,6 +97,14 @@ var signerDB = make(map[jwa.SignatureAlgorithm]SignerFactory)
|
||||
// Unlike the `UnregisterSigner` function, this function automatically
|
||||
// calls `jwa.RegisterSignatureAlgorithm` to register the algorithm
|
||||
// in this module's algorithm database.
|
||||
//
|
||||
// For backwards compatibility, this function also accepts
|
||||
// `SignerFactory` implementations, but this usage is deprecated.
|
||||
// You should use `Signer2` implementations instead.
|
||||
//
|
||||
// If you want to completely remove an algorithm, you must call
|
||||
// `jwa.UnregisterSignatureAlgorithm` yourself after calling
|
||||
// `UnregisterSigner`.
|
||||
func RegisterSigner(alg jwa.SignatureAlgorithm, f any) error {
|
||||
jwa.RegisterSignatureAlgorithm(alg)
|
||||
switch s := f.(type) {
|
||||
@@ -87,22 +112,10 @@ func RegisterSigner(alg jwa.SignatureAlgorithm, f any) error {
|
||||
muSigner2DB.Lock()
|
||||
signer2DB[alg] = s
|
||||
muSigner2DB.Unlock()
|
||||
|
||||
// delete the other signer, if there was one
|
||||
muSignerDB.Lock()
|
||||
delete(signerDB, alg)
|
||||
muSignerDB.Unlock()
|
||||
case SignerFactory:
|
||||
muSignerDB.Lock()
|
||||
signerDB[alg] = s
|
||||
muSignerDB.Unlock()
|
||||
|
||||
// Remove previous signer, if there was one
|
||||
removeSigner(alg)
|
||||
|
||||
muSigner2DB.Lock()
|
||||
delete(signer2DB, alg)
|
||||
muSigner2DB.Unlock()
|
||||
default:
|
||||
return fmt.Errorf(`jws.RegisterSigner: unsupported type %T for algorithm %q`, f, alg)
|
||||
}
|
||||
@@ -132,11 +145,25 @@ func UnregisterSigner(alg jwa.SignatureAlgorithm) {
|
||||
}
|
||||
|
||||
// NewSigner creates a signer that signs payloads using the given signature algorithm.
|
||||
// This function is deprecated. You should use `SignerFor()` instead.
|
||||
// This function is deprecated, and will either be removed to re-purposed using
|
||||
// a different signature.
|
||||
//
|
||||
// This function only exists for backwards compatibility, but will not work
|
||||
// unless you enable the legacy support mode by calling jws.Settings(jws.WithLegacySigners(true)).
|
||||
// When you want to load a Signer object, you should use `SignerFor()` instead.
|
||||
func NewSigner(alg jwa.SignatureAlgorithm) (Signer, error) {
|
||||
s, err := newLegacySigner(alg)
|
||||
if err == nil {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
if strings.HasPrefix(err.Error(), `jws.NewSigner: unsupported signature algorithm`) {
|
||||
// When newLegacySigner fails, automatically trigger to enable signers
|
||||
enableLegacySignersOnce.Do(enableLegacySigners)
|
||||
return newLegacySigner(alg)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func newLegacySigner(alg jwa.SignatureAlgorithm) (Signer, error) {
|
||||
muSignerDB.RLock()
|
||||
f, ok := signerDB[alg]
|
||||
muSignerDB.RUnlock()
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ func (o *TokenOptionSet) Enable(flag TokenOption) {
|
||||
*o = TokenOptionSet(o.Value() | uint64(flag))
|
||||
}
|
||||
|
||||
// Enable sets the appropriate value to disable the option in the
|
||||
// Disable sets the appropriate value to disable the option in the
|
||||
// option set
|
||||
func (o *TokenOptionSet) Disable(flag TokenOption) {
|
||||
*o = TokenOptionSet(o.Value() & ^uint64(flag))
|
||||
|
||||
+4
-4
@@ -17,9 +17,9 @@ const _TokenOption_name = "FlattenAudienceMaxPerTokenOption"
|
||||
var _TokenOption_index = [...]uint8{0, 15, 32}
|
||||
|
||||
func (i TokenOption) String() string {
|
||||
i -= 1
|
||||
if i >= TokenOption(len(_TokenOption_index)-1) {
|
||||
return "TokenOption(" + strconv.FormatInt(int64(i+1), 10) + ")"
|
||||
idx := int(i) - 1
|
||||
if i < 1 || idx >= len(_TokenOption_index)-1 {
|
||||
return "TokenOption(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _TokenOption_name[_TokenOption_index[i]:_TokenOption_index[i+1]]
|
||||
return _TokenOption_name[_TokenOption_index[idx]:_TokenOption_index[idx+1]]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user