Skip to content

Add Hybrid Public Key Encryption (HPKE) API Support#1061

Open
sylph01 wants to merge 45 commits into
ruby:masterfrom
sylph01:hpke
Open

Add Hybrid Public Key Encryption (HPKE) API Support#1061
sylph01 wants to merge 45 commits into
ruby:masterfrom
sylph01:hpke

Conversation

@sylph01
Copy link
Copy Markdown
Contributor

@sylph01 sylph01 commented Jun 5, 2026

This patch introduces Hybrid Public Key Encryption (HPKE; RFC 9180) through OpenSSL's HPKE APIs ( https://docs.openssl.org/3.5/man3/OSSL_HPKE_CTX_new/ ), added in OpenSSL 3.2.0.

Usage

suite = OpenSSL::HPKE::Suite.new_with_names(:dhkem_x25519_hkdf_sha256, :hkdf_sha256, :aes_128_gcm)
pkey = OpenSSL::HPKE.keygen_with_suite(suite)
pub = pkey.raw_public_key
s = OpenSSL::HPKE::Context::Sender.new(:base, suite)
enc = s.encap(pub, "info")
ct = s.seal("aad", "hi")
r = OpenSSL::HPKE::Context::Receiver.new(:base, suite)
r.decap(enc, pkey, "info")
puts "roundtrip: #{r.open("aad", ct) == "hi"}, export match: #{s.export(32,"l")==r.export(32,"l")}"

APIs

OpenSSL::HPKE::Suite

  • new: Instantiate cipher suite with KEM, KDF, and AEAD identifiers listed in RFC 9180
  • new_with_names: Instantiate cipher suite with pre-defined names. Uses the list of KEMs, KDFs, AEADs listed in RFC 9180.

OpenSSL::HPKE

  • keygen: Generate OpenSSL::PKey private key with the specified KEM, KDF, and AEAD ID.
    • This exposes OpenSSL's OSSL_HPKE_keygen() API.
  • keygen_with_suite: Generate OpenSSL::PKey private key with the specified cipher suite

These are more like utility functions so if they look extraneous they can be removed in favor of using OpenSSL::PKey to generate corresponding keys.

OpenSSL::HPKE::Context::Sender and OpenSSL::HPKE::Context::Receiver

  • new: Instantiate HPKE Context.
    • Currently supports :base mode only; I wanted to let the maintainers see this pull request before adding :auth, :psk, and :auth_psk modes

OpenSSL::HPKE::Context::Sender

  • encap: Encapsulates key into the specified public key. Takes receiver's public key and info (application context information)
  • seal: Using the encapsulated key, seal message into ciphertext. Takes aad (additional authenticated data) and ciphertext itself.

OpenSSL::HPKE::Context::Receiver

  • decap: Decapsulates the key using the private key. Takes the encapsulation, private key, and info (application context information).
  • open: Using the decapsulated key, decrypt the ciphertext. Takes aad and ciphertext.

Availability

  • This functionality is available on OpenSSL newer than 3.2.0 without FIPS mode.
    • As OpenSSL's FIPS mode does not implement EC KEMs, HPKE on OpenSSL is unavailable even for curves that are supported by FIPS.
  • LibreSSL, AWS-LC is not supported.
    • As far as I know, they do not have the corresponding APIs.

sylph01 added 30 commits June 5, 2026 09:18
from now on this needs OpenSSL 3.2 to compile
works only with hpke.h that exposes OSSL_HPKE_CTX
The current longest possible public key size is 133 bytes, according to RFC 9180 section 7.1
- HPKE::Context.new that takes mode, role, and suite
- HPKE::Context now keeps track of which KEM/KDF/AEAD it uses under instance variable
- HPKE.keygen_with_suite
In this patch I also moved the `attr_reader` definitions of kem/kdf/aead_ids into C code
I am very iffy about this. Is there a safer way to handle this allocation?
The last version was not working....

- Sender and Receiver contexts get different classes
- Sender gets only sender APIs, Receiver gets only receiver APIs
- Sender and Receiver need Suite to initialize
- Removed old Context initialization API
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.

1 participant