@@ -4703,6 +4703,127 @@ const Digest Digest::FromName(const char* name) {
47034703 return ncrypto::getDigestByName (name);
47044704}
47054705
4706+ // ============================================================================
4707+ // KEM Implementation
4708+ #if OPENSSL_VERSION_MAJOR >= 3
4709+ #if !OPENSSL_VERSION_PREREQ(3, 5)
4710+ bool KEM::SetOperationParameter (EVP_PKEY_CTX* ctx, const EVPKeyPointer& key) {
4711+ const char * operation = nullptr ;
4712+
4713+ switch (EVP_PKEY_id (key.get ())) {
4714+ case EVP_PKEY_RSA:
4715+ operation = OSSL_KEM_PARAM_OPERATION_RSASVE;
4716+ break ;
4717+ #if OPENSSL_VERSION_PREREQ(3, 2)
4718+ case EVP_PKEY_EC:
4719+ case EVP_PKEY_X25519:
4720+ case EVP_PKEY_X448:
4721+ operation = OSSL_KEM_PARAM_OPERATION_DHKEM;
4722+ break ;
4723+ #endif
4724+ default :
4725+ unreachable ();
4726+ }
4727+
4728+ if (operation != nullptr ) {
4729+ OSSL_PARAM params[] = {
4730+ OSSL_PARAM_utf8_string (
4731+ OSSL_KEM_PARAM_OPERATION, const_cast <char *>(operation), 0 ),
4732+ OSSL_PARAM_END};
4733+
4734+ if (EVP_PKEY_CTX_set_params (ctx, params) <= 0 ) {
4735+ return false ;
4736+ }
4737+ }
4738+
4739+ return true ;
4740+ }
4741+ #endif
4742+
4743+ std::optional<KEM::EncapsulateResult> KEM::Encapsulate (
4744+ const EVPKeyPointer& public_key) {
4745+ ClearErrorOnReturn clear_error_on_return;
4746+
4747+ auto ctx = public_key.newCtx ();
4748+ if (!ctx) return std::nullopt ;
4749+
4750+ if (EVP_PKEY_encapsulate_init (ctx.get (), nullptr ) <= 0 ) {
4751+ return std::nullopt ;
4752+ }
4753+
4754+ #if !OPENSSL_VERSION_PREREQ(3, 5)
4755+ if (!SetOperationParameter (ctx.get (), public_key)) {
4756+ return std::nullopt ;
4757+ }
4758+ #endif
4759+
4760+ // Determine output buffer sizes
4761+ size_t ciphertext_len = 0 ;
4762+ size_t shared_key_len = 0 ;
4763+
4764+ if (EVP_PKEY_encapsulate (
4765+ ctx.get (), nullptr , &ciphertext_len, nullptr , &shared_key_len) <= 0 ) {
4766+ return std::nullopt ;
4767+ }
4768+
4769+ auto ciphertext = DataPointer::Alloc (ciphertext_len);
4770+ auto shared_key = DataPointer::Alloc (shared_key_len);
4771+ if (!ciphertext || !shared_key) return std::nullopt ;
4772+
4773+ if (EVP_PKEY_encapsulate (ctx.get (),
4774+ static_cast <unsigned char *>(ciphertext.get ()),
4775+ &ciphertext_len,
4776+ static_cast <unsigned char *>(shared_key.get ()),
4777+ &shared_key_len) <= 0 ) {
4778+ return std::nullopt ;
4779+ }
4780+
4781+ return EncapsulateResult (std::move (ciphertext), std::move (shared_key));
4782+ }
4783+
4784+ DataPointer KEM::Decapsulate (const EVPKeyPointer& private_key,
4785+ const Buffer<const void >& ciphertext) {
4786+ ClearErrorOnReturn clear_error_on_return;
4787+
4788+ auto ctx = private_key.newCtx ();
4789+ if (!ctx) return {};
4790+
4791+ if (EVP_PKEY_decapsulate_init (ctx.get (), nullptr ) <= 0 ) {
4792+ return {};
4793+ }
4794+
4795+ #if !OPENSSL_VERSION_PREREQ(3, 5)
4796+ if (!SetOperationParameter (ctx.get (), private_key)) {
4797+ return {};
4798+ }
4799+ #endif
4800+
4801+ // First pass: determine shared secret size
4802+ size_t shared_key_len = 0 ;
4803+ if (EVP_PKEY_decapsulate (ctx.get (),
4804+ nullptr ,
4805+ &shared_key_len,
4806+ static_cast <const unsigned char *>(ciphertext.data ),
4807+ ciphertext.len ) <= 0 ) {
4808+ return {};
4809+ }
4810+
4811+ auto shared_key = DataPointer::Alloc (shared_key_len);
4812+ if (!shared_key) return {};
4813+
4814+ if (EVP_PKEY_decapsulate (ctx.get (),
4815+ static_cast <unsigned char *>(shared_key.get ()),
4816+ &shared_key_len,
4817+ static_cast <const unsigned char *>(ciphertext.data ),
4818+ ciphertext.len ) <= 0 ) {
4819+ return {};
4820+ }
4821+
4822+ return shared_key;
4823+ }
4824+
4825+ #endif // OPENSSL_VERSION_MAJOR >= 3
4826+
47064827} // namespace ncrypto
47074828
47084829// ===========================================================================
0 commit comments