Skip to content

Headers missing in custom key provider when decrypting with jwe.WithKeyProvider #1481

@ilya-korotya

Description

@ilya-korotya

Describe the bug
I updated the jwx library from v3.0.10 to v3.0.12. I started using the new opt jwe.WithLegacyHeaderMerging(false) to prevent legacy header merging that was discussed in the PR. With this new options, I call the encryption function in this way:

	jweOpts := append([]jwe.EncryptOption{
		jwe.WithJSON(),
		jwe.WithContentEncryption(cea),
		jwe.WithProtectedHeaders(headers),
		jwe.WithLegacyHeaderMerging(false),
	}, withKeys...)
    ret, err := jwe.Encrypt(payload, jweOpts...)

As a result, I get a flattened token (because len(withKeys) is 1). Token:

{
  "ciphertext": "b3t1n6vUbELEkuZJ9_4faebSpy421MfucLLfHAoxGpPBptNn9sK2QidSbSX1KAQTDGFdWUM35m_B4wXTRP42JSrfFRAkLKfDtnFXxwaTN4eety0KUR9nxbXNXWPjrXD5uODkARszCofznKuYDJ67hjJQsPY16cj3c2_2a-L5eRmr2D5nagphrS4Jfd6kqzr7eQEJ7x01UX7z5jer_5TAjLhLrlZx2oHjH-OHGBjCplFlVayZJ1v1WzcGWL7ErILvLUcIuhVTdIJ0OCV5U_Qmm6Fx9069WWptBh23ELDMeSpU3QYqDnAjlhz-shrhoFIAJ7uREw9SAoCVV7rgK1_0T2m41q0UunAvXoH5q_IknAyaUmJAWTmuoxxaBA-GjYDLL0lEqK-yrKpCVX6p26Afh3RhJXCDrJIziQIcAILRoMqT8V12V4ni",
  "encrypted_key": "Vpv7bxGfT8fBhNQYvB2Cxpm2O4fEEqj7x8ZMDbIv5IrWC7Ad9a37Uw",
  "iv": "zwftaPgcs5FBGHKe",
  "protected": "eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkEyNTZHQ00iLCJlcGsiOnsiY3J2IjoiUC0zODQiLCJrdHkiOiJFQyIsIngiOiJhT0ZoRV9rZk9lQW9xc2ZlNlZRelY0SnREbGhzMzZEN2dud1RZN1p4NmVmUXVkMmp6YzgzNlR0aF9lYi1xWm0yIiwieSI6Im9idVdNS1A2UEFEcENCRjJYMXZmdnZDMXRNUXBHRjl2ams4RFdOZ044NjF4LXRiNEpfUHhuWlJPRmxFM21MdFQifSwia2lkIjoiMTIzNDU2Nzg5IiwidHlwIjoiYXBwbGljYXRpb24vaWRlbjNjb21tLWVuY3J5cHRlZC1qc29uIn0",
  "tag": "-k7eGDDgoeRBFtwp02F3gg"
}

If I decode the protected field from base64, I get the following payload:

{
  "alg": "ECDH-ES+A256KW",
  "enc": "A256GCM",
  "epk": {
    "crv": "P-384",
    "kty": "EC",
    "x": "aOFhE_kfOeAoqsfe6VQzV4JtDlhs36D7gnwTY7Zx6efQud2jzc836Tth_eb-qZm2",
    "y": "obuWMKP6PADpCBF2X1vfvvC1tMQpGF9vjk8DWNgN861x-tb4J_PxnZROFlE3mLtT"
  },
  "kid": "123456789",
  "typ": "application/iden3comm-encrypted-json"
}

As you can see, the alg field was presented during encryption. But when I try to decrypt the token with the custom provider:

func Decrypt(envelope []byte, fn KeyResolutionFunc) ([]byte, error) {
	customKeyProvider := func(ctx context.Context, sink jwe.KeySink, r jwe.Recipient, msg *jwe.Message) error {
		alg, ok := r.Headers().Algorithm()
		if !ok || alg.String() == "" {
			return fmt.Errorf("recipient has no algorithm")
		}
		if !IsSupportedKeyEncryptionAlgorithm(alg.String()) {
			return fmt.Errorf("unsupported key encryption algorithm: %s", alg.String())
		}

		enc, ok := msg.ProtectedHeaders().ContentEncryption()
		if !ok || enc.String() == "" {
			return fmt.Errorf("message has no content encryption algorithm")
		}
		if !IsSupportedContentEncryptionAlgorithm(enc.String()) {
			return fmt.Errorf("unsupported content encryption algorithm: %s", enc.String())
		}

		kid, ok := r.Headers().KeyID()
		if !ok || kid == "" {
			return fmt.Errorf("recipient has no key ID")
		}
		decryptionKey, err := fn(kid)
		if err != nil {
			return err
		}
		sink.Key(alg, decryptionKey)
		return nil
	}

	payload, err := jwe.Decrypt(envelope, jwe.WithKeyProvider(jwe.KeyProviderFunc(customKeyProvider)))
	if err != nil {
		return nil, fmt.Errorf("%s: %w", err, ErrDecryptionKeyNotFound)
	}
	return payload, nil
}

I get an error saying the alg header was not provided:

jwe.Decrypt: failed to decrypt any of the recipients: key provider 0 failed: recipient has no algorithm: decryption key not found
        	            	failed to decrypt message

Here is an unit test to reproduce the issue:

func TestAlgInHeaders(t *testing.T) {
	token := `{
  "ciphertext": "b3t1n6vUbELEkuZJ9_4faebSpy421MfucLLfHAoxGpPBptNn9sK2QidSbSX1KAQTDGFdWUM35m_B4wXTRP42JSrfFRAkLKfDtnFXxwaTN4eety0KUR9nxbXNXWPjrXD5uODkARszCofznKuYDJ67hjJQsPY16cj3c2_2a-L5eRmr2D5nagphrS4Jfd6kqzr7eQEJ7x01UX7z5jer_5TAjLhLrlZx2oHjH-OHGBjCplFlVayZJ1v1WzcGWL7ErILvLUcIuhVTdIJ0OCV5U_Qmm6Fx9069WWptBh23ELDMeSpU3QYqDnAjlhz-shrhoFIAJ7uREw9SAoCVV7rgK1_0T2m41q0UunAvXoH5q_IknAyaUmJAWTmuoxxaBA-GjYDLL0lEqK-yrKpCVX6p26Afh3RhJXCDrJIziQIcAILRoMqT8V12V4ni",
  "encrypted_key": "Vpv7bxGfT8fBhNQYvB2Cxpm2O4fEEqj7x8ZMDbIv5IrWC7Ad9a37Uw",
  "iv": "zwftaPgcs5FBGHKe",
  "protected": "eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkEyNTZHQ00iLCJlcGsiOnsiY3J2IjoiUC0zODQiLCJrdHkiOiJFQyIsIngiOiJhT0ZoRV9rZk9lQW9xc2ZlNlZRelY0SnREbGhzMzZEN2dud1RZN1p4NmVmUXVkMmp6YzgzNlR0aF9lYi1xWm0yIiwieSI6Im9idVdNS1A2UEFEcENCRjJYMXZmdnZDMXRNUXBHRjl2ams4RFdOZ044NjF4LXRiNEpfUHhuWlJPRmxFM21MdFQifSwia2lkIjoiMTIzNDU2Nzg5IiwidHlwIjoiYXBwbGljYXRpb24vaWRlbjNjb21tLWVuY3J5cHRlZC1qc29uIn0",
  "tag": "-k7eGDDgoeRBFtwp02F3gg"
}`

	var mapToken map[string]interface{}
	err := json.Unmarshal([]byte(token), &mapToken)
	require.NoError(t, err)

	// decode protected header
	protectedB64, ok := mapToken["protected"].(string)
	require.True(t, ok)
	protectedBytes, err := base64.RawURLEncoding.DecodeString(protectedB64)
	require.NoError(t, err)

	var protectedHeader map[string]interface{}
	err = json.Unmarshal(protectedBytes, &protectedHeader)
	require.NoError(t, err)
	require.NotEmpty(t, protectedHeader["alg"])

	checkIfAlgHeaderPresented := func(ctx context.Context, sink jwe.KeySink, r jwe.Recipient, msg *jwe.Message) error {
		recipientAlgHeader, ok := r.Headers().Algorithm()
		require.True(t, ok)
		require.NotEmpty(t, recipientAlgHeader.String())
		// code to resolve key would go here
		// headers for jwe.Recipient weren't merged with protected headers
		return nil
	}

	jwe.Decrypt([]byte(token),
		jwe.WithKeyProvider(
			jwe.KeyProviderFunc(
				checkIfAlgHeaderPresented)))
}

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions