Add Hybrid Public Key Encryption (HPKE) API Support#1061
Conversation
|
Thanks for working on this! Regarding the Ruby API:
Do you think if it makes sense to have this as an overload to I'm ambivalent about maintaining our own name list in ruby/openssl. I wonder if we could use
This seems useful to me, since there is no straightforward way to map from HPKE KEM IDs to OpenSSL algorithm object names. However I'm less sure about
That sounds like a good plan to me. |
rhenium
left a comment
There was a problem hiding this comment.
For style, please add newlines at the end of files and break long lines.
| #else | ||
| EVP_PKEY *pkey; | ||
| VALUE pkey_obj; | ||
| unsigned char pub[133]; // as per RFC9180 section 7.1, the maximum size of Npk possible is 133 |
There was a problem hiding this comment.
The list in RFC 9180 appears to be the initial content of the registry and I'd expect OpenSSL to implement new KEMs in the future, once published as an RFC: https://www.iana.org/assignments/hpke/hpke.xhtml
Is there any way to avoid hardcoding the maximum size?
There was a problem hiding this comment.
I looked for any publicly available constants/functions that can reach Npk, but couldn't find one.
OSSL_HPKE_get_public_encap_size(suite)does giveNenc, but for classical KEMs we can assumeNenc=Npk, but for PQ algorithms we cannot assume that (for example, ML-KEM has Nenc of 768 but has Npk of 800). And as for cases whenNenc=/=Npk, it isNenc<Npk, which means we cannot use the size ofNencfor the buffer size of key generation.- There is
OSSL_HPKE_MAXSIZE(512 for current OpenSSL implementations) underhpke.c, which is the internal limit for how large this buffer can be, but that value is not exposed publicly (only local tohpke.c). EVP_PKEY_get_size(pkey)exists but that needs a concrete EVP_PKEY, which means we cannot use that for deciding the buffer size of key generation.
As this function is the only part with hardcoded buffers, when PQ suites are introduced into HPKE, only this part will break, but breaks by raising that we could not keygen (EVP_PKEY_get_octet_string_param(skR, ENCODED_PUBLIC_KEY, pub, *publen, publen is called, but with the current publen it returns 0 without overwriting), not by introducing a buffer overflow.
For the reasons stated above, I suggest re-writing the comment so that:
- The value
133is the maximum Npk for classical KEMs - When a suite that requires a larger Npk is provided, this function will raise an error
- When PQ KEMs are introduced in future versions of OpenSSL this value needs to change accordingly
If we could get Npk in a similar manner with OSSL_HPKE_get_public_encap_size(suite) we can migrate gracefully, but as far as my research goes we're stuck with this option for now.
I might as well write up an issue in openssl/openssl...
There was a problem hiding this comment.
Thanks for the explanation. I saw openssl/openssl#31205, and it does seem like a missing feature in the current OpenSSL API. Let's revisit this when OSSL_HPKE_get_public_key_size() becomes available.
|
Regarding the public API:
Using
This looks reasonable, so I will change |
|
Just to clarify, I was wondering whether
|
Oops, I went too far on that one. I checked if there's an actual use case that wants the ID instead of the name:
As such I will fix that on the next round of patches. |
| unless defined?(OpenSSL::HPKE) | ||
| omit "HPKE is not supported by this OpenSSL" | ||
| end |
There was a problem hiding this comment.
In tests, we should check for openssl?(3, 2, 0) to avoid accidentally skipping tests for OpenSSL versions that are known to support it.
There was a problem hiding this comment.
Fixed @ cfcf9f2.
I am split on this one though; it's fine for now, but if LibreSSL gets HPKE, this version gate becomes more complicated, and I think checking the existence of the HPKE module would become the more straightforward check. If the point is to guard against the version gate (checking of OSSL_HPKE_CTX_new) not functioning properly on versions that actually have HPKE support, then I'll keep this version.
|
Added documentation @ 8c05446. I'll come back to the following when the right time comes:
(I have another feature regarding recent RFCs that I want to land so I'll work on that next) |
|
Could you squash changes into a commit? The changes look good to merge to me. |
|
ready @ 6b80681 |
rhenium
left a comment
There was a problem hiding this comment.
Thank you for your work (and patience with me) on this!
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
APIs
OpenSSL::HPKE::Suitenew: Instantiate cipher suite with KEM, KDF, and AEAD identifiers listed in RFC 9180new_with_names: Instantiate cipher suite with pre-defined names. Uses the list of KEMs, KDFs, AEADs listed in RFC 9180.OpenSSL::HPKEkeygen: GenerateOpenSSL::PKeyprivate key with the specified KEM, KDF, and AEAD ID.OSSL_HPKE_keygen()API.keygen_with_suite: GenerateOpenSSL::PKeyprivate key with the specified cipher suiteThese are more like utility functions so if they look extraneous they can be removed in favor of using
OpenSSL::PKeyto generate corresponding keys.OpenSSL::HPKE::Context::SenderandOpenSSL::HPKE::Context::Receivernew: Instantiate HPKE Context.:basemode only; I wanted to let the maintainers see this pull request before adding:auth,:psk, and:auth_pskmodesOpenSSL::HPKE::Context::Senderencap: Encapsulates key into the specified public key. Takes receiver's public key andinfo(application context information)seal: Using the encapsulated key, seal message into ciphertext. Takesaad(additional authenticated data) and ciphertext itself.OpenSSL::HPKE::Context::Receiverdecap: Decapsulates the key using the private key. Takes the encapsulation, private key, andinfo(application context information).open: Using the decapsulated key, decrypt the ciphertext. Takesaadand ciphertext.Availability