-
-
Notifications
You must be signed in to change notification settings - Fork 192
Closed
Description
Summary
When creating a JWE with JSON Serialization, the same header parameters (e.g., kid, alg) appear in both the protected and per-recipient unprotected (header) sections.
RFC 7516 requires that the header parameter names across the three locations—protected, unprotected, and per-recipient header—“MUST be disjoint.”
RFC reference: https://www.rfc-editor.org/rfc/rfc7516#section-7.2.1
package jwxmergeissue
import (
"crypto/rand"
"crypto/rsa"
"encoding/base64"
"encoding/json"
"testing"
"github.com/lestrrat-go/jwx/v3/jwa"
"github.com/lestrrat-go/jwx/v3/jwe"
"github.com/stretchr/testify/require"
)
func TestDuplicateHeaders(t *testing.T) {
recipientPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048)
require.NoError(t, err)
// Per-recipient unprotected header includes kid
recipientHeaders := jwe.NewHeaders()
require.NoError(t, recipientHeaders.Set("kid", "recipient1"))
recipient := jwe.WithKey(
jwa.RSA_OAEP_256(),
recipientPrivateKey.PublicKey,
jwe.WithPerRecipientHeaders(recipientHeaders),
)
// Produce JSON Serialization (flattened for single recipient)
ret, err := jwe.Encrypt([]byte("Hello!"), jwe.WithJSON(), recipient)
require.NoError(t, err)
var jweJSON map[string]any
require.NoError(t, json.Unmarshal(ret, &jweJSON))
// Decode protected header (Base64URL)
protectedB64, ok := jweJSON["protected"].(string)
require.True(t, ok)
protectedJSON, err := base64.RawURLEncoding.DecodeString(protectedB64)
require.NoError(t, err)
var protected map[string]any
require.NoError(t, json.Unmarshal(protectedJSON, &protected))
// Per-recipient header
recipHeader, ok := jweJSON["header"].(map[string]any)
require.True(t, ok)
// Fail if the same names appear in both places (violates RFC 7516 §7.2.1)
if _, has := protected["kid"]; has {
if _, also := recipHeader["kid"]; also {
t.Fatalf("duplicate 'kid' present in both protected and per-recipient headers")
}
}
if _, has := protected["alg"]; has {
if _, also := recipHeader["alg"]; also {
t.Fatalf("duplicate 'alg' present in both protected and per-recipient headers")
}
}
}Expected behavior
Library should prevent duplicate header names across protected, unprotected, and per-recipient headers
go.mod file:
module github.com/ilya-korotya/jwx_merge_issue
go 1.24.4
toolchain go1.24.8
require (
github.com/lestrrat-go/jwx/v3 v3.0.11
github.com/stretchr/testify v1.11.1
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/lestrrat-go/blackmagic v1.0.4 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc/v3 v3.0.1 // indirect
github.com/lestrrat-go/option v1.0.1 // indirect
github.com/lestrrat-go/option/v2 v2.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
golang.org/x/crypto v0.42.0 // indirect
golang.org/x/sys v0.36.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels