Skip to content

Add initial support of Hybrid Public-Key Encryption - #390

Draft
rjeffman wants to merge 2 commits into
latchset:mainfrom
rjeffman:hpke-draft
Draft

Add initial support of Hybrid Public-Key Encryption#390
rjeffman wants to merge 2 commits into
latchset:mainfrom
rjeffman:hpke-draft

Conversation

@rjeffman

Copy link
Copy Markdown
Collaborator

Hybrid Public-Key Encryption with JWE is in 'draft', and this PR implements the current RFC draft.

Due to pyca's cryptography limitations, X448 KEM and HPKE-[0:7] algorithms were not implemented as they are not currently exposed by the library, and I choose to not use private/protected methods/functions.

Also, PSK mode is not implemented as the cryptography HPKE implementation currently only supports mode_base.

See individual commits for more details on the implementation and decisions made.

@simo5

simo5 commented Aug 11, 2026

Copy link
Copy Markdown
Member

openssl only supports the base mode, so I guess that is why pyca also has that limitation

@simo5 simo5 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add the relevant "Implements" comment line at the top of jwa.py
Also move the official testvectors to the top of tests.py and identify their prvenance with a comment like for all other test vectors.

Implement HPKE (Hybrid Public Key Encryption) Key Encryption mode for
JWE, as specified in draft-ietf-jose-hpke-encrypt-22 ("Use of Hybrid
Public Key Encryption (HPKE) with JSON Web Encryption (JWE)").

In Key Encryption mode, HPKE encrypts the Content Encryption Key (CEK),
which then encrypts the payload via the standard JWE "enc" algorithm.
The HPKE encapsulated secret is carried in the new "ek" JWE header
parameter, while the HPKE-encrypted CEK goes to the JWE Encrypted Key
field. The HPKE "info" parameter is set to the Recipient_structure
defined in Section 6.1 of the draft:

  Recipient_structure = ASCII("JOSE-HPKE rcpt") || 0xFF ||
                        ASCII(content_encryption_alg) || 0xFF ||
                        recipient_extra_info

This mode supports multiple recipients and both compact and JSON JWE
serialization formats.

The following algorithms are implemented:

  HPKE-0-KE: DHKEM(P-256, HKDF-SHA256) + HKDF-SHA256 + AES-128-GCM
  HPKE-1-KE: DHKEM(P-384, HKDF-SHA384) + HKDF-SHA384 + AES-256-GCM
  HPKE-2-KE: DHKEM(P-521, HKDF-SHA512) + HKDF-SHA512 + AES-256-GCM
  HPKE-3-KE: DHKEM(X25519, HKDF-SHA256) + HKDF-SHA256 + AES-128-GCM
  HPKE-7-KE: DHKEM(P-256, HKDF-SHA256) + HKDF-SHA256 + AES-256-GCM

All algorithms use the cryptography library's public hpke.Suite API
with existing EC and OKP JWK key types (no new key types required).

Not implemented:

  HPKE-5-KE (DHKEM(X448)): The cryptography library (v49.0.0) does not
  expose an X448 KEM in its HPKE module, despite supporting X448 keys
  for key agreement. Without DHKEM(X448) support in the crypto backend,
  this algorithm cannot be implemented.

  Integrated Encryption (HPKE-0 through HPKE-7): These algorithms
  combine key encapsulation and content encryption in a single HPKE
  operation, requiring the HPKE AAD parameter to be set to the JWE
  protected header. The cryptography library's public hpke.Suite API
  does not accept an AAD parameter (only info), so integrated encryption
  cannot be implemented without relying on private/internal APIs.

  PSK mode (mode_psk with psk_id header): The cryptography library's
  HPKE implementation only supports mode_base. There is no API to pass
  a pre-shared key or PSK identifier to the HPKE key schedule.

Changes:
  jwa.py: Add _HpkeKe base class with wrap/unwrap methods and five
          concrete subclasses, registered in JWA.algorithms_registry.
  jwe.py: Register the "ek" header parameter in JWEHeaderRegistry and
          add all five HPKE-KE algorithms to default_allowed_algs.

Assisted-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Rafael Guterres Jeffman <rjeffman@redhat.com>
Test coverage for the HPKE-KE algorithms added in the previous commit,
exercising both the algorithm layer and the JWE integration.

Draft test vectors: Decryption of compact serialization tokens from
draft-ietf-jose-hpke-encrypt-22 Appendix A, verifying interoperability
against the reference outputs for HPKE-0-KE (A.2), HPKE-1-KE (A.4),
HPKE-2-KE (A.6), HPKE-3-KE (A.8), and HPKE-7-KE (A.14). Each vector
uses the private JWK from the draft and recovers the expected plaintext.
The HPKE-5-KE vector (A.11, X448) is omitted because the cryptography
library does not provide DHKEM(X448).

Round-trip tests: Encrypt-serialize-deserialize-decrypt for all five
algorithms in both JSON and compact JWE serialization formats,
verifying the "ek" header parameter is correctly placed in the
protected header for compact serialization.

Content encryption interoperability: HPKE-3-KE tested with A128GCM,
A256GCM, A128CBC-HS256, and A256CBC-HS512 to verify the key encryption
mode works with all standard JWE content encryption algorithms.

Multi-recipient: A JWE token with one HPKE-3-KE recipient and one
ECDH-ES+A128KW recipient sharing the same CEK, verifying that either
key can independently decrypt the message.

Key validation: Wrong key type (RSA key with HPKE-3-KE) and wrong
curve (P-384 key with HPKE-0-KE which requires P-256) both raise
errors. Decryption with a different X25519 key than the one used for
encryption correctly fails.

Recipient_structure: Direct verification that the binary info parameter
matches the hex value from Section 6.1 of the draft
(4a4f53452d48504b452072637074ff4131323847434dff for A128GCM).

Additional coverage: Large plaintext (64KB), DEF compression, JWE AAD,
and encryption with a public-key-only JWK (decryption with the full
key pair).

Assisted-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Rafael Guterres Jeffman <rjeffman@redhat.com>
@rjeffman
rjeffman requested a review from simo5 August 11, 2026 17:53
@rjeffman
rjeffman marked this pull request as ready for review August 11, 2026 17:53
@simo5

simo5 commented Aug 11, 2026

Copy link
Copy Markdown
Member

This looks good to me, however maybe we should wait a little bit and see if pyca/cryptography#15292 also gets resolved in the meanwhile ?

@rjeffman

Copy link
Copy Markdown
Collaborator Author

I agree. @simo5 I removed the draft status just because you did an actual review.

IMHO, there's still some time for this to be merged (the RFC is still a draft).

@simo5
simo5 marked this pull request as draft August 11, 2026 18:09
@simo5

simo5 commented Aug 11, 2026

Copy link
Copy Markdown
Member

let's keep it as draft until we are ready to merge then.

@rjeffman rjeffman linked an issue Aug 12, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Teak and implement HPKE once the RFC is published

2 participants