From 9ee96bd6e25b83e18dec37c98c3ef5ad228d5390 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Sat, 2 Dec 2023 23:08:58 +0900 Subject: [PATCH 01/45] [hpke] initialize OSSL::HPKE::Context --- ext/openssl/ossl_hpke_ctx.c | 25 +++++++++++++++++++++++++ ext/openssl/ossl_hpke_ctx.h | 10 ++++++++++ 2 files changed, 35 insertions(+) create mode 100644 ext/openssl/ossl_hpke_ctx.c create mode 100644 ext/openssl/ossl_hpke_ctx.h diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c new file mode 100644 index 000000000..d58242cb3 --- /dev/null +++ b/ext/openssl/ossl_hpke_ctx.c @@ -0,0 +1,25 @@ +#include "ossl.h" + +VALUE mHPKE; +VALUE cContext; + +static void +ossl_hpke_ctx_free(void *ptr) +{ + // OSSL_HPKE_CTX_free(ptr); +} + +const rb_data_type_t ossl_hpke_ctx_type = { + "OpenSSL/HPKE_CTX", + { + 0, ossl_hpke_ctx_free, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY +}; + +void +Init_ossl_hpke_ctx(void) +{ + mHPKE = rb_define_module_under(mOSSL, "HPKE"); + cContext = rb_define_class_under(mHPKE, "Context", rb_cObject); +} \ No newline at end of file diff --git a/ext/openssl/ossl_hpke_ctx.h b/ext/openssl/ossl_hpke_ctx.h new file mode 100644 index 000000000..992cea851 --- /dev/null +++ b/ext/openssl/ossl_hpke_ctx.h @@ -0,0 +1,10 @@ +#if !defined(OSSL_HPKE_CTX_H) +#define OSSL_HPKE_CTX_H + +extern VALUE mHPKE; +extern VALUE cContext; +extern const rb_data_type_t ossl_hpke_ctx_type; + +void Init_ossl_hpke_ctx(void); + +#endif \ No newline at end of file From e7e1f17c076e3701cfcaffd8b47db1ca40a8bb04 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Sun, 3 Dec 2023 13:33:50 +0900 Subject: [PATCH 02/45] [hpke] check for openssl/hpke.h from now on this needs OpenSSL 3.2 to compile --- ext/openssl/extconf.rb | 3 +++ ext/openssl/ossl.h | 3 +++ ext/openssl/ossl_hpke_ctx.c | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index 1f3298094..a862a61a4 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -176,6 +176,9 @@ def find_openssl_library # added in 4.0.0 have_func("ASN1_BIT_STRING_set1(NULL, NULL, 0, 0)", "openssl/asn1.h") +# added in 3.2.0 +have_header("openssl/hpke.h") + Logging::message "=== Checking done. ===\n" # Append flags from environment variables. diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h index 0b479a720..b89c7fc86 100644 --- a/ext/openssl/ossl.h +++ b/ext/openssl/ossl.h @@ -46,6 +46,9 @@ #include #include #include "openssl_missing.h" +#ifdef HAVE_OPENSSL_HPKE_H + #include +#endif #ifndef LIBRESSL_VERSION_NUMBER # define OSSL_IS_LIBRESSL 0 diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index d58242cb3..fb94372e9 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -6,7 +6,7 @@ VALUE cContext; static void ossl_hpke_ctx_free(void *ptr) { - // OSSL_HPKE_CTX_free(ptr); + OSSL_HPKE_CTX_free(ptr); } const rb_data_type_t ossl_hpke_ctx_type = { From ec8a3a279eddfb605b090b490b05b47556a8459d Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Sun, 3 Dec 2023 13:50:51 +0900 Subject: [PATCH 03/45] [hpke] allocate/initialize context --- ext/openssl/ossl_hpke_ctx.c | 36 ++++++++++++++++++++++++++++++++++++ ext/openssl/ossl_hpke_ctx.h | 2 ++ 2 files changed, 38 insertions(+) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index fb94372e9..b4970d3d9 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -9,6 +9,7 @@ ossl_hpke_ctx_free(void *ptr) OSSL_HPKE_CTX_free(ptr); } +/* public */ const rb_data_type_t ossl_hpke_ctx_type = { "OpenSSL/HPKE_CTX", { @@ -17,9 +18,44 @@ const rb_data_type_t ossl_hpke_ctx_type = { 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; +static VALUE +hpke_ctx_new0(VALUE arg) +{ + OSSL_HPKE_CTX *ctx = (OSSL_HPKE_CTX *)arg; + VALUE obj; + + obj = rb_obj_alloc(cContext); + RTYPEDDATA_DATA(obj) = ctx; + return obj; +} + +VALUE +ossl_hpke_ctx_new(OSSL_HPKE_CTX *ctx) +{ + VALUE obj; + int status; + + obj = rb_protect(hpke_ctx_new0, (VALUE)ctx, &status); + if (status) { + OSSL_HPKE_CTX_free(ctx); + rb_jump_tag(status); + } + + return obj; +} + +/* private */ +static VALUE +ossl_hpke_ctx_alloc(VALUE klass) +{ + return TypedData_Wrap_Struct(klass, &ossl_hpke_ctx_type, NULL); +} + void Init_ossl_hpke_ctx(void) { mHPKE = rb_define_module_under(mOSSL, "HPKE"); cContext = rb_define_class_under(mHPKE, "Context", rb_cObject); + + rb_define_alloc_func(cContext, ossl_hpke_ctx_alloc); } \ No newline at end of file diff --git a/ext/openssl/ossl_hpke_ctx.h b/ext/openssl/ossl_hpke_ctx.h index 992cea851..dbc8c9144 100644 --- a/ext/openssl/ossl_hpke_ctx.h +++ b/ext/openssl/ossl_hpke_ctx.h @@ -7,4 +7,6 @@ extern const rb_data_type_t ossl_hpke_ctx_type; void Init_ossl_hpke_ctx(void); +VALUE ossl_hpke_ctx_new(OSSL_HPKE_CTX *); + #endif \ No newline at end of file From afe4bffb3c19ea61c677fcd6a04cc84b3b29e699 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Sun, 3 Dec 2023 17:28:59 +0900 Subject: [PATCH 04/45] [hpke-a] experiment: try reopening class with a ruby file --- lib/openssl.rb | 1 + lib/openssl/hpke.rb | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 lib/openssl/hpke.rb diff --git a/lib/openssl.rb b/lib/openssl.rb index 98fa8d39f..233f1c048 100644 --- a/lib/openssl.rb +++ b/lib/openssl.rb @@ -21,6 +21,7 @@ require_relative 'openssl/ssl' require_relative 'openssl/version' require_relative 'openssl/x509' +require_relative 'openssl/hpke' module OpenSSL # :call-seq: diff --git a/lib/openssl/hpke.rb b/lib/openssl/hpke.rb new file mode 100644 index 000000000..5ff5e04f3 --- /dev/null +++ b/lib/openssl/hpke.rb @@ -0,0 +1,7 @@ +module OpenSSL::HPKE + class Context + def hoge + 'a' + end + end +end From 34ea7a331a2f6da93714badc5d1502ded440f53f Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Sun, 3 Dec 2023 18:34:38 +0900 Subject: [PATCH 05/45] [hpke] HPKE.keygen --- ext/openssl/ossl_hpke_ctx.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index b4970d3d9..597dc7c8b 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -2,6 +2,7 @@ VALUE mHPKE; VALUE cContext; +VALUE eHPKEError; static void ossl_hpke_ctx_free(void *ptr) @@ -51,11 +52,35 @@ ossl_hpke_ctx_alloc(VALUE klass) return TypedData_Wrap_Struct(klass, &ossl_hpke_ctx_type, NULL); } +/* HPKE module method */ +VALUE +ossl_hpke_keygen(VALUE self, VALUE kem_id, VALUE kdf_id, VALUE aead_id) +{ + EVP_PKEY *pkey; + VALUE pkey_obj; + unsigned char pub[128]; + size_t publen; + OSSL_HPKE_SUITE hpke_suite = { + NUM2INT(kem_id), NUM2INT(kdf_id), NUM2INT(aead_id) + }; + + if(!OSSL_HPKE_keygen(hpke_suite, pub, &publen, &pkey, NULL, 0, NULL, NULL)){ + ossl_raise(eHPKEError, "could not keygen"); + } + + pkey_obj = ossl_pkey_new(pkey); + + return pkey_obj; +} + void Init_ossl_hpke_ctx(void) { mHPKE = rb_define_module_under(mOSSL, "HPKE"); cContext = rb_define_class_under(mHPKE, "Context", rb_cObject); + eHPKEError = rb_define_class_under(mHPKE, "HPKEError", eOSSLError); + + rb_define_module_function(mHPKE, "keygen", ossl_hpke_keygen, 3); rb_define_alloc_func(cContext, ossl_hpke_ctx_alloc); } \ No newline at end of file From 5301f445149a4b798cfd9e8f25582d6bbc0b106e Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Sun, 3 Dec 2023 19:55:17 +0900 Subject: [PATCH 06/45] [hpke] create HPKE::Context --- ext/openssl/ossl_hpke_ctx.c | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index 597dc7c8b..754bf279b 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -45,6 +45,42 @@ ossl_hpke_ctx_new(OSSL_HPKE_CTX *ctx) return obj; } +VALUE +ossl_hpke_ctx_new_sender(VALUE self, VALUE mode_id, VALUE kem_id, VALUE kdf_id, VALUE aead_id) +{ + OSSL_HPKE_CTX *sctx; + VALUE obj; + OSSL_HPKE_SUITE hpke_suite = { + NUM2INT(kem_id), NUM2INT(kdf_id), NUM2INT(aead_id) + }; + + if((sctx = OSSL_HPKE_CTX_new(NUM2INT(mode_id), hpke_suite, OSSL_HPKE_ROLE_SENDER, NULL, NULL)) == NULL) { + ossl_raise(eHPKEError, "could not create ctx"); + } + + obj = ossl_hpke_ctx_new(sctx); + + return obj; +} + +VALUE +ossl_hpke_ctx_new_receiver(VALUE self, VALUE mode_id, VALUE kem_id, VALUE kdf_id, VALUE aead_id) +{ + OSSL_HPKE_CTX *sctx; + VALUE obj; + OSSL_HPKE_SUITE hpke_suite = { + NUM2INT(kem_id), NUM2INT(kdf_id), NUM2INT(aead_id) + }; + + if((sctx = OSSL_HPKE_CTX_new(NUM2INT(mode_id), hpke_suite, OSSL_HPKE_ROLE_RECEIVER, NULL, NULL)) == NULL) { + ossl_raise(eHPKEError, "could not create ctx"); + } + + obj = ossl_hpke_ctx_new(sctx); + + return obj; +} + /* private */ static VALUE ossl_hpke_ctx_alloc(VALUE klass) @@ -82,5 +118,8 @@ Init_ossl_hpke_ctx(void) rb_define_module_function(mHPKE, "keygen", ossl_hpke_keygen, 3); + rb_define_singleton_method(cContext, "new_sender", ossl_hpke_ctx_new_sender, 4); + rb_define_singleton_method(cContext, "new_receiver", ossl_hpke_ctx_new_receiver, 4); + rb_define_alloc_func(cContext, ossl_hpke_ctx_alloc); } \ No newline at end of file From aaaac909a5d8918fa17dea017de943b92276c146 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Sun, 3 Dec 2023 21:45:36 +0900 Subject: [PATCH 07/45] [hpke] encap --- ext/openssl/ossl_hpke_ctx.c | 26 ++++++++++++++++++++++++++ ext/openssl/ossl_hpke_ctx.h | 7 +++++++ 2 files changed, 33 insertions(+) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index 754bf279b..bf89cf13c 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -81,6 +81,31 @@ ossl_hpke_ctx_new_receiver(VALUE self, VALUE mode_id, VALUE kem_id, VALUE kdf_id return obj; } +VALUE +ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) +{ + VALUE enc_obj; + unsigned char enc[1000]; + size_t enclen; + OSSL_HPKE_CTX *sctx; + size_t publen; + size_t infolen; + + GetHpkeCtx(self, sctx); + + enclen = sizeof(enc); + publen = RSTRING_LEN(pub); + infolen = RSTRING_LEN(info); + + if (OSSL_HPKE_encap(sctx, enc, &enclen, (unsigned char*)RSTRING_PTR(pub), publen, (unsigned char*)RSTRING_PTR(pub), infolen) != 1) { + ossl_raise(eHPKEError, "could not encap"); + } + + enc_obj = rb_str_new_cstr((char *)enc); + + return enc_obj; +} + /* private */ static VALUE ossl_hpke_ctx_alloc(VALUE klass) @@ -120,6 +145,7 @@ Init_ossl_hpke_ctx(void) rb_define_singleton_method(cContext, "new_sender", ossl_hpke_ctx_new_sender, 4); rb_define_singleton_method(cContext, "new_receiver", ossl_hpke_ctx_new_receiver, 4); + rb_define_method(cContext, "encap", ossl_hpke_encap, 2); rb_define_alloc_func(cContext, ossl_hpke_ctx_alloc); } \ No newline at end of file diff --git a/ext/openssl/ossl_hpke_ctx.h b/ext/openssl/ossl_hpke_ctx.h index dbc8c9144..020d89955 100644 --- a/ext/openssl/ossl_hpke_ctx.h +++ b/ext/openssl/ossl_hpke_ctx.h @@ -5,6 +5,13 @@ extern VALUE mHPKE; extern VALUE cContext; extern const rb_data_type_t ossl_hpke_ctx_type; +#define GetHpkeCtx(obj, ctx) do {\ + TypedData_Get_Struct((obj), OSSL_HPKE_CTX, &ossl_hpke_ctx_type, (ctx)); \ + if (!(ctx)) { \ + rb_raise(rb_eRuntimeError, "OSSL_HPKE_CTX wasn't initialized!");\ + } \ +} while (0) + void Init_ossl_hpke_ctx(void); VALUE ossl_hpke_ctx_new(OSSL_HPKE_CTX *); From d8b0ddaf1eaa28c01cdd182931cf97e73f554b5e Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Sun, 3 Dec 2023 21:50:44 +0900 Subject: [PATCH 08/45] set constant buffers to a high enough number --- ext/openssl/ossl_hpke_ctx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index bf89cf13c..6c6c266a6 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -85,7 +85,7 @@ VALUE ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) { VALUE enc_obj; - unsigned char enc[1000]; + unsigned char enc[1024]; size_t enclen; OSSL_HPKE_CTX *sctx; size_t publen; @@ -119,7 +119,7 @@ ossl_hpke_keygen(VALUE self, VALUE kem_id, VALUE kdf_id, VALUE aead_id) { EVP_PKEY *pkey; VALUE pkey_obj; - unsigned char pub[128]; + unsigned char pub[256]; size_t publen; OSSL_HPKE_SUITE hpke_suite = { NUM2INT(kem_id), NUM2INT(kdf_id), NUM2INT(aead_id) From d3b0ddf3e00aa22d080a70cfc385425c06a9ac7c Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Sun, 3 Dec 2023 22:08:36 +0900 Subject: [PATCH 09/45] [hpke] seal --- ext/openssl/ossl_hpke_ctx.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index 6c6c266a6..b7c6f6881 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -106,6 +106,28 @@ ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) return enc_obj; } +VALUE +ossl_hpke_seal(VALUE self, VALUE aad, VALUE pt) +{ + VALUE ct_obj; + OSSL_HPKE_CTX *sctx; + size_t ctlen, aadlen, ptlen; + + aadlen = RSTRING_LEN(aad); + ptlen = RSTRING_LEN(pt); + ctlen = ptlen + 16; // block size is known to be at maximum 16 characters so use that + + ct_obj = rb_str_new(0, ctlen); + + GetHpkeCtx(self, sctx); + + if (OSSL_HPKE_seal(sctx, (unsigned char *)RSTRING_PTR(ct_obj), &ctlen, (unsigned char*)RSTRING_PTR(aad), aadlen, (unsigned char*)RSTRING_PTR(pt), ptlen) != 1) { + ossl_raise(eHPKEError, "could not seal"); + } + + return ct_obj; +} + /* private */ static VALUE ossl_hpke_ctx_alloc(VALUE klass) @@ -146,6 +168,7 @@ Init_ossl_hpke_ctx(void) rb_define_singleton_method(cContext, "new_sender", ossl_hpke_ctx_new_sender, 4); rb_define_singleton_method(cContext, "new_receiver", ossl_hpke_ctx_new_receiver, 4); rb_define_method(cContext, "encap", ossl_hpke_encap, 2); + rb_define_method(cContext, "seal", ossl_hpke_seal, 2); rb_define_alloc_func(cContext, ossl_hpke_ctx_alloc); } \ No newline at end of file From 64748ed260914b22c4b495825ed7f1fe55727dba Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Sun, 3 Dec 2023 22:26:02 +0900 Subject: [PATCH 10/45] [hpke] decap --- ext/openssl/ossl_hpke_ctx.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index b7c6f6881..f33c972d6 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -128,6 +128,27 @@ ossl_hpke_seal(VALUE self, VALUE aad, VALUE pt) return ct_obj; } +VALUE +ossl_hpke_decap(VALUE self, VALUE enc, VALUE priv, VALUE info) +{ + OSSL_HPKE_CTX *rctx; + EVP_PKEY *pkey; + size_t enclen; + size_t infolen; + + GetHpkeCtx(self, rctx); + GetPKey(priv, pkey); // TODO: if priv was not a PKey then reject + + enclen = RSTRING_LEN(enc); + infolen = RSTRING_LEN(info); + + if (OSSL_HPKE_decap(rctx, (unsigned char *)RSTRING_PTR(enc), enclen, pkey, (unsigned char *)RSTRING_PTR(info), infolen) != 1) { + ossl_raise(eHPKEError, "could not decap"); + } + + return Qtrue; +} + /* private */ static VALUE ossl_hpke_ctx_alloc(VALUE klass) @@ -170,5 +191,8 @@ Init_ossl_hpke_ctx(void) rb_define_method(cContext, "encap", ossl_hpke_encap, 2); rb_define_method(cContext, "seal", ossl_hpke_seal, 2); + rb_define_method(cContext, "decap", ossl_hpke_decap, 3); + // rb_define_method(cContext, "open", ossl_hpke_open, 2); + rb_define_alloc_func(cContext, ossl_hpke_ctx_alloc); } \ No newline at end of file From 2f5d6454216df4798e911f55a0ba46a5a283dd91 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Sun, 3 Dec 2023 23:02:38 +0900 Subject: [PATCH 11/45] [hpke] open and export, but getting different values? --- ext/openssl/ossl_hpke_ctx.c | 47 ++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index f33c972d6..84b6f15d6 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -116,6 +116,7 @@ ossl_hpke_seal(VALUE self, VALUE aad, VALUE pt) aadlen = RSTRING_LEN(aad); ptlen = RSTRING_LEN(pt); ctlen = ptlen + 16; // block size is known to be at maximum 16 characters so use that + // TODO: use OSSL_HPKE_get_ciphertext_size ct_obj = rb_str_new(0, ctlen); @@ -149,6 +150,48 @@ ossl_hpke_decap(VALUE self, VALUE enc, VALUE priv, VALUE info) return Qtrue; } +VALUE +ossl_hpke_open(VALUE self, VALUE aad, VALUE ct) +{ + VALUE pt_obj; + OSSL_HPKE_CTX *rctx; + size_t ptlen, aadlen, ctlen; + + aadlen = RSTRING_LEN(aad); + ctlen = RSTRING_LEN(ct); + ptlen = ctlen; + + pt_obj = rb_str_new(0, ptlen); + + GetHpkeCtx(self, rctx); + + if (OSSL_HPKE_open(rctx, (unsigned char *)RSTRING_PTR(pt_obj), &ptlen, (unsigned char*)RSTRING_PTR(aad), aadlen, (unsigned char*)RSTRING_PTR(ct), ctlen) != 1) { + ossl_raise(eHPKEError, "could not open"); + } + + return pt_obj; +} + +VALUE +ossl_hpke_export(VALUE self, VALUE secretlen, VALUE label) +{ + VALUE secret_obj; + OSSL_HPKE_CTX *ctx; + size_t labellen; + + labellen = RSTRING_LEN(label); + + secret_obj = rb_str_new(0, NUM2INT(secretlen)); + + GetHpkeCtx(self, ctx); + + if (OSSL_HPKE_export(ctx, (unsigned char *)RSTRING_PTR(secret_obj), NUM2INT(secretlen), (unsigned char*)RSTRING_PTR(label), labellen) != 1) { + ossl_raise(eHPKEError, "could not export"); + } + + return secret_obj; +} + /* private */ static VALUE ossl_hpke_ctx_alloc(VALUE klass) @@ -192,7 +235,9 @@ Init_ossl_hpke_ctx(void) rb_define_method(cContext, "seal", ossl_hpke_seal, 2); rb_define_method(cContext, "decap", ossl_hpke_decap, 3); - // rb_define_method(cContext, "open", ossl_hpke_open, 2); + rb_define_method(cContext, "open", ossl_hpke_open, 2); + + rb_define_method(cContext, "export", ossl_hpke_export, 2); rb_define_alloc_func(cContext, ossl_hpke_ctx_alloc); } \ No newline at end of file From 024c8f0c7ad3db66ff9675bbeab94f1d88bad1c1 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Mon, 4 Dec 2023 08:38:09 +0900 Subject: [PATCH 12/45] [hpke] debug: keygen_pub --- ext/openssl/ossl_hpke_ctx.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index 84b6f15d6..ff8ac73cf 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -220,6 +220,26 @@ ossl_hpke_keygen(VALUE self, VALUE kem_id, VALUE kdf_id, VALUE aead_id) return pkey_obj; } +VALUE +ossl_hpke_keygen_pub(VALUE self, VALUE kem_id, VALUE kdf_id, VALUE aead_id) +{ + EVP_PKEY *pkey; + VALUE pub_obj; + unsigned char pub[256]; + size_t publen; + OSSL_HPKE_SUITE hpke_suite = { + NUM2INT(kem_id), NUM2INT(kdf_id), NUM2INT(aead_id) + }; + + if(!OSSL_HPKE_keygen(hpke_suite, pub, &publen, &pkey, NULL, 0, NULL, NULL)){ + ossl_raise(eHPKEError, "could not keygen"); + } + + pub_obj = rb_str_new((char *)pub, publen); + + return pub_obj; +} + void Init_ossl_hpke_ctx(void) { @@ -228,6 +248,7 @@ Init_ossl_hpke_ctx(void) eHPKEError = rb_define_class_under(mHPKE, "HPKEError", eOSSLError); rb_define_module_function(mHPKE, "keygen", ossl_hpke_keygen, 3); + rb_define_module_function(mHPKE, "keygen_pub", ossl_hpke_keygen_pub, 3); rb_define_singleton_method(cContext, "new_sender", ossl_hpke_ctx_new_sender, 4); rb_define_singleton_method(cContext, "new_receiver", ossl_hpke_ctx_new_receiver, 4); From 3d0b6e3bf9119e88ae70fc7c9918bfcae13a7316 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Mon, 4 Dec 2023 08:46:30 +0900 Subject: [PATCH 13/45] [hpke] debug: assume fixed ikme --- ext/openssl/ossl_hpke_ctx.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index ff8ac73cf..79222da04 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -97,6 +97,9 @@ ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) publen = RSTRING_LEN(pub); infolen = RSTRING_LEN(info); + unsigned char ikme[32] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + OSSL_HPKE_CTX_set1_ikme(sctx, ikme, 32); + if (OSSL_HPKE_encap(sctx, enc, &enclen, (unsigned char*)RSTRING_PTR(pub), publen, (unsigned char*)RSTRING_PTR(pub), infolen) != 1) { ossl_raise(eHPKEError, "could not encap"); } @@ -140,6 +143,9 @@ ossl_hpke_decap(VALUE self, VALUE enc, VALUE priv, VALUE info) GetHpkeCtx(self, rctx); GetPKey(priv, pkey); // TODO: if priv was not a PKey then reject + unsigned char ikme[32] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + OSSL_HPKE_CTX_set1_ikme(rctx, ikme, 32); + enclen = RSTRING_LEN(enc); infolen = RSTRING_LEN(info); From 1efad02e0a5a4c406508ea6f4b103680a5a03620 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Mon, 4 Dec 2023 11:00:49 +0900 Subject: [PATCH 14/45] lots of debug prints works only with hpke.h that exposes OSSL_HPKE_CTX --- ext/openssl/ossl_hpke_ctx.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index 79222da04..c23bab164 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -97,13 +97,16 @@ ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) publen = RSTRING_LEN(pub); infolen = RSTRING_LEN(info); - unsigned char ikme[32] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + unsigned char ikme[32] = {0x02, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; OSSL_HPKE_CTX_set1_ikme(sctx, ikme, 32); if (OSSL_HPKE_encap(sctx, enc, &enclen, (unsigned char*)RSTRING_PTR(pub), publen, (unsigned char*)RSTRING_PTR(pub), infolen) != 1) { ossl_raise(eHPKEError, "could not encap"); } + rb_p(rb_sprintf("ss: %s", sctx->shared_secret)); + rb_p(rb_sprintf("sl: %ld", sctx->shared_secretlen)); + enc_obj = rb_str_new_cstr((char *)enc); return enc_obj; @@ -121,11 +124,18 @@ ossl_hpke_seal(VALUE self, VALUE aad, VALUE pt) ctlen = ptlen + 16; // block size is known to be at maximum 16 characters so use that // TODO: use OSSL_HPKE_get_ciphertext_size + const unsigned char aad_[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + size_t aadlen_ = 8; + ct_obj = rb_str_new(0, ctlen); GetHpkeCtx(self, sctx); - if (OSSL_HPKE_seal(sctx, (unsigned char *)RSTRING_PTR(ct_obj), &ctlen, (unsigned char*)RSTRING_PTR(aad), aadlen, (unsigned char*)RSTRING_PTR(pt), ptlen) != 1) { + rb_p(rb_sprintf("%s", sctx->shared_secret)); + rb_p(rb_sprintf("%ld", sctx->shared_secretlen)); + + // if (OSSL_HPKE_seal(sctx, (unsigned char *)RSTRING_PTR(ct_obj), &ctlen, (unsigned char*)RSTRING_PTR(aad), aadlen, (unsigned char*)RSTRING_PTR(pt), ptlen) != 1) { + if (OSSL_HPKE_seal(sctx, (unsigned char *)RSTRING_PTR(ct_obj), &ctlen, aad_, aadlen_, (unsigned char*)RSTRING_PTR(pt), ptlen) != 1) { ossl_raise(eHPKEError, "could not seal"); } @@ -143,16 +153,22 @@ ossl_hpke_decap(VALUE self, VALUE enc, VALUE priv, VALUE info) GetHpkeCtx(self, rctx); GetPKey(priv, pkey); // TODO: if priv was not a PKey then reject - unsigned char ikme[32] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; - OSSL_HPKE_CTX_set1_ikme(rctx, ikme, 32); + // unsigned char ikme[32] = {0x02, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + // OSSL_HPKE_CTX_set1_ikme(rctx, ikme, 32); enclen = RSTRING_LEN(enc); infolen = RSTRING_LEN(info); + rb_p(enc); + rb_p(rb_sprintf("enclen: %ld\n", enclen)); + if (OSSL_HPKE_decap(rctx, (unsigned char *)RSTRING_PTR(enc), enclen, pkey, (unsigned char *)RSTRING_PTR(info), infolen) != 1) { ossl_raise(eHPKEError, "could not decap"); } + rb_p(rb_sprintf("%s", rctx->shared_secret)); + rb_p(rb_sprintf("%ld", rctx->shared_secretlen)); + return Qtrue; } @@ -190,6 +206,10 @@ ossl_hpke_export(VALUE self, VALUE secretlen, VALUE label) secret_obj = rb_str_new(0, NUM2INT(secretlen)); GetHpkeCtx(self, ctx); + rb_p(rb_sprintf("%s", ctx->shared_secret)); + rb_p(rb_sprintf("%ld", ctx->shared_secretlen)); + rb_p(rb_sprintf("%s", ctx->exportersec)); + rb_p(rb_sprintf("%ld", ctx->exporterseclen)); if (OSSL_HPKE_export(ctx, (unsigned char *)RSTRING_PTR(secret_obj), NUM2INT(secretlen), (unsigned char*)RSTRING_PTR(label), labellen) != 1) { ossl_raise(eHPKEError, "could not export"); @@ -216,8 +236,11 @@ ossl_hpke_keygen(VALUE self, VALUE kem_id, VALUE kdf_id, VALUE aead_id) OSSL_HPKE_SUITE hpke_suite = { NUM2INT(kem_id), NUM2INT(kdf_id), NUM2INT(aead_id) }; + publen = 256; - if(!OSSL_HPKE_keygen(hpke_suite, pub, &publen, &pkey, NULL, 0, NULL, NULL)){ + unsigned char ikm[32] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + + if(!OSSL_HPKE_keygen(hpke_suite, pub, &publen, &pkey, ikm, 32, NULL, NULL)){ ossl_raise(eHPKEError, "could not keygen"); } From 5411682786697168436ab4be30439793b881e6ff Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Mon, 4 Dec 2023 11:48:01 +0900 Subject: [PATCH 15/45] [hpke] :tada: --- ext/openssl/ossl_hpke_ctx.c | 45 ++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index c23bab164..482fc4001 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -4,6 +4,16 @@ VALUE mHPKE; VALUE cContext; VALUE eHPKEError; +void +rbdebug_print_hex(const unsigned char *str, size_t len) +{ + VALUE rbstr; + + rbstr = rb_str_new((char *)str, len); + + rb_p(rb_funcall(rbstr, rb_intern("unpack1"), 1, rb_str_new_cstr("H*"))); +} + static void ossl_hpke_ctx_free(void *ptr) { @@ -100,12 +110,13 @@ ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) unsigned char ikme[32] = {0x02, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; OSSL_HPKE_CTX_set1_ikme(sctx, ikme, 32); - if (OSSL_HPKE_encap(sctx, enc, &enclen, (unsigned char*)RSTRING_PTR(pub), publen, (unsigned char*)RSTRING_PTR(pub), infolen) != 1) { + if (OSSL_HPKE_encap(sctx, enc, &enclen, (unsigned char*)RSTRING_PTR(pub), publen, (unsigned char*)RSTRING_PTR(info), infolen) != 1) { ossl_raise(eHPKEError, "could not encap"); } - rb_p(rb_sprintf("ss: %s", sctx->shared_secret)); - rb_p(rb_sprintf("sl: %ld", sctx->shared_secretlen)); + rbdebug_print_hex(sctx->shared_secret, sctx->shared_secretlen); + rbdebug_print_hex(sctx->nonce, sctx->noncelen); + rbdebug_print_hex(sctx->key, sctx->keylen); enc_obj = rb_str_new_cstr((char *)enc); @@ -124,18 +135,16 @@ ossl_hpke_seal(VALUE self, VALUE aad, VALUE pt) ctlen = ptlen + 16; // block size is known to be at maximum 16 characters so use that // TODO: use OSSL_HPKE_get_ciphertext_size - const unsigned char aad_[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; - size_t aadlen_ = 8; - ct_obj = rb_str_new(0, ctlen); GetHpkeCtx(self, sctx); - rb_p(rb_sprintf("%s", sctx->shared_secret)); - rb_p(rb_sprintf("%ld", sctx->shared_secretlen)); + rbdebug_print_hex(sctx->shared_secret, sctx->shared_secretlen); + rbdebug_print_hex(sctx->nonce, sctx->noncelen); + rbdebug_print_hex(sctx->key, sctx->keylen); // if (OSSL_HPKE_seal(sctx, (unsigned char *)RSTRING_PTR(ct_obj), &ctlen, (unsigned char*)RSTRING_PTR(aad), aadlen, (unsigned char*)RSTRING_PTR(pt), ptlen) != 1) { - if (OSSL_HPKE_seal(sctx, (unsigned char *)RSTRING_PTR(ct_obj), &ctlen, aad_, aadlen_, (unsigned char*)RSTRING_PTR(pt), ptlen) != 1) { + if (OSSL_HPKE_seal(sctx, (unsigned char *)RSTRING_PTR(ct_obj), &ctlen, (unsigned char*)RSTRING_PTR(aad), aadlen, (unsigned char*)RSTRING_PTR(pt), ptlen) != 1) { ossl_raise(eHPKEError, "could not seal"); } @@ -166,8 +175,9 @@ ossl_hpke_decap(VALUE self, VALUE enc, VALUE priv, VALUE info) ossl_raise(eHPKEError, "could not decap"); } - rb_p(rb_sprintf("%s", rctx->shared_secret)); - rb_p(rb_sprintf("%ld", rctx->shared_secretlen)); + rbdebug_print_hex(rctx->shared_secret, rctx->shared_secretlen); + rbdebug_print_hex(rctx->nonce, rctx->noncelen); + rbdebug_print_hex(rctx->key, rctx->keylen); return Qtrue; } @@ -186,11 +196,16 @@ ossl_hpke_open(VALUE self, VALUE aad, VALUE ct) pt_obj = rb_str_new(0, ptlen); GetHpkeCtx(self, rctx); + rbdebug_print_hex(rctx->shared_secret, rctx->shared_secretlen); + rbdebug_print_hex(rctx->nonce, rctx->noncelen); + rbdebug_print_hex(rctx->key, rctx->keylen); if (OSSL_HPKE_open(rctx, (unsigned char *)RSTRING_PTR(pt_obj), &ptlen, (unsigned char*)RSTRING_PTR(aad), aadlen, (unsigned char*)RSTRING_PTR(ct), ctlen) != 1) { ossl_raise(eHPKEError, "could not open"); } + rb_str_resize(pt_obj, ptlen); + return pt_obj; } @@ -206,10 +221,10 @@ ossl_hpke_export(VALUE self, VALUE secretlen, VALUE label) secret_obj = rb_str_new(0, NUM2INT(secretlen)); GetHpkeCtx(self, ctx); - rb_p(rb_sprintf("%s", ctx->shared_secret)); - rb_p(rb_sprintf("%ld", ctx->shared_secretlen)); - rb_p(rb_sprintf("%s", ctx->exportersec)); - rb_p(rb_sprintf("%ld", ctx->exporterseclen)); + rbdebug_print_hex(ctx->shared_secret, ctx->shared_secretlen); + rbdebug_print_hex(ctx->nonce, ctx->noncelen); + rbdebug_print_hex(ctx->key, ctx->keylen); + rbdebug_print_hex(ctx->exportersec, ctx->exporterseclen); if (OSSL_HPKE_export(ctx, (unsigned char *)RSTRING_PTR(secret_obj), NUM2INT(secretlen), (unsigned char*)RSTRING_PTR(label), labellen) != 1) { ossl_raise(eHPKEError, "could not export"); From e1f743e20fa8062ca78941bb42bf42b91b76e315 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Mon, 4 Dec 2023 20:16:44 +0900 Subject: [PATCH 16/45] [hpke] fix encap so that it returns a string with correct length --- ext/openssl/ossl_hpke_ctx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index 482fc4001..48fe2f7b1 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -118,7 +118,7 @@ ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) rbdebug_print_hex(sctx->nonce, sctx->noncelen); rbdebug_print_hex(sctx->key, sctx->keylen); - enc_obj = rb_str_new_cstr((char *)enc); + enc_obj = rb_str_new((char *)enc, enclen); return enc_obj; } From e68494bfe390e2b46ae618554327b73c93095197 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Mon, 4 Dec 2023 20:17:07 +0900 Subject: [PATCH 17/45] [hpke] do not assume fixed ikm_e --- ext/openssl/ossl_hpke_ctx.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index 48fe2f7b1..d337be973 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -107,9 +107,6 @@ ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) publen = RSTRING_LEN(pub); infolen = RSTRING_LEN(info); - unsigned char ikme[32] = {0x02, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; - OSSL_HPKE_CTX_set1_ikme(sctx, ikme, 32); - if (OSSL_HPKE_encap(sctx, enc, &enclen, (unsigned char*)RSTRING_PTR(pub), publen, (unsigned char*)RSTRING_PTR(info), infolen) != 1) { ossl_raise(eHPKEError, "could not encap"); } @@ -143,7 +140,6 @@ ossl_hpke_seal(VALUE self, VALUE aad, VALUE pt) rbdebug_print_hex(sctx->nonce, sctx->noncelen); rbdebug_print_hex(sctx->key, sctx->keylen); - // if (OSSL_HPKE_seal(sctx, (unsigned char *)RSTRING_PTR(ct_obj), &ctlen, (unsigned char*)RSTRING_PTR(aad), aadlen, (unsigned char*)RSTRING_PTR(pt), ptlen) != 1) { if (OSSL_HPKE_seal(sctx, (unsigned char *)RSTRING_PTR(ct_obj), &ctlen, (unsigned char*)RSTRING_PTR(aad), aadlen, (unsigned char*)RSTRING_PTR(pt), ptlen) != 1) { ossl_raise(eHPKEError, "could not seal"); } @@ -162,15 +158,9 @@ ossl_hpke_decap(VALUE self, VALUE enc, VALUE priv, VALUE info) GetHpkeCtx(self, rctx); GetPKey(priv, pkey); // TODO: if priv was not a PKey then reject - // unsigned char ikme[32] = {0x02, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; - // OSSL_HPKE_CTX_set1_ikme(rctx, ikme, 32); - enclen = RSTRING_LEN(enc); infolen = RSTRING_LEN(info); - rb_p(enc); - rb_p(rb_sprintf("enclen: %ld\n", enclen)); - if (OSSL_HPKE_decap(rctx, (unsigned char *)RSTRING_PTR(enc), enclen, pkey, (unsigned char *)RSTRING_PTR(info), infolen) != 1) { ossl_raise(eHPKEError, "could not decap"); } @@ -253,9 +243,7 @@ ossl_hpke_keygen(VALUE self, VALUE kem_id, VALUE kdf_id, VALUE aead_id) }; publen = 256; - unsigned char ikm[32] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; - - if(!OSSL_HPKE_keygen(hpke_suite, pub, &publen, &pkey, ikm, 32, NULL, NULL)){ + if(!OSSL_HPKE_keygen(hpke_suite, pub, &publen, &pkey, NULL, 0, NULL, NULL)){ ossl_raise(eHPKEError, "could not keygen"); } From 012f241f44afe3c0cb67b595a62b91f5349c4109 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Thu, 7 Dec 2023 16:42:36 +0900 Subject: [PATCH 18/45] Set buffer size of OSSL_HPKE_keygen to 133 The current longest possible public key size is 133 bytes, according to RFC 9180 section 7.1 --- ext/openssl/ossl_hpke_ctx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index d337be973..6dd8aa93b 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -236,12 +236,12 @@ ossl_hpke_keygen(VALUE self, VALUE kem_id, VALUE kdf_id, VALUE aead_id) { EVP_PKEY *pkey; VALUE pkey_obj; - unsigned char pub[256]; + unsigned char pub[133]; // as per RFC9810 section 7.1, the maximum size of Npk possible is 133 size_t publen; OSSL_HPKE_SUITE hpke_suite = { NUM2INT(kem_id), NUM2INT(kdf_id), NUM2INT(aead_id) }; - publen = 256; + publen = 133; // set it to maximum length first, it will shrink down upon call of OSSL_HPKE_keygen if(!OSSL_HPKE_keygen(hpke_suite, pub, &publen, &pkey, NULL, 0, NULL, NULL)){ ossl_raise(eHPKEError, "could not keygen"); From 4a1a787cca6bb3c0644966cf085b3f2de877092f Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Thu, 7 Dec 2023 16:42:53 +0900 Subject: [PATCH 19/45] Remove debug method keygen_pub --- ext/openssl/ossl_hpke_ctx.c | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index 6dd8aa93b..f78b01fe9 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -252,26 +252,6 @@ ossl_hpke_keygen(VALUE self, VALUE kem_id, VALUE kdf_id, VALUE aead_id) return pkey_obj; } -VALUE -ossl_hpke_keygen_pub(VALUE self, VALUE kem_id, VALUE kdf_id, VALUE aead_id) -{ - EVP_PKEY *pkey; - VALUE pub_obj; - unsigned char pub[256]; - size_t publen; - OSSL_HPKE_SUITE hpke_suite = { - NUM2INT(kem_id), NUM2INT(kdf_id), NUM2INT(aead_id) - }; - - if(!OSSL_HPKE_keygen(hpke_suite, pub, &publen, &pkey, NULL, 0, NULL, NULL)){ - ossl_raise(eHPKEError, "could not keygen"); - } - - pub_obj = rb_str_new((char *)pub, publen); - - return pub_obj; -} - void Init_ossl_hpke_ctx(void) { @@ -280,7 +260,6 @@ Init_ossl_hpke_ctx(void) eHPKEError = rb_define_class_under(mHPKE, "HPKEError", eOSSLError); rb_define_module_function(mHPKE, "keygen", ossl_hpke_keygen, 3); - rb_define_module_function(mHPKE, "keygen_pub", ossl_hpke_keygen_pub, 3); rb_define_singleton_method(cContext, "new_sender", ossl_hpke_ctx_new_sender, 4); rb_define_singleton_method(cContext, "new_receiver", ossl_hpke_ctx_new_receiver, 4); From 1f5e54c36eef835f640cb70384475b80ce015e52 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Thu, 7 Dec 2023 16:50:47 +0900 Subject: [PATCH 20/45] Use of HPKE::Suite - 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 --- lib/openssl/hpke.rb | 66 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/lib/openssl/hpke.rb b/lib/openssl/hpke.rb index 5ff5e04f3..e0fb13852 100644 --- a/lib/openssl/hpke.rb +++ b/lib/openssl/hpke.rb @@ -1,7 +1,69 @@ module OpenSSL::HPKE + def self.keygen_with_suite(suite) + raise OpenSSL::HPKE::HPKEError, 'Invalid suite specified' unless suite.is_a?(OpenSSL::HPKE::Suite) + + keygen(suite.kem_id, suite.kdf_id, suite.aead_id) + end + class Context - def hoge - 'a' + # supports only base mode for now + MODES = { + base: 0x00 + }.freeze + + attr_reader :mode_id, :kem_id, :kdf_id, :aead_id + + def initialize(mode, role, suite) + raise OpenSSL::HPKE::HPKEError, 'Invalid mode specified' unless MODES[mode] + raise OpenSSL::HPKE::HPKEError, 'Invalid suite specified' unless suite.is_a?(OpenSSL::HPKE::Suite) + + @mode_id = MODES[mode] + @kem_id = suite.kem_id + @kdf_id = suite.kdf_id + @aead_id = suite.aead_id + + if role == :sender + Context.new_sender(@mode_id, @kem_id, @kdf_id, @aead_id) + elsif role == :receiver + Context.new_receiver(@mode_id, @kem_id, @kdf_id, @aead_id) + else + raise OpenSSL::HPKE::HPKEError, 'Invalid role specified' + end + end + end + + class Suite + attr_reader :kem_id, :kdf_id, :aead_id + + KEMS = { + dhkem_p256_hkdf_sha256: 0x0010, + dhkem_p384_hkdf_sha384: 0x0011, + dhkem_p521_hkdf_sha512: 0x0012, # yes this is not a typo of p512 + dhkem_x25519_hkdf_sha256: 0x0020, + dhkem_x448_hkdf_sha512: 0x0021 + }.freeze + + KDFS = { + hkdf_sha256: 0x0001, + hkdf_sha384: 0x0002, + hkdf_sha512: 0x0003 + }.freeze + + AEADS = { + aes_128_gcm: 0x0001, + aes_256_gcm: 0x0002, + chacha20poly1305: 0x0003, + export_only: 0xffff + }.freeze + + def initialize(kem_id, kdf_id, aead_id) + @kem_id = kem_id + @kdf_id = kdf_id + @aead_id = aead_id + end + + def self.new_with_names(kem_name, kdf_name, aead_name) + new(KEMS[kem_name], KDFS[kdf_name], AEADS[aead_name]) if KEMS[kem_name] && KDFS[kdf_name] && AEADS[aead_name] end end end From 097751342eeb7c1dc99e89fb373bce2140e38d5c Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Thu, 7 Dec 2023 17:09:28 +0900 Subject: [PATCH 21/45] Use OSSL_HPKE_get_ciphertext_size to determine ciphertext size In this patch I also moved the `attr_reader` definitions of kem/kdf/aead_ids into C code --- ext/openssl/ossl_hpke_ctx.c | 21 +++++++++++++++++++-- lib/openssl/hpke.rb | 6 +----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index f78b01fe9..ca2375d61 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -70,6 +70,10 @@ ossl_hpke_ctx_new_sender(VALUE self, VALUE mode_id, VALUE kem_id, VALUE kdf_id, obj = ossl_hpke_ctx_new(sctx); + rb_iv_set(obj, "@kem_id", kem_id); + rb_iv_set(obj, "@kdf_id", kdf_id); + rb_iv_set(obj, "@aead_id", aead_id); + return obj; } @@ -88,6 +92,10 @@ ossl_hpke_ctx_new_receiver(VALUE self, VALUE mode_id, VALUE kem_id, VALUE kdf_id obj = ossl_hpke_ctx_new(sctx); + rb_iv_set(obj, "@kem_id", kem_id); + rb_iv_set(obj, "@kdf_id", kdf_id); + rb_iv_set(obj, "@aead_id", aead_id); + return obj; } @@ -125,12 +133,16 @@ ossl_hpke_seal(VALUE self, VALUE aad, VALUE pt) { VALUE ct_obj; OSSL_HPKE_CTX *sctx; + OSSL_HPKE_SUITE suite = { + NUM2INT(rb_iv_get(self, "@kem_id")), + NUM2INT(rb_iv_get(self, "@kdf_id")), + NUM2INT(rb_iv_get(self, "@aead_id")) + }; size_t ctlen, aadlen, ptlen; aadlen = RSTRING_LEN(aad); ptlen = RSTRING_LEN(pt); - ctlen = ptlen + 16; // block size is known to be at maximum 16 characters so use that - // TODO: use OSSL_HPKE_get_ciphertext_size + ctlen = OSSL_HPKE_get_ciphertext_size(suite, ptlen); ct_obj = rb_str_new(0, ctlen); @@ -259,6 +271,11 @@ Init_ossl_hpke_ctx(void) cContext = rb_define_class_under(mHPKE, "Context", rb_cObject); eHPKEError = rb_define_class_under(mHPKE, "HPKEError", eOSSLError); + // attr_readers for suite values + rb_define_attr(cContext, "kem_id", 1, 0); + rb_define_attr(cContext, "kdf_id", 1, 0); + rb_define_attr(cContext, "aead_id", 1, 0); + rb_define_module_function(mHPKE, "keygen", ossl_hpke_keygen, 3); rb_define_singleton_method(cContext, "new_sender", ossl_hpke_ctx_new_sender, 4); diff --git a/lib/openssl/hpke.rb b/lib/openssl/hpke.rb index e0fb13852..516f8e51e 100644 --- a/lib/openssl/hpke.rb +++ b/lib/openssl/hpke.rb @@ -11,17 +11,13 @@ class Context base: 0x00 }.freeze - attr_reader :mode_id, :kem_id, :kdf_id, :aead_id + attr_reader :mode_id def initialize(mode, role, suite) raise OpenSSL::HPKE::HPKEError, 'Invalid mode specified' unless MODES[mode] raise OpenSSL::HPKE::HPKEError, 'Invalid suite specified' unless suite.is_a?(OpenSSL::HPKE::Suite) @mode_id = MODES[mode] - @kem_id = suite.kem_id - @kdf_id = suite.kdf_id - @aead_id = suite.aead_id - if role == :sender Context.new_sender(@mode_id, @kem_id, @kdf_id, @aead_id) elsif role == :receiver From efb922d557b87983667e6a1479502d5a1b92aa04 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Thu, 7 Dec 2023 17:15:38 +0900 Subject: [PATCH 22/45] Allocate memory based on OSSL_HPKE_get_public_encap_size I am very iffy about this. Is there a safer way to handle this allocation? --- ext/openssl/ossl_hpke_ctx.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index ca2375d61..30d060bf4 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -103,19 +103,29 @@ VALUE ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) { VALUE enc_obj; - unsigned char enc[1024]; + unsigned char *enc; size_t enclen; OSSL_HPKE_CTX *sctx; size_t publen; size_t infolen; + OSSL_HPKE_SUITE suite = { + NUM2INT(rb_iv_get(self, "@kem_id")), + NUM2INT(rb_iv_get(self, "@kdf_id")), + NUM2INT(rb_iv_get(self, "@aead_id")) + }; GetHpkeCtx(self, sctx); - enclen = sizeof(enc); + enclen = OSSL_HPKE_get_public_encap_size(suite); + if((enc = (unsigned char *)malloc(enclen * sizeof(unsigned char))) == NULL) { + ossl_raise(eHPKEError, "could not allocate memory for encapsulation"); + } + publen = RSTRING_LEN(pub); infolen = RSTRING_LEN(info); if (OSSL_HPKE_encap(sctx, enc, &enclen, (unsigned char*)RSTRING_PTR(pub), publen, (unsigned char*)RSTRING_PTR(info), infolen) != 1) { + free(enc); ossl_raise(eHPKEError, "could not encap"); } @@ -125,6 +135,7 @@ ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) enc_obj = rb_str_new((char *)enc, enclen); + free(enc); return enc_obj; } From e798bf2a0ae9d7968b3aa4e2518aef08be875e13 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Thu, 7 Dec 2023 17:22:49 +0900 Subject: [PATCH 23/45] Now that most of the things work, comment out the debug prints --- ext/openssl/ossl_hpke_ctx.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index 30d060bf4..f3a6105a4 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -129,9 +129,11 @@ ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) ossl_raise(eHPKEError, "could not encap"); } + /* rbdebug_print_hex(sctx->shared_secret, sctx->shared_secretlen); rbdebug_print_hex(sctx->nonce, sctx->noncelen); rbdebug_print_hex(sctx->key, sctx->keylen); + */ enc_obj = rb_str_new((char *)enc, enclen); @@ -159,9 +161,11 @@ ossl_hpke_seal(VALUE self, VALUE aad, VALUE pt) GetHpkeCtx(self, sctx); + /* rbdebug_print_hex(sctx->shared_secret, sctx->shared_secretlen); rbdebug_print_hex(sctx->nonce, sctx->noncelen); rbdebug_print_hex(sctx->key, sctx->keylen); + */ if (OSSL_HPKE_seal(sctx, (unsigned char *)RSTRING_PTR(ct_obj), &ctlen, (unsigned char*)RSTRING_PTR(aad), aadlen, (unsigned char*)RSTRING_PTR(pt), ptlen) != 1) { ossl_raise(eHPKEError, "could not seal"); @@ -188,9 +192,11 @@ ossl_hpke_decap(VALUE self, VALUE enc, VALUE priv, VALUE info) ossl_raise(eHPKEError, "could not decap"); } + /* rbdebug_print_hex(rctx->shared_secret, rctx->shared_secretlen); rbdebug_print_hex(rctx->nonce, rctx->noncelen); rbdebug_print_hex(rctx->key, rctx->keylen); + */ return Qtrue; } @@ -209,9 +215,12 @@ ossl_hpke_open(VALUE self, VALUE aad, VALUE ct) pt_obj = rb_str_new(0, ptlen); GetHpkeCtx(self, rctx); + + /* rbdebug_print_hex(rctx->shared_secret, rctx->shared_secretlen); rbdebug_print_hex(rctx->nonce, rctx->noncelen); rbdebug_print_hex(rctx->key, rctx->keylen); + */ if (OSSL_HPKE_open(rctx, (unsigned char *)RSTRING_PTR(pt_obj), &ptlen, (unsigned char*)RSTRING_PTR(aad), aadlen, (unsigned char*)RSTRING_PTR(ct), ctlen) != 1) { ossl_raise(eHPKEError, "could not open"); @@ -234,10 +243,12 @@ ossl_hpke_export(VALUE self, VALUE secretlen, VALUE label) secret_obj = rb_str_new(0, NUM2INT(secretlen)); GetHpkeCtx(self, ctx); + /* rbdebug_print_hex(ctx->shared_secret, ctx->shared_secretlen); rbdebug_print_hex(ctx->nonce, ctx->noncelen); rbdebug_print_hex(ctx->key, ctx->keylen); rbdebug_print_hex(ctx->exportersec, ctx->exporterseclen); + */ if (OSSL_HPKE_export(ctx, (unsigned char *)RSTRING_PTR(secret_obj), NUM2INT(secretlen), (unsigned char*)RSTRING_PTR(label), labellen) != 1) { ossl_raise(eHPKEError, "could not export"); From eb533a97d9d20010a00547ce92dcee1d7f2ba484 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 8 Dec 2023 21:18:08 +0900 Subject: [PATCH 24/45] [hpke] macro to remove code when OpenSSL is less than 3.2.0 --- ext/openssl/ossl_hpke_ctx.c | 40 +++++++++++++++++++++++++++++++++++++ ext/openssl/ossl_hpke_ctx.h | 4 ++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index f3a6105a4..54357f4b0 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -17,7 +17,11 @@ rbdebug_print_hex(const unsigned char *str, size_t len) static void ossl_hpke_ctx_free(void *ptr) { +#if !OSSL_OPENSSL_PREREQ(3, 2, 0) + ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); +#else OSSL_HPKE_CTX_free(ptr); +#endif } /* public */ @@ -43,6 +47,9 @@ hpke_ctx_new0(VALUE arg) VALUE ossl_hpke_ctx_new(OSSL_HPKE_CTX *ctx) { +#if !OSSL_OPENSSL_PREREQ(3, 2, 0) + ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); +#else VALUE obj; int status; @@ -53,11 +60,15 @@ ossl_hpke_ctx_new(OSSL_HPKE_CTX *ctx) } return obj; +#endif } VALUE ossl_hpke_ctx_new_sender(VALUE self, VALUE mode_id, VALUE kem_id, VALUE kdf_id, VALUE aead_id) { +#if !OSSL_OPENSSL_PREREQ(3, 2, 0) + ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); +#else OSSL_HPKE_CTX *sctx; VALUE obj; OSSL_HPKE_SUITE hpke_suite = { @@ -75,11 +86,15 @@ ossl_hpke_ctx_new_sender(VALUE self, VALUE mode_id, VALUE kem_id, VALUE kdf_id, rb_iv_set(obj, "@aead_id", aead_id); return obj; +#endif } VALUE ossl_hpke_ctx_new_receiver(VALUE self, VALUE mode_id, VALUE kem_id, VALUE kdf_id, VALUE aead_id) { +#if !OSSL_OPENSSL_PREREQ(3, 2, 0) + ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); +#else OSSL_HPKE_CTX *sctx; VALUE obj; OSSL_HPKE_SUITE hpke_suite = { @@ -97,11 +112,15 @@ ossl_hpke_ctx_new_receiver(VALUE self, VALUE mode_id, VALUE kem_id, VALUE kdf_id rb_iv_set(obj, "@aead_id", aead_id); return obj; +#endif } VALUE ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) { +#if !OSSL_OPENSSL_PREREQ(3, 2, 0) + ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); +#else VALUE enc_obj; unsigned char *enc; size_t enclen; @@ -139,11 +158,15 @@ ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) free(enc); return enc_obj; +#endif } VALUE ossl_hpke_seal(VALUE self, VALUE aad, VALUE pt) { +#if !OSSL_OPENSSL_PREREQ(3, 2, 0) + ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); +#else VALUE ct_obj; OSSL_HPKE_CTX *sctx; OSSL_HPKE_SUITE suite = { @@ -172,11 +195,15 @@ ossl_hpke_seal(VALUE self, VALUE aad, VALUE pt) } return ct_obj; +#endif } VALUE ossl_hpke_decap(VALUE self, VALUE enc, VALUE priv, VALUE info) { +#if !OSSL_OPENSSL_PREREQ(3, 2, 0) + ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); +#else OSSL_HPKE_CTX *rctx; EVP_PKEY *pkey; size_t enclen; @@ -199,11 +226,15 @@ ossl_hpke_decap(VALUE self, VALUE enc, VALUE priv, VALUE info) */ return Qtrue; +#endif } VALUE ossl_hpke_open(VALUE self, VALUE aad, VALUE ct) { +#if !OSSL_OPENSSL_PREREQ(3, 2, 0) + ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); +#else VALUE pt_obj; OSSL_HPKE_CTX *rctx; size_t ptlen, aadlen, ctlen; @@ -229,11 +260,15 @@ ossl_hpke_open(VALUE self, VALUE aad, VALUE ct) rb_str_resize(pt_obj, ptlen); return pt_obj; +#endif } VALUE ossl_hpke_export(VALUE self, VALUE secretlen, VALUE label) { +#if !OSSL_OPENSSL_PREREQ(3, 2, 0) + ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); +#else VALUE secret_obj; OSSL_HPKE_CTX *ctx; size_t labellen; @@ -255,6 +290,7 @@ ossl_hpke_export(VALUE self, VALUE secretlen, VALUE label) } return secret_obj; +#endif } /* private */ @@ -268,6 +304,9 @@ ossl_hpke_ctx_alloc(VALUE klass) VALUE ossl_hpke_keygen(VALUE self, VALUE kem_id, VALUE kdf_id, VALUE aead_id) { +#if !OSSL_OPENSSL_PREREQ(3, 2, 0) + ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); +#else EVP_PKEY *pkey; VALUE pkey_obj; unsigned char pub[133]; // as per RFC9810 section 7.1, the maximum size of Npk possible is 133 @@ -284,6 +323,7 @@ ossl_hpke_keygen(VALUE self, VALUE kem_id, VALUE kdf_id, VALUE aead_id) pkey_obj = ossl_pkey_new(pkey); return pkey_obj; +#endif } void diff --git a/ext/openssl/ossl_hpke_ctx.h b/ext/openssl/ossl_hpke_ctx.h index 020d89955..7f3108ab1 100644 --- a/ext/openssl/ossl_hpke_ctx.h +++ b/ext/openssl/ossl_hpke_ctx.h @@ -5,15 +5,15 @@ extern VALUE mHPKE; extern VALUE cContext; extern const rb_data_type_t ossl_hpke_ctx_type; +#if OSSL_OPENSSL_PREREQ(3, 2, 0) #define GetHpkeCtx(obj, ctx) do {\ TypedData_Get_Struct((obj), OSSL_HPKE_CTX, &ossl_hpke_ctx_type, (ctx)); \ if (!(ctx)) { \ rb_raise(rb_eRuntimeError, "OSSL_HPKE_CTX wasn't initialized!");\ } \ } while (0) +#endif void Init_ossl_hpke_ctx(void); -VALUE ossl_hpke_ctx_new(OSSL_HPKE_CTX *); - #endif \ No newline at end of file From b73af546759b62720ee90d772d569175c954abc4 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 8 Dec 2023 21:41:07 +0900 Subject: [PATCH 25/45] Fix the guards against older OpenSSL --- ext/openssl/ossl_hpke_ctx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index 54357f4b0..865593603 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -33,6 +33,7 @@ const rb_data_type_t ossl_hpke_ctx_type = { 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; +#if OSSL_OPENSSL_PREREQ(3, 2, 0) static VALUE hpke_ctx_new0(VALUE arg) { @@ -47,9 +48,6 @@ hpke_ctx_new0(VALUE arg) VALUE ossl_hpke_ctx_new(OSSL_HPKE_CTX *ctx) { -#if !OSSL_OPENSSL_PREREQ(3, 2, 0) - ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); -#else VALUE obj; int status; @@ -60,8 +58,8 @@ ossl_hpke_ctx_new(OSSL_HPKE_CTX *ctx) } return obj; -#endif } +#endif VALUE ossl_hpke_ctx_new_sender(VALUE self, VALUE mode_id, VALUE kem_id, VALUE kdf_id, VALUE aead_id) From f7c42781157f042bd726703565ce26d83b4e411f Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 8 Dec 2023 21:41:22 +0900 Subject: [PATCH 26/45] Fix initialize API --- lib/openssl/hpke.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/openssl/hpke.rb b/lib/openssl/hpke.rb index 516f8e51e..a74e4de9a 100644 --- a/lib/openssl/hpke.rb +++ b/lib/openssl/hpke.rb @@ -19,9 +19,9 @@ def initialize(mode, role, suite) @mode_id = MODES[mode] if role == :sender - Context.new_sender(@mode_id, @kem_id, @kdf_id, @aead_id) + Context.new_sender(@mode_id, suite.kem_id, suite.kdf_id, suite.aead_id) elsif role == :receiver - Context.new_receiver(@mode_id, @kem_id, @kdf_id, @aead_id) + Context.new_receiver(@mode_id, suite.kem_id, suite.kdf_id, suite.aead_id) else raise OpenSSL::HPKE::HPKEError, 'Invalid role specified' end From b181210fe80e829931f587c17377be35c9ce20ba Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Wed, 13 Dec 2023 17:06:17 +0900 Subject: [PATCH 27/45] Change class structure, fix initialize API 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 --- ext/openssl/ossl_hpke_ctx.c | 97 +++++++++++++++++++++++++------------ lib/openssl/hpke.rb | 16 +----- 2 files changed, 66 insertions(+), 47 deletions(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index 865593603..95b380e0b 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -1,7 +1,12 @@ #include "ossl.h" +#define SENDER_CONTEXT 0 +#define RECEIVER_CONTEXT 1 + VALUE mHPKE; VALUE cContext; +VALUE cSenderContext; +VALUE cReceiverContext; VALUE eHPKEError; void @@ -35,23 +40,39 @@ const rb_data_type_t ossl_hpke_ctx_type = { #if OSSL_OPENSSL_PREREQ(3, 2, 0) static VALUE -hpke_ctx_new0(VALUE arg) +hpke_ctx_new0_sender(VALUE arg) { OSSL_HPKE_CTX *ctx = (OSSL_HPKE_CTX *)arg; VALUE obj; - obj = rb_obj_alloc(cContext); + obj = rb_obj_alloc(cSenderContext); + RTYPEDDATA_DATA(obj) = ctx; + return obj; +} + +static VALUE +hpke_ctx_new0_receiver(VALUE arg) +{ + OSSL_HPKE_CTX *ctx = (OSSL_HPKE_CTX *)arg; + VALUE obj; + + obj = rb_obj_alloc(cReceiverContext); RTYPEDDATA_DATA(obj) = ctx; return obj; } VALUE -ossl_hpke_ctx_new(OSSL_HPKE_CTX *ctx) +ossl_hpke_ctx_new(OSSL_HPKE_CTX *ctx, int role) { VALUE obj; int status; - obj = rb_protect(hpke_ctx_new0, (VALUE)ctx, &status); + if (role == SENDER_CONTEXT) { + obj = rb_protect(hpke_ctx_new0_sender, (VALUE)ctx, &status); + } else { + obj = rb_protect(hpke_ctx_new0_receiver, (VALUE)ctx, &status); + } + if (status) { OSSL_HPKE_CTX_free(ctx); rb_jump_tag(status); @@ -62,54 +83,64 @@ ossl_hpke_ctx_new(OSSL_HPKE_CTX *ctx) #endif VALUE -ossl_hpke_ctx_new_sender(VALUE self, VALUE mode_id, VALUE kem_id, VALUE kdf_id, VALUE aead_id) +ossl_hpke_ctx_new_sender(VALUE self, VALUE mode, VALUE suite) { #if !OSSL_OPENSSL_PREREQ(3, 2, 0) ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); #else OSSL_HPKE_CTX *sctx; - VALUE obj; + VALUE kem_id, kdf_id, aead_id, mode_table, mode_id; + kem_id = rb_iv_get(suite, "@kem_id"); + kdf_id = rb_iv_get(suite, "@kdf_id"); + aead_id = rb_iv_get(suite, "@aead_id"); + + rb_iv_set(self, "@kem_id", kem_id); + rb_iv_set(self, "@kdf_id", kdf_id); + rb_iv_set(self, "@aead_id", aead_id); + OSSL_HPKE_SUITE hpke_suite = { NUM2INT(kem_id), NUM2INT(kdf_id), NUM2INT(aead_id) }; + mode_table = rb_const_get_at(cContext, rb_intern("MODES")); + mode_id = rb_funcall(mode_table, rb_intern("[]"), 1, mode); if((sctx = OSSL_HPKE_CTX_new(NUM2INT(mode_id), hpke_suite, OSSL_HPKE_ROLE_SENDER, NULL, NULL)) == NULL) { ossl_raise(eHPKEError, "could not create ctx"); } - obj = ossl_hpke_ctx_new(sctx); - - rb_iv_set(obj, "@kem_id", kem_id); - rb_iv_set(obj, "@kdf_id", kdf_id); - rb_iv_set(obj, "@aead_id", aead_id); - - return obj; + RTYPEDDATA_DATA(self) = sctx; + return self; #endif } VALUE -ossl_hpke_ctx_new_receiver(VALUE self, VALUE mode_id, VALUE kem_id, VALUE kdf_id, VALUE aead_id) +ossl_hpke_ctx_new_receiver(VALUE self, VALUE mode, VALUE suite) { #if !OSSL_OPENSSL_PREREQ(3, 2, 0) ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); #else - OSSL_HPKE_CTX *sctx; - VALUE obj; + OSSL_HPKE_CTX *rctx; + VALUE kem_id, kdf_id, aead_id, mode_table, mode_id; + kem_id = rb_iv_get(suite, "@kem_id"); + kdf_id = rb_iv_get(suite, "@kdf_id"); + aead_id = rb_iv_get(suite, "@aead_id"); + + rb_iv_set(self, "@kem_id", kem_id); + rb_iv_set(self, "@kdf_id", kdf_id); + rb_iv_set(self, "@aead_id", aead_id); + OSSL_HPKE_SUITE hpke_suite = { NUM2INT(kem_id), NUM2INT(kdf_id), NUM2INT(aead_id) }; + mode_table = rb_const_get_at(cContext, rb_intern("MODES")); + mode_id = rb_funcall(mode_table, rb_intern("[]"), 1, mode); - if((sctx = OSSL_HPKE_CTX_new(NUM2INT(mode_id), hpke_suite, OSSL_HPKE_ROLE_RECEIVER, NULL, NULL)) == NULL) { + if((rctx = OSSL_HPKE_CTX_new(NUM2INT(mode_id), hpke_suite, OSSL_HPKE_ROLE_RECEIVER, NULL, NULL)) == NULL) { ossl_raise(eHPKEError, "could not create ctx"); } - obj = ossl_hpke_ctx_new(sctx); - - rb_iv_set(obj, "@kem_id", kem_id); - rb_iv_set(obj, "@kdf_id", kdf_id); - rb_iv_set(obj, "@aead_id", aead_id); - - return obj; + RTYPEDDATA_DATA(self) = rctx; + return self; #endif } @@ -327,8 +358,10 @@ ossl_hpke_keygen(VALUE self, VALUE kem_id, VALUE kdf_id, VALUE aead_id) void Init_ossl_hpke_ctx(void) { - mHPKE = rb_define_module_under(mOSSL, "HPKE"); - cContext = rb_define_class_under(mHPKE, "Context", rb_cObject); + mHPKE = rb_define_module_under(mOSSL, "HPKE"); + cContext = rb_define_class_under(mHPKE, "Context", rb_cObject); + cSenderContext = rb_define_class_under(cContext, "Sender", cContext); + cReceiverContext = rb_define_class_under(cContext, "Receiver", cContext); eHPKEError = rb_define_class_under(mHPKE, "HPKEError", eOSSLError); // attr_readers for suite values @@ -338,13 +371,13 @@ Init_ossl_hpke_ctx(void) rb_define_module_function(mHPKE, "keygen", ossl_hpke_keygen, 3); - rb_define_singleton_method(cContext, "new_sender", ossl_hpke_ctx_new_sender, 4); - rb_define_singleton_method(cContext, "new_receiver", ossl_hpke_ctx_new_receiver, 4); - rb_define_method(cContext, "encap", ossl_hpke_encap, 2); - rb_define_method(cContext, "seal", ossl_hpke_seal, 2); + rb_define_method(cSenderContext, "initialize", ossl_hpke_ctx_new_sender, 2); + rb_define_method(cSenderContext, "encap", ossl_hpke_encap, 2); + rb_define_method(cSenderContext, "seal", ossl_hpke_seal, 2); - rb_define_method(cContext, "decap", ossl_hpke_decap, 3); - rb_define_method(cContext, "open", ossl_hpke_open, 2); + rb_define_method(cReceiverContext, "initialize", ossl_hpke_ctx_new_receiver, 2); + rb_define_method(cReceiverContext, "decap", ossl_hpke_decap, 3); + rb_define_method(cReceiverContext, "open", ossl_hpke_open, 2); rb_define_method(cContext, "export", ossl_hpke_export, 2); diff --git a/lib/openssl/hpke.rb b/lib/openssl/hpke.rb index a74e4de9a..2ee1b920f 100644 --- a/lib/openssl/hpke.rb +++ b/lib/openssl/hpke.rb @@ -11,21 +11,7 @@ class Context base: 0x00 }.freeze - attr_reader :mode_id - - def initialize(mode, role, suite) - raise OpenSSL::HPKE::HPKEError, 'Invalid mode specified' unless MODES[mode] - raise OpenSSL::HPKE::HPKEError, 'Invalid suite specified' unless suite.is_a?(OpenSSL::HPKE::Suite) - - @mode_id = MODES[mode] - if role == :sender - Context.new_sender(@mode_id, suite.kem_id, suite.kdf_id, suite.aead_id) - elsif role == :receiver - Context.new_receiver(@mode_id, suite.kem_id, suite.kdf_id, suite.aead_id) - else - raise OpenSSL::HPKE::HPKEError, 'Invalid role specified' - end - end + attr_reader :mode_id, :kem_id, :kdf_id, :aead_id end class Suite From 0212ddb733f978e103fd8cce07d301e60a591083 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 5 Jun 2026 09:45:03 +0900 Subject: [PATCH 28/45] Apply rename: ossl_pkey_new -> ossl_pkey_wrap --- ext/openssl/ossl_hpke_ctx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index 95b380e0b..4a0501fad 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -349,7 +349,7 @@ ossl_hpke_keygen(VALUE self, VALUE kem_id, VALUE kdf_id, VALUE aead_id) ossl_raise(eHPKEError, "could not keygen"); } - pkey_obj = ossl_pkey_new(pkey); + pkey_obj = ossl_pkey_wrap(pkey); return pkey_obj; #endif From 798b929e8eee07329ed63e8b726ebf81d7ccac5a Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 5 Jun 2026 09:47:51 +0900 Subject: [PATCH 29/45] Re-add include and init --- ext/openssl/ossl.c | 1 + ext/openssl/ossl.h | 1 + 2 files changed, 2 insertions(+) diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c index 5716e6f10..53807dab1 100644 --- a/ext/openssl/ossl.c +++ b/ext/openssl/ossl.c @@ -1148,6 +1148,7 @@ Init_openssl(void) Init_ossl_digest(); Init_ossl_engine(); Init_ossl_hmac(); + Init_ossl_hpke_ctx(); Init_ossl_kdf(); Init_ossl_ns_spki(); Init_ossl_ocsp(); diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h index b89c7fc86..b25199d6e 100644 --- a/ext/openssl/ossl.h +++ b/ext/openssl/ossl.h @@ -195,6 +195,7 @@ extern VALUE dOSSL; #include "ossl_digest.h" #include "ossl_engine.h" #include "ossl_hmac.h" +#include "ossl_hpke_ctx.h" #include "ossl_kdf.h" #include "ossl_ns_spki.h" #include "ossl_ocsp.h" From 9a7cb45778311a941f4b12c0b05aeb12e1161b4e Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 5 Jun 2026 09:54:00 +0900 Subject: [PATCH 30/45] Ensure String inputs are String with StringValue --- ext/openssl/ossl_hpke_ctx.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index 4a0501fad..6e65201de 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -169,6 +169,8 @@ ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) ossl_raise(eHPKEError, "could not allocate memory for encapsulation"); } + StringValue(pub); + StringValue(info); publen = RSTRING_LEN(pub); infolen = RSTRING_LEN(info); @@ -205,6 +207,8 @@ ossl_hpke_seal(VALUE self, VALUE aad, VALUE pt) }; size_t ctlen, aadlen, ptlen; + StringValue(aad); + StringValue(pt); aadlen = RSTRING_LEN(aad); ptlen = RSTRING_LEN(pt); ctlen = OSSL_HPKE_get_ciphertext_size(suite, ptlen); @@ -241,6 +245,8 @@ ossl_hpke_decap(VALUE self, VALUE enc, VALUE priv, VALUE info) GetHpkeCtx(self, rctx); GetPKey(priv, pkey); // TODO: if priv was not a PKey then reject + StringValue(enc); + StringValue(info); enclen = RSTRING_LEN(enc); infolen = RSTRING_LEN(info); @@ -268,6 +274,8 @@ ossl_hpke_open(VALUE self, VALUE aad, VALUE ct) OSSL_HPKE_CTX *rctx; size_t ptlen, aadlen, ctlen; + StringValue(aad); + StringValue(ct); aadlen = RSTRING_LEN(aad); ctlen = RSTRING_LEN(ct); ptlen = ctlen; @@ -302,6 +310,7 @@ ossl_hpke_export(VALUE self, VALUE secretlen, VALUE label) OSSL_HPKE_CTX *ctx; size_t labellen; + StringValue(label); labellen = RSTRING_LEN(label); secret_obj = rb_str_new(0, NUM2INT(secretlen)); From e823e6aa7b896c56e590bc76caa00d54fafba9e9 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 5 Jun 2026 09:58:18 +0900 Subject: [PATCH 31/45] Use rb_str_new instead of direct malloc/free --- ext/openssl/ossl_hpke_ctx.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index 6e65201de..8c6fc2f83 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -151,7 +151,6 @@ ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); #else VALUE enc_obj; - unsigned char *enc; size_t enclen; OSSL_HPKE_CTX *sctx; size_t publen; @@ -164,18 +163,15 @@ ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) GetHpkeCtx(self, sctx); - enclen = OSSL_HPKE_get_public_encap_size(suite); - if((enc = (unsigned char *)malloc(enclen * sizeof(unsigned char))) == NULL) { - ossl_raise(eHPKEError, "could not allocate memory for encapsulation"); - } - StringValue(pub); StringValue(info); publen = RSTRING_LEN(pub); infolen = RSTRING_LEN(info); - if (OSSL_HPKE_encap(sctx, enc, &enclen, (unsigned char*)RSTRING_PTR(pub), publen, (unsigned char*)RSTRING_PTR(info), infolen) != 1) { - free(enc); + enclen = OSSL_HPKE_get_public_encap_size(suite); + enc_obj = rb_str_new(0, enclen); + + if (OSSL_HPKE_encap(sctx, (unsigned char *)RSTRING_PTR(enc_obj), &enclen, (unsigned char*)RSTRING_PTR(pub), publen, (unsigned char*)RSTRING_PTR(info), infolen) != 1) { ossl_raise(eHPKEError, "could not encap"); } @@ -185,9 +181,7 @@ ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) rbdebug_print_hex(sctx->key, sctx->keylen); */ - enc_obj = rb_str_new((char *)enc, enclen); - - free(enc); + rb_str_resize(enc_obj, enclen); return enc_obj; #endif } From 45e9c6553cdbdca75557eb7c9dbaa7cf1405804f Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 5 Jun 2026 10:07:09 +0900 Subject: [PATCH 32/45] No-op instead of raising inside GC-free --- ext/openssl/ossl_hpke_ctx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index 8c6fc2f83..fdf0349b7 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -22,9 +22,7 @@ rbdebug_print_hex(const unsigned char *str, size_t len) static void ossl_hpke_ctx_free(void *ptr) { -#if !OSSL_OPENSSL_PREREQ(3, 2, 0) - ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); -#else +#if OSSL_OPENSSL_PREREQ(3, 2, 0) OSSL_HPKE_CTX_free(ptr); #endif } From c07515f1a31a2635d32a8d9dce0aa4efb43e4e94 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 5 Jun 2026 10:08:38 +0900 Subject: [PATCH 33/45] Guard against re-initialization --- ext/openssl/ossl_hpke_ctx.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index fdf0349b7..283ab485e 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -88,6 +88,10 @@ ossl_hpke_ctx_new_sender(VALUE self, VALUE mode, VALUE suite) #else OSSL_HPKE_CTX *sctx; VALUE kem_id, kdf_id, aead_id, mode_table, mode_id; + + if (RTYPEDDATA_DATA(self)) + ossl_raise(eHPKEError, "HPKE context is already initialized"); + kem_id = rb_iv_get(suite, "@kem_id"); kdf_id = rb_iv_get(suite, "@kdf_id"); aead_id = rb_iv_get(suite, "@aead_id"); @@ -119,6 +123,10 @@ ossl_hpke_ctx_new_receiver(VALUE self, VALUE mode, VALUE suite) #else OSSL_HPKE_CTX *rctx; VALUE kem_id, kdf_id, aead_id, mode_table, mode_id; + + if (RTYPEDDATA_DATA(self)) + ossl_raise(eHPKEError, "HPKE context is already initialized"); + kem_id = rb_iv_get(suite, "@kem_id"); kdf_id = rb_iv_get(suite, "@kdf_id"); aead_id = rb_iv_get(suite, "@aead_id"); From 49ec6402b6a4bf8cef100e983765b26980b5ef3f Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 5 Jun 2026 10:14:44 +0900 Subject: [PATCH 34/45] Styling fix - Remove debug functions - 2 space indentation -> 4 space indentation --- ext/openssl/ossl_hpke_ctx.c | 456 +++++++++++++++--------------------- ext/openssl/ossl_hpke_ctx.h | 2 +- 2 files changed, 185 insertions(+), 273 deletions(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index 283ab485e..9c52cecd2 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -1,117 +1,60 @@ #include "ossl.h" -#define SENDER_CONTEXT 0 -#define RECEIVER_CONTEXT 1 - VALUE mHPKE; VALUE cContext; VALUE cSenderContext; VALUE cReceiverContext; VALUE eHPKEError; -void -rbdebug_print_hex(const unsigned char *str, size_t len) -{ - VALUE rbstr; - - rbstr = rb_str_new((char *)str, len); - - rb_p(rb_funcall(rbstr, rb_intern("unpack1"), 1, rb_str_new_cstr("H*"))); -} - static void ossl_hpke_ctx_free(void *ptr) { #if OSSL_OPENSSL_PREREQ(3, 2, 0) - OSSL_HPKE_CTX_free(ptr); + OSSL_HPKE_CTX_free(ptr); #endif } /* public */ const rb_data_type_t ossl_hpke_ctx_type = { - "OpenSSL/HPKE_CTX", - { - 0, ossl_hpke_ctx_free, - }, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY + "OpenSSL/HPKE_CTX", + { + 0, ossl_hpke_ctx_free, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; -#if OSSL_OPENSSL_PREREQ(3, 2, 0) -static VALUE -hpke_ctx_new0_sender(VALUE arg) -{ - OSSL_HPKE_CTX *ctx = (OSSL_HPKE_CTX *)arg; - VALUE obj; - - obj = rb_obj_alloc(cSenderContext); - RTYPEDDATA_DATA(obj) = ctx; - return obj; -} - -static VALUE -hpke_ctx_new0_receiver(VALUE arg) -{ - OSSL_HPKE_CTX *ctx = (OSSL_HPKE_CTX *)arg; - VALUE obj; - - obj = rb_obj_alloc(cReceiverContext); - RTYPEDDATA_DATA(obj) = ctx; - return obj; -} - -VALUE -ossl_hpke_ctx_new(OSSL_HPKE_CTX *ctx, int role) -{ - VALUE obj; - int status; - - if (role == SENDER_CONTEXT) { - obj = rb_protect(hpke_ctx_new0_sender, (VALUE)ctx, &status); - } else { - obj = rb_protect(hpke_ctx_new0_receiver, (VALUE)ctx, &status); - } - - if (status) { - OSSL_HPKE_CTX_free(ctx); - rb_jump_tag(status); - } - - return obj; -} -#endif - VALUE ossl_hpke_ctx_new_sender(VALUE self, VALUE mode, VALUE suite) { #if !OSSL_OPENSSL_PREREQ(3, 2, 0) - ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); + ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); #else - OSSL_HPKE_CTX *sctx; - VALUE kem_id, kdf_id, aead_id, mode_table, mode_id; + OSSL_HPKE_CTX *sctx; + VALUE kem_id, kdf_id, aead_id, mode_table, mode_id; - if (RTYPEDDATA_DATA(self)) - ossl_raise(eHPKEError, "HPKE context is already initialized"); + if (RTYPEDDATA_DATA(self)) + ossl_raise(eHPKEError, "HPKE context is already initialized"); - kem_id = rb_iv_get(suite, "@kem_id"); - kdf_id = rb_iv_get(suite, "@kdf_id"); - aead_id = rb_iv_get(suite, "@aead_id"); + kem_id = rb_iv_get(suite, "@kem_id"); + kdf_id = rb_iv_get(suite, "@kdf_id"); + aead_id = rb_iv_get(suite, "@aead_id"); - rb_iv_set(self, "@kem_id", kem_id); - rb_iv_set(self, "@kdf_id", kdf_id); - rb_iv_set(self, "@aead_id", aead_id); + rb_iv_set(self, "@kem_id", kem_id); + rb_iv_set(self, "@kdf_id", kdf_id); + rb_iv_set(self, "@aead_id", aead_id); - OSSL_HPKE_SUITE hpke_suite = { - NUM2INT(kem_id), NUM2INT(kdf_id), NUM2INT(aead_id) - }; - mode_table = rb_const_get_at(cContext, rb_intern("MODES")); - mode_id = rb_funcall(mode_table, rb_intern("[]"), 1, mode); + OSSL_HPKE_SUITE hpke_suite = { + NUM2INT(kem_id), NUM2INT(kdf_id), NUM2INT(aead_id) + }; + mode_table = rb_const_get_at(cContext, rb_intern("MODES")); + mode_id = rb_funcall(mode_table, rb_intern("[]"), 1, mode); - if((sctx = OSSL_HPKE_CTX_new(NUM2INT(mode_id), hpke_suite, OSSL_HPKE_ROLE_SENDER, NULL, NULL)) == NULL) { - ossl_raise(eHPKEError, "could not create ctx"); - } + if((sctx = OSSL_HPKE_CTX_new(NUM2INT(mode_id), hpke_suite, OSSL_HPKE_ROLE_SENDER, NULL, NULL)) == NULL) { + ossl_raise(eHPKEError, "could not create ctx"); + } - RTYPEDDATA_DATA(self) = sctx; - return self; + RTYPEDDATA_DATA(self) = sctx; + return self; #endif } @@ -119,34 +62,34 @@ VALUE ossl_hpke_ctx_new_receiver(VALUE self, VALUE mode, VALUE suite) { #if !OSSL_OPENSSL_PREREQ(3, 2, 0) - ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); + ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); #else - OSSL_HPKE_CTX *rctx; - VALUE kem_id, kdf_id, aead_id, mode_table, mode_id; + OSSL_HPKE_CTX *rctx; + VALUE kem_id, kdf_id, aead_id, mode_table, mode_id; - if (RTYPEDDATA_DATA(self)) - ossl_raise(eHPKEError, "HPKE context is already initialized"); + if (RTYPEDDATA_DATA(self)) + ossl_raise(eHPKEError, "HPKE context is already initialized"); - kem_id = rb_iv_get(suite, "@kem_id"); - kdf_id = rb_iv_get(suite, "@kdf_id"); - aead_id = rb_iv_get(suite, "@aead_id"); + kem_id = rb_iv_get(suite, "@kem_id"); + kdf_id = rb_iv_get(suite, "@kdf_id"); + aead_id = rb_iv_get(suite, "@aead_id"); - rb_iv_set(self, "@kem_id", kem_id); - rb_iv_set(self, "@kdf_id", kdf_id); - rb_iv_set(self, "@aead_id", aead_id); + rb_iv_set(self, "@kem_id", kem_id); + rb_iv_set(self, "@kdf_id", kdf_id); + rb_iv_set(self, "@aead_id", aead_id); - OSSL_HPKE_SUITE hpke_suite = { - NUM2INT(kem_id), NUM2INT(kdf_id), NUM2INT(aead_id) - }; - mode_table = rb_const_get_at(cContext, rb_intern("MODES")); - mode_id = rb_funcall(mode_table, rb_intern("[]"), 1, mode); + OSSL_HPKE_SUITE hpke_suite = { + NUM2INT(kem_id), NUM2INT(kdf_id), NUM2INT(aead_id) + }; + mode_table = rb_const_get_at(cContext, rb_intern("MODES")); + mode_id = rb_funcall(mode_table, rb_intern("[]"), 1, mode); - if((rctx = OSSL_HPKE_CTX_new(NUM2INT(mode_id), hpke_suite, OSSL_HPKE_ROLE_RECEIVER, NULL, NULL)) == NULL) { - ossl_raise(eHPKEError, "could not create ctx"); - } + if((rctx = OSSL_HPKE_CTX_new(NUM2INT(mode_id), hpke_suite, OSSL_HPKE_ROLE_RECEIVER, NULL, NULL)) == NULL) { + ossl_raise(eHPKEError, "could not create ctx"); + } - RTYPEDDATA_DATA(self) = rctx; - return self; + RTYPEDDATA_DATA(self) = rctx; + return self; #endif } @@ -154,41 +97,35 @@ VALUE ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) { #if !OSSL_OPENSSL_PREREQ(3, 2, 0) - ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); + ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); #else - VALUE enc_obj; - size_t enclen; - OSSL_HPKE_CTX *sctx; - size_t publen; - size_t infolen; - OSSL_HPKE_SUITE suite = { - NUM2INT(rb_iv_get(self, "@kem_id")), - NUM2INT(rb_iv_get(self, "@kdf_id")), - NUM2INT(rb_iv_get(self, "@aead_id")) - }; - - GetHpkeCtx(self, sctx); - - StringValue(pub); - StringValue(info); - publen = RSTRING_LEN(pub); - infolen = RSTRING_LEN(info); - - enclen = OSSL_HPKE_get_public_encap_size(suite); - enc_obj = rb_str_new(0, enclen); - - if (OSSL_HPKE_encap(sctx, (unsigned char *)RSTRING_PTR(enc_obj), &enclen, (unsigned char*)RSTRING_PTR(pub), publen, (unsigned char*)RSTRING_PTR(info), infolen) != 1) { - ossl_raise(eHPKEError, "could not encap"); - } - - /* - rbdebug_print_hex(sctx->shared_secret, sctx->shared_secretlen); - rbdebug_print_hex(sctx->nonce, sctx->noncelen); - rbdebug_print_hex(sctx->key, sctx->keylen); - */ - - rb_str_resize(enc_obj, enclen); - return enc_obj; + VALUE enc_obj; + size_t enclen; + OSSL_HPKE_CTX *sctx; + size_t publen; + size_t infolen; + OSSL_HPKE_SUITE suite = { + NUM2INT(rb_iv_get(self, "@kem_id")), + NUM2INT(rb_iv_get(self, "@kdf_id")), + NUM2INT(rb_iv_get(self, "@aead_id")) + }; + + GetHpkeCtx(self, sctx); + + StringValue(pub); + StringValue(info); + publen = RSTRING_LEN(pub); + infolen = RSTRING_LEN(info); + + enclen = OSSL_HPKE_get_public_encap_size(suite); + enc_obj = rb_str_new(0, enclen); + + if (OSSL_HPKE_encap(sctx, (unsigned char *)RSTRING_PTR(enc_obj), &enclen, (unsigned char*)RSTRING_PTR(pub), publen, (unsigned char*)RSTRING_PTR(info), infolen) != 1) { + ossl_raise(eHPKEError, "could not encap"); + } + + rb_str_resize(enc_obj, enclen); + return enc_obj; #endif } @@ -196,38 +133,32 @@ VALUE ossl_hpke_seal(VALUE self, VALUE aad, VALUE pt) { #if !OSSL_OPENSSL_PREREQ(3, 2, 0) - ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); + ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); #else - VALUE ct_obj; - OSSL_HPKE_CTX *sctx; - OSSL_HPKE_SUITE suite = { - NUM2INT(rb_iv_get(self, "@kem_id")), - NUM2INT(rb_iv_get(self, "@kdf_id")), - NUM2INT(rb_iv_get(self, "@aead_id")) - }; - size_t ctlen, aadlen, ptlen; - - StringValue(aad); - StringValue(pt); - aadlen = RSTRING_LEN(aad); - ptlen = RSTRING_LEN(pt); - ctlen = OSSL_HPKE_get_ciphertext_size(suite, ptlen); - - ct_obj = rb_str_new(0, ctlen); - - GetHpkeCtx(self, sctx); - - /* - rbdebug_print_hex(sctx->shared_secret, sctx->shared_secretlen); - rbdebug_print_hex(sctx->nonce, sctx->noncelen); - rbdebug_print_hex(sctx->key, sctx->keylen); - */ - - if (OSSL_HPKE_seal(sctx, (unsigned char *)RSTRING_PTR(ct_obj), &ctlen, (unsigned char*)RSTRING_PTR(aad), aadlen, (unsigned char*)RSTRING_PTR(pt), ptlen) != 1) { - ossl_raise(eHPKEError, "could not seal"); - } - - return ct_obj; + VALUE ct_obj; + OSSL_HPKE_CTX *sctx; + OSSL_HPKE_SUITE suite = { + NUM2INT(rb_iv_get(self, "@kem_id")), + NUM2INT(rb_iv_get(self, "@kdf_id")), + NUM2INT(rb_iv_get(self, "@aead_id")) + }; + size_t ctlen, aadlen, ptlen; + + StringValue(aad); + StringValue(pt); + aadlen = RSTRING_LEN(aad); + ptlen = RSTRING_LEN(pt); + ctlen = OSSL_HPKE_get_ciphertext_size(suite, ptlen); + + ct_obj = rb_str_new(0, ctlen); + + GetHpkeCtx(self, sctx); + + if (OSSL_HPKE_seal(sctx, (unsigned char *)RSTRING_PTR(ct_obj), &ctlen, (unsigned char*)RSTRING_PTR(aad), aadlen, (unsigned char*)RSTRING_PTR(pt), ptlen) != 1) { + ossl_raise(eHPKEError, "could not seal"); + } + + return ct_obj; #endif } @@ -235,32 +166,26 @@ VALUE ossl_hpke_decap(VALUE self, VALUE enc, VALUE priv, VALUE info) { #if !OSSL_OPENSSL_PREREQ(3, 2, 0) - ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); + ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); #else - OSSL_HPKE_CTX *rctx; - EVP_PKEY *pkey; - size_t enclen; - size_t infolen; - - GetHpkeCtx(self, rctx); - GetPKey(priv, pkey); // TODO: if priv was not a PKey then reject - - StringValue(enc); - StringValue(info); - enclen = RSTRING_LEN(enc); - infolen = RSTRING_LEN(info); - - if (OSSL_HPKE_decap(rctx, (unsigned char *)RSTRING_PTR(enc), enclen, pkey, (unsigned char *)RSTRING_PTR(info), infolen) != 1) { - ossl_raise(eHPKEError, "could not decap"); - } - - /* - rbdebug_print_hex(rctx->shared_secret, rctx->shared_secretlen); - rbdebug_print_hex(rctx->nonce, rctx->noncelen); - rbdebug_print_hex(rctx->key, rctx->keylen); - */ - - return Qtrue; + OSSL_HPKE_CTX *rctx; + EVP_PKEY *pkey; + size_t enclen; + size_t infolen; + + GetHpkeCtx(self, rctx); + GetPKey(priv, pkey); // TODO: if priv was not a PKey then reject + + StringValue(enc); + StringValue(info); + enclen = RSTRING_LEN(enc); + infolen = RSTRING_LEN(info); + + if (OSSL_HPKE_decap(rctx, (unsigned char *)RSTRING_PTR(enc), enclen, pkey, (unsigned char *)RSTRING_PTR(info), infolen) != 1) { + ossl_raise(eHPKEError, "could not decap"); + } + + return Qtrue; #endif } @@ -268,35 +193,29 @@ VALUE ossl_hpke_open(VALUE self, VALUE aad, VALUE ct) { #if !OSSL_OPENSSL_PREREQ(3, 2, 0) - ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); + ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); #else - VALUE pt_obj; - OSSL_HPKE_CTX *rctx; - size_t ptlen, aadlen, ctlen; + VALUE pt_obj; + OSSL_HPKE_CTX *rctx; + size_t ptlen, aadlen, ctlen; - StringValue(aad); - StringValue(ct); - aadlen = RSTRING_LEN(aad); - ctlen = RSTRING_LEN(ct); - ptlen = ctlen; + StringValue(aad); + StringValue(ct); + aadlen = RSTRING_LEN(aad); + ctlen = RSTRING_LEN(ct); + ptlen = ctlen; - pt_obj = rb_str_new(0, ptlen); + pt_obj = rb_str_new(0, ptlen); - GetHpkeCtx(self, rctx); + GetHpkeCtx(self, rctx); - /* - rbdebug_print_hex(rctx->shared_secret, rctx->shared_secretlen); - rbdebug_print_hex(rctx->nonce, rctx->noncelen); - rbdebug_print_hex(rctx->key, rctx->keylen); - */ + if (OSSL_HPKE_open(rctx, (unsigned char *)RSTRING_PTR(pt_obj), &ptlen, (unsigned char*)RSTRING_PTR(aad), aadlen, (unsigned char*)RSTRING_PTR(ct), ctlen) != 1) { + ossl_raise(eHPKEError, "could not open"); + } - if (OSSL_HPKE_open(rctx, (unsigned char *)RSTRING_PTR(pt_obj), &ptlen, (unsigned char*)RSTRING_PTR(aad), aadlen, (unsigned char*)RSTRING_PTR(ct), ctlen) != 1) { - ossl_raise(eHPKEError, "could not open"); - } + rb_str_resize(pt_obj, ptlen); - rb_str_resize(pt_obj, ptlen); - - return pt_obj; + return pt_obj; #endif } @@ -304,30 +223,23 @@ VALUE ossl_hpke_export(VALUE self, VALUE secretlen, VALUE label) { #if !OSSL_OPENSSL_PREREQ(3, 2, 0) - ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); + ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); #else - VALUE secret_obj; - OSSL_HPKE_CTX *ctx; - size_t labellen; - - StringValue(label); - labellen = RSTRING_LEN(label); + VALUE secret_obj; + OSSL_HPKE_CTX *ctx; + size_t labellen; - secret_obj = rb_str_new(0, NUM2INT(secretlen)); + StringValue(label); + labellen = RSTRING_LEN(label); - GetHpkeCtx(self, ctx); - /* - rbdebug_print_hex(ctx->shared_secret, ctx->shared_secretlen); - rbdebug_print_hex(ctx->nonce, ctx->noncelen); - rbdebug_print_hex(ctx->key, ctx->keylen); - rbdebug_print_hex(ctx->exportersec, ctx->exporterseclen); - */ + secret_obj = rb_str_new(0, NUM2INT(secretlen)); - if (OSSL_HPKE_export(ctx, (unsigned char *)RSTRING_PTR(secret_obj), NUM2INT(secretlen), (unsigned char*)RSTRING_PTR(label), labellen) != 1) { - ossl_raise(eHPKEError, "could not export"); - } + GetHpkeCtx(self, ctx); + if (OSSL_HPKE_export(ctx, (unsigned char *)RSTRING_PTR(secret_obj), NUM2INT(secretlen), (unsigned char*)RSTRING_PTR(label), labellen) != 1) { + ossl_raise(eHPKEError, "could not export"); + } - return secret_obj; + return secret_obj; #endif } @@ -335,7 +247,7 @@ ossl_hpke_export(VALUE self, VALUE secretlen, VALUE label) static VALUE ossl_hpke_ctx_alloc(VALUE klass) { - return TypedData_Wrap_Struct(klass, &ossl_hpke_ctx_type, NULL); + return TypedData_Wrap_Struct(klass, &ossl_hpke_ctx_type, NULL); } /* HPKE module method */ @@ -343,52 +255,52 @@ VALUE ossl_hpke_keygen(VALUE self, VALUE kem_id, VALUE kdf_id, VALUE aead_id) { #if !OSSL_OPENSSL_PREREQ(3, 2, 0) - ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); + ossl_raise(eHPKEError, "OpenSSL 3.2.0 required"); #else - EVP_PKEY *pkey; - VALUE pkey_obj; - unsigned char pub[133]; // as per RFC9810 section 7.1, the maximum size of Npk possible is 133 - size_t publen; - OSSL_HPKE_SUITE hpke_suite = { - NUM2INT(kem_id), NUM2INT(kdf_id), NUM2INT(aead_id) - }; - publen = 133; // set it to maximum length first, it will shrink down upon call of OSSL_HPKE_keygen - - if(!OSSL_HPKE_keygen(hpke_suite, pub, &publen, &pkey, NULL, 0, NULL, NULL)){ - ossl_raise(eHPKEError, "could not keygen"); - } - - pkey_obj = ossl_pkey_wrap(pkey); - - return pkey_obj; + EVP_PKEY *pkey; + VALUE pkey_obj; + unsigned char pub[133]; // as per RFC9810 section 7.1, the maximum size of Npk possible is 133 + size_t publen; + OSSL_HPKE_SUITE hpke_suite = { + NUM2INT(kem_id), NUM2INT(kdf_id), NUM2INT(aead_id) + }; + publen = 133; // set it to maximum length first, it will shrink down upon call of OSSL_HPKE_keygen + + if(!OSSL_HPKE_keygen(hpke_suite, pub, &publen, &pkey, NULL, 0, NULL, NULL)){ + ossl_raise(eHPKEError, "could not keygen"); + } + + pkey_obj = ossl_pkey_wrap(pkey); + + return pkey_obj; #endif } void Init_ossl_hpke_ctx(void) { - mHPKE = rb_define_module_under(mOSSL, "HPKE"); - cContext = rb_define_class_under(mHPKE, "Context", rb_cObject); - cSenderContext = rb_define_class_under(cContext, "Sender", cContext); - cReceiverContext = rb_define_class_under(cContext, "Receiver", cContext); - eHPKEError = rb_define_class_under(mHPKE, "HPKEError", eOSSLError); + mHPKE = rb_define_module_under(mOSSL, "HPKE"); + cContext = rb_define_class_under(mHPKE, "Context", rb_cObject); + cSenderContext = rb_define_class_under(cContext, "Sender", cContext); + cReceiverContext = rb_define_class_under(cContext, "Receiver", cContext); + eHPKEError = rb_define_class_under(mHPKE, "HPKEError", eOSSLError); - // attr_readers for suite values - rb_define_attr(cContext, "kem_id", 1, 0); - rb_define_attr(cContext, "kdf_id", 1, 0); - rb_define_attr(cContext, "aead_id", 1, 0); + // attr_readers for suite values + rb_define_attr(cContext, "kem_id", 1, 0); + rb_define_attr(cContext, "kdf_id", 1, 0); + rb_define_attr(cContext, "aead_id", 1, 0); - rb_define_module_function(mHPKE, "keygen", ossl_hpke_keygen, 3); + rb_define_module_function(mHPKE, "keygen", ossl_hpke_keygen, 3); - rb_define_method(cSenderContext, "initialize", ossl_hpke_ctx_new_sender, 2); - rb_define_method(cSenderContext, "encap", ossl_hpke_encap, 2); - rb_define_method(cSenderContext, "seal", ossl_hpke_seal, 2); + rb_define_method(cSenderContext, "initialize", ossl_hpke_ctx_new_sender, 2); + rb_define_method(cSenderContext, "encap", ossl_hpke_encap, 2); + rb_define_method(cSenderContext, "seal", ossl_hpke_seal, 2); - rb_define_method(cReceiverContext, "initialize", ossl_hpke_ctx_new_receiver, 2); - rb_define_method(cReceiverContext, "decap", ossl_hpke_decap, 3); - rb_define_method(cReceiverContext, "open", ossl_hpke_open, 2); + rb_define_method(cReceiverContext, "initialize", ossl_hpke_ctx_new_receiver, 2); + rb_define_method(cReceiverContext, "decap", ossl_hpke_decap, 3); + rb_define_method(cReceiverContext, "open", ossl_hpke_open, 2); - rb_define_method(cContext, "export", ossl_hpke_export, 2); + rb_define_method(cContext, "export", ossl_hpke_export, 2); - rb_define_alloc_func(cContext, ossl_hpke_ctx_alloc); + rb_define_alloc_func(cContext, ossl_hpke_ctx_alloc); } \ No newline at end of file diff --git a/ext/openssl/ossl_hpke_ctx.h b/ext/openssl/ossl_hpke_ctx.h index 7f3108ab1..5614dd620 100644 --- a/ext/openssl/ossl_hpke_ctx.h +++ b/ext/openssl/ossl_hpke_ctx.h @@ -9,7 +9,7 @@ extern const rb_data_type_t ossl_hpke_ctx_type; #define GetHpkeCtx(obj, ctx) do {\ TypedData_Get_Struct((obj), OSSL_HPKE_CTX, &ossl_hpke_ctx_type, (ctx)); \ if (!(ctx)) { \ - rb_raise(rb_eRuntimeError, "OSSL_HPKE_CTX wasn't initialized!");\ + rb_raise(rb_eRuntimeError, "OSSL_HPKE_CTX wasn't initialized!");\ } \ } while (0) #endif From cc6686aaf65b4cec3b1d61b3ab78ea380ed177f0 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 5 Jun 2026 10:20:36 +0900 Subject: [PATCH 35/45] Add static to HPKE-internal functions --- ext/openssl/ossl_hpke_ctx.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index 9c52cecd2..ef052a44d 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -23,7 +23,7 @@ const rb_data_type_t ossl_hpke_ctx_type = { 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; -VALUE +static VALUE ossl_hpke_ctx_new_sender(VALUE self, VALUE mode, VALUE suite) { #if !OSSL_OPENSSL_PREREQ(3, 2, 0) @@ -58,7 +58,7 @@ ossl_hpke_ctx_new_sender(VALUE self, VALUE mode, VALUE suite) #endif } -VALUE +static VALUE ossl_hpke_ctx_new_receiver(VALUE self, VALUE mode, VALUE suite) { #if !OSSL_OPENSSL_PREREQ(3, 2, 0) @@ -93,7 +93,7 @@ ossl_hpke_ctx_new_receiver(VALUE self, VALUE mode, VALUE suite) #endif } -VALUE +static VALUE ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) { #if !OSSL_OPENSSL_PREREQ(3, 2, 0) @@ -129,7 +129,7 @@ ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) #endif } -VALUE +static VALUE ossl_hpke_seal(VALUE self, VALUE aad, VALUE pt) { #if !OSSL_OPENSSL_PREREQ(3, 2, 0) @@ -162,7 +162,7 @@ ossl_hpke_seal(VALUE self, VALUE aad, VALUE pt) #endif } -VALUE +static VALUE ossl_hpke_decap(VALUE self, VALUE enc, VALUE priv, VALUE info) { #if !OSSL_OPENSSL_PREREQ(3, 2, 0) @@ -189,7 +189,7 @@ ossl_hpke_decap(VALUE self, VALUE enc, VALUE priv, VALUE info) #endif } -VALUE +static VALUE ossl_hpke_open(VALUE self, VALUE aad, VALUE ct) { #if !OSSL_OPENSSL_PREREQ(3, 2, 0) @@ -219,7 +219,7 @@ ossl_hpke_open(VALUE self, VALUE aad, VALUE ct) #endif } -VALUE +static VALUE ossl_hpke_export(VALUE self, VALUE secretlen, VALUE label) { #if !OSSL_OPENSSL_PREREQ(3, 2, 0) @@ -251,7 +251,7 @@ ossl_hpke_ctx_alloc(VALUE klass) } /* HPKE module method */ -VALUE +static VALUE ossl_hpke_keygen(VALUE self, VALUE kem_id, VALUE kdf_id, VALUE aead_id) { #if !OSSL_OPENSSL_PREREQ(3, 2, 0) From e15b4d748bcb7092e148b2ded5e9a41825addac8 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 5 Jun 2026 10:24:57 +0900 Subject: [PATCH 36/45] Add license header --- ext/openssl/ossl_hpke_ctx.c | 4 ++++ ext/openssl/ossl_hpke_ctx.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index ef052a44d..eb9ed65d1 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -1,3 +1,7 @@ +/* + * Ruby/OpenSSL Project + * Copyright (C) 2026 Ruby/OpenSSL Project Authors + */ #include "ossl.h" VALUE mHPKE; diff --git a/ext/openssl/ossl_hpke_ctx.h b/ext/openssl/ossl_hpke_ctx.h index 5614dd620..ddfeeffce 100644 --- a/ext/openssl/ossl_hpke_ctx.h +++ b/ext/openssl/ossl_hpke_ctx.h @@ -1,3 +1,7 @@ +/* + * Ruby/OpenSSL Project + * Copyright (C) 2026 Ruby/OpenSSL Project Authors + */ #if !defined(OSSL_HPKE_CTX_H) #define OSSL_HPKE_CTX_H From 9d9e4539be7a4576d90d06bc74add343cd164968 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 5 Jun 2026 10:25:22 +0900 Subject: [PATCH 37/45] Remove todo comment --- ext/openssl/ossl_hpke_ctx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index eb9ed65d1..41eecb4ed 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -178,7 +178,7 @@ ossl_hpke_decap(VALUE self, VALUE enc, VALUE priv, VALUE info) size_t infolen; GetHpkeCtx(self, rctx); - GetPKey(priv, pkey); // TODO: if priv was not a PKey then reject + GetPKey(priv, pkey); StringValue(enc); StringValue(info); From a447ff3c535f80f63411d02bb9ea069074c24c2b Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 5 Jun 2026 10:34:15 +0900 Subject: [PATCH 38/45] Add test code --- test/openssl/test_hpke.rb | 191 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 test/openssl/test_hpke.rb diff --git a/test/openssl/test_hpke.rb b/test/openssl/test_hpke.rb new file mode 100644 index 000000000..b0c1894b2 --- /dev/null +++ b/test/openssl/test_hpke.rb @@ -0,0 +1,191 @@ +# frozen_string_literal: true +require_relative 'utils' + +if defined?(OpenSSL) + +class OpenSSL::TestHPKE < OpenSSL::TestCase + def setup + super + # The HPKE API was added in OpenSSL 3.2.0. LibreSSL and AWS-LC do not + # provide it, and openssl? returns false for those. + unless openssl?(3, 2, 0) + omit "HPKE is only supported on OpenSSL >= 3.2.0" + end + end + + def test_suite_new_with_names + suite = OpenSSL::HPKE::Suite.new_with_names( + :dhkem_x25519_hkdf_sha256, :hkdf_sha256, :aes_128_gcm) + assert_equal(0x0020, suite.kem_id) + assert_equal(0x0001, suite.kdf_id) + assert_equal(0x0001, suite.aead_id) + end + + def test_suite_new_with_names_unknown_returns_nil + assert_nil(OpenSSL::HPKE::Suite.new_with_names(:bogus, :hkdf_sha256, :aes_128_gcm)) + assert_nil(OpenSSL::HPKE::Suite.new_with_names(:dhkem_x25519_hkdf_sha256, :bogus, :aes_128_gcm)) + assert_nil(OpenSSL::HPKE::Suite.new_with_names(:dhkem_x25519_hkdf_sha256, :hkdf_sha256, :bogus)) + end + + def test_suite_new_with_ids + suite = OpenSSL::HPKE::Suite.new(0x0020, 0x0001, 0x0001) + assert_equal(0x0020, suite.kem_id) + assert_equal(0x0001, suite.kdf_id) + assert_equal(0x0001, suite.aead_id) + end + + def test_keygen_returns_pkey + pkey = OpenSSL::HPKE.keygen_with_suite(x25519_suite) + assert_kind_of(OpenSSL::PKey::PKey, pkey) + end + + def test_keygen_for_all_kems + OpenSSL::HPKE::Suite::KEMS.each_key do |kem| + suite = OpenSSL::HPKE::Suite.new_with_names(kem, :hkdf_sha256, :aes_128_gcm) + assert_kind_of(OpenSSL::PKey::PKey, + OpenSSL::HPKE.keygen_with_suite(suite), + "keygen failed for KEM #{kem}") + end + end + + def test_keygen_with_suite_rejects_non_suite + assert_raise(OpenSSL::HPKE::HPKEError) do + OpenSSL::HPKE.keygen_with_suite("not a suite") + end + end + + def test_base_mode_roundtrip_x25519 + assert_hpke_roundtrip(x25519_suite) + end + + def test_base_mode_roundtrip_x448 + assert_hpke_roundtrip(OpenSSL::HPKE::Suite.new_with_names( + :dhkem_x448_hkdf_sha512, :hkdf_sha512, :aes_256_gcm)) + end + + def test_base_mode_roundtrip_p256 + assert_hpke_roundtrip(OpenSSL::HPKE::Suite.new_with_names( + :dhkem_p256_hkdf_sha256, :hkdf_sha256, :aes_128_gcm)) + end + + def test_base_mode_roundtrip_chacha20poly1305 + assert_hpke_roundtrip(OpenSSL::HPKE::Suite.new_with_names( + :dhkem_x25519_hkdf_sha256, :hkdf_sha256, :chacha20poly1305)) + end + + def test_seal_open_multiple_messages_in_order + suite = x25519_suite + sender, receiver = paired_contexts(suite) + messages = ["first", "second", "third"] + ciphertexts = messages.map { |m| sender.seal("aad", m) } + opened = ciphertexts.map { |c| receiver.open("aad", c) } + assert_equal(messages, opened) + end + + def test_open_fails_with_wrong_aad + suite = x25519_suite + sender, receiver = paired_contexts(suite) + ct = sender.seal("correct aad", "secret") + assert_raise(OpenSSL::HPKE::HPKEError) do + receiver.open("wrong aad", ct) + end + end + + def test_open_fails_on_tampered_ciphertext + suite = x25519_suite + sender, receiver = paired_contexts(suite) + ct = sender.seal("aad", "secret message") + tampered = ct.dup + tampered.setbyte(0, tampered.getbyte(0) ^ 0xff) + assert_raise(OpenSSL::HPKE::HPKEError) do + receiver.open("aad", tampered) + end + end + + def test_export_secret_agreement + suite = x25519_suite + sender, receiver = paired_contexts(suite) + sender_secret = sender.export(32, "context label") + receiver_secret = receiver.export(32, "context label") + assert_equal(32, sender_secret.bytesize) + assert_equal(sender_secret, receiver_secret) + end + + def test_export_different_labels_differ + suite = x25519_suite + sender, = paired_contexts(suite) + assert_not_equal(sender.export(32, "label a"), sender.export(32, "label b")) + end + + def test_export_only_suite + suite = OpenSSL::HPKE::Suite.new_with_names( + :dhkem_x25519_hkdf_sha256, :hkdf_sha256, :export_only) + sender, receiver = paired_contexts(suite) + assert_equal(sender.export(32, "label"), receiver.export(32, "label")) + # The export-only AEAD cannot seal or open. + assert_raise(OpenSSL::HPKE::HPKEError) { sender.seal("aad", "msg") } + end + + def test_context_cannot_be_reinitialized + suite = x25519_suite + sender = OpenSSL::HPKE::Context::Sender.new(:base, suite) + assert_raise(OpenSSL::HPKE::HPKEError) do + sender.send(:initialize, :base, suite) + end + + receiver = OpenSSL::HPKE::Context::Receiver.new(:base, suite) + assert_raise(OpenSSL::HPKE::HPKEError) do + receiver.send(:initialize, :base, suite) + end + end + + def test_string_arguments_are_required + suite = x25519_suite + pkey = OpenSSL::HPKE.keygen_with_suite(suite) + sender = OpenSSL::HPKE::Context::Sender.new(:base, suite) + assert_raise(TypeError) { sender.encap(12345, "info") } + assert_raise(TypeError) { sender.encap(public_key_bytes(pkey), 12345) } + end + + private + + def x25519_suite + OpenSSL::HPKE::Suite.new_with_names( + :dhkem_x25519_hkdf_sha256, :hkdf_sha256, :aes_128_gcm) + end + + # The KEM public key passed to #encap is the recipient's public key in the + # KEM's wire encoding: the raw key for X25519/X448, the uncompressed point + # for the NIST curves. + def public_key_bytes(pkey) + if pkey.is_a?(OpenSSL::PKey::EC) + pkey.public_key.to_octet_string(:uncompressed) + else + pkey.raw_public_key + end + end + + # Returns an established [sender, receiver] pair sharing the same context. + def paired_contexts(suite, info: "shared info") + pkey = OpenSSL::HPKE.keygen_with_suite(suite) + sender = OpenSSL::HPKE::Context::Sender.new(:base, suite) + enc = sender.encap(public_key_bytes(pkey), info) + receiver = OpenSSL::HPKE::Context::Receiver.new(:base, suite) + assert_equal(true, receiver.decap(enc, pkey, info)) + [sender, receiver] + end + + def assert_hpke_roundtrip(suite, info: "some info", aad: "some aad", message: "hello hpke") + pkey = OpenSSL::HPKE.keygen_with_suite(suite) + + sender = OpenSSL::HPKE::Context::Sender.new(:base, suite) + enc = sender.encap(public_key_bytes(pkey), info) + ct = sender.seal(aad, message) + + receiver = OpenSSL::HPKE::Context::Receiver.new(:base, suite) + assert_equal(true, receiver.decap(enc, pkey, info)) + assert_equal(message, receiver.open(aad, ct)) + end +end + +end From fb5d2706da8489ce9393294414df0b98379e5be4 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 5 Jun 2026 10:57:29 +0900 Subject: [PATCH 39/45] Remove conflicting attr_reader --- ext/openssl/ossl_hpke_ctx.c | 5 ----- lib/openssl/hpke.rb | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index 41eecb4ed..f00b320bf 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -289,11 +289,6 @@ Init_ossl_hpke_ctx(void) cReceiverContext = rb_define_class_under(cContext, "Receiver", cContext); eHPKEError = rb_define_class_under(mHPKE, "HPKEError", eOSSLError); - // attr_readers for suite values - rb_define_attr(cContext, "kem_id", 1, 0); - rb_define_attr(cContext, "kdf_id", 1, 0); - rb_define_attr(cContext, "aead_id", 1, 0); - rb_define_module_function(mHPKE, "keygen", ossl_hpke_keygen, 3); rb_define_method(cSenderContext, "initialize", ossl_hpke_ctx_new_sender, 2); diff --git a/lib/openssl/hpke.rb b/lib/openssl/hpke.rb index 2ee1b920f..a6cfd7b6f 100644 --- a/lib/openssl/hpke.rb +++ b/lib/openssl/hpke.rb @@ -11,7 +11,7 @@ class Context base: 0x00 }.freeze - attr_reader :mode_id, :kem_id, :kdf_id, :aead_id + attr_reader :kem_id, :kdf_id, :aead_id end class Suite From 9ba8033d3e834e6aa3c61dc1b66b9936a903833f Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 5 Jun 2026 11:23:27 +0900 Subject: [PATCH 40/45] Add propq "fips=yes" when FIPS is enabled --- ext/openssl/ossl_hpke_ctx.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index f00b320bf..a732b1476 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -53,7 +53,9 @@ ossl_hpke_ctx_new_sender(VALUE self, VALUE mode, VALUE suite) mode_table = rb_const_get_at(cContext, rb_intern("MODES")); mode_id = rb_funcall(mode_table, rb_intern("[]"), 1, mode); - if((sctx = OSSL_HPKE_CTX_new(NUM2INT(mode_id), hpke_suite, OSSL_HPKE_ROLE_SENDER, NULL, NULL)) == NULL) { + const char *propq = EVP_default_properties_is_fips_enabled(NULL) ? "fips=yes" : NULL; + + if((sctx = OSSL_HPKE_CTX_new(NUM2INT(mode_id), hpke_suite, OSSL_HPKE_ROLE_SENDER, NULL, propq)) == NULL) { ossl_raise(eHPKEError, "could not create ctx"); } @@ -88,7 +90,9 @@ ossl_hpke_ctx_new_receiver(VALUE self, VALUE mode, VALUE suite) mode_table = rb_const_get_at(cContext, rb_intern("MODES")); mode_id = rb_funcall(mode_table, rb_intern("[]"), 1, mode); - if((rctx = OSSL_HPKE_CTX_new(NUM2INT(mode_id), hpke_suite, OSSL_HPKE_ROLE_RECEIVER, NULL, NULL)) == NULL) { + const char *propq = EVP_default_properties_is_fips_enabled(NULL) ? "fips=yes" : NULL; + + if((rctx = OSSL_HPKE_CTX_new(NUM2INT(mode_id), hpke_suite, OSSL_HPKE_ROLE_RECEIVER, NULL, propq)) == NULL) { ossl_raise(eHPKEError, "could not create ctx"); } @@ -270,7 +274,9 @@ ossl_hpke_keygen(VALUE self, VALUE kem_id, VALUE kdf_id, VALUE aead_id) }; publen = 133; // set it to maximum length first, it will shrink down upon call of OSSL_HPKE_keygen - if(!OSSL_HPKE_keygen(hpke_suite, pub, &publen, &pkey, NULL, 0, NULL, NULL)){ + const char *propq = EVP_default_properties_is_fips_enabled(NULL) ? "fips=yes" : NULL; + + if(!OSSL_HPKE_keygen(hpke_suite, pub, &publen, &pkey, NULL, 0, NULL, propq)){ ossl_raise(eHPKEError, "could not keygen"); } From c60f0cf7c61faf874cc913b35a754ca2b1288e59 Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 5 Jun 2026 11:27:52 +0900 Subject: [PATCH 41/45] Change behavior of tests based on FIPS-compatibility --- test/openssl/test_hpke.rb | 47 ++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/test/openssl/test_hpke.rb b/test/openssl/test_hpke.rb index b0c1894b2..28f7a8417 100644 --- a/test/openssl/test_hpke.rb +++ b/test/openssl/test_hpke.rb @@ -35,11 +35,13 @@ def test_suite_new_with_ids end def test_keygen_returns_pkey - pkey = OpenSSL::HPKE.keygen_with_suite(x25519_suite) + pkey = OpenSSL::HPKE.keygen_with_suite(fips_compatible_suite) assert_kind_of(OpenSSL::PKey::PKey, pkey) end def test_keygen_for_all_kems + # X25519 and X448 are not FIPS-approved. + omit_on_fips OpenSSL::HPKE::Suite::KEMS.each_key do |kem| suite = OpenSSL::HPKE::Suite.new_with_names(kem, :hkdf_sha256, :aes_128_gcm) assert_kind_of(OpenSSL::PKey::PKey, @@ -54,28 +56,31 @@ def test_keygen_with_suite_rejects_non_suite end end - def test_base_mode_roundtrip_x25519 - assert_hpke_roundtrip(x25519_suite) + def test_base_mode_roundtrip_p256 + assert_hpke_roundtrip(OpenSSL::HPKE::Suite.new_with_names( + :dhkem_p256_hkdf_sha256, :hkdf_sha256, :aes_128_gcm)) end - def test_base_mode_roundtrip_x448 + def test_base_mode_roundtrip_x25519 + omit_on_fips # X25519 is not FIPS-approved assert_hpke_roundtrip(OpenSSL::HPKE::Suite.new_with_names( - :dhkem_x448_hkdf_sha512, :hkdf_sha512, :aes_256_gcm)) + :dhkem_x25519_hkdf_sha256, :hkdf_sha256, :aes_128_gcm)) end - def test_base_mode_roundtrip_p256 + def test_base_mode_roundtrip_x448 + omit_on_fips # X448 is not FIPS-approved assert_hpke_roundtrip(OpenSSL::HPKE::Suite.new_with_names( - :dhkem_p256_hkdf_sha256, :hkdf_sha256, :aes_128_gcm)) + :dhkem_x448_hkdf_sha512, :hkdf_sha512, :aes_256_gcm)) end def test_base_mode_roundtrip_chacha20poly1305 + omit_on_fips # ChaCha20-Poly1305 is not FIPS-approved assert_hpke_roundtrip(OpenSSL::HPKE::Suite.new_with_names( :dhkem_x25519_hkdf_sha256, :hkdf_sha256, :chacha20poly1305)) end def test_seal_open_multiple_messages_in_order - suite = x25519_suite - sender, receiver = paired_contexts(suite) + sender, receiver = paired_contexts(fips_compatible_suite) messages = ["first", "second", "third"] ciphertexts = messages.map { |m| sender.seal("aad", m) } opened = ciphertexts.map { |c| receiver.open("aad", c) } @@ -83,8 +88,7 @@ def test_seal_open_multiple_messages_in_order end def test_open_fails_with_wrong_aad - suite = x25519_suite - sender, receiver = paired_contexts(suite) + sender, receiver = paired_contexts(fips_compatible_suite) ct = sender.seal("correct aad", "secret") assert_raise(OpenSSL::HPKE::HPKEError) do receiver.open("wrong aad", ct) @@ -92,8 +96,7 @@ def test_open_fails_with_wrong_aad end def test_open_fails_on_tampered_ciphertext - suite = x25519_suite - sender, receiver = paired_contexts(suite) + sender, receiver = paired_contexts(fips_compatible_suite) ct = sender.seal("aad", "secret message") tampered = ct.dup tampered.setbyte(0, tampered.getbyte(0) ^ 0xff) @@ -103,8 +106,7 @@ def test_open_fails_on_tampered_ciphertext end def test_export_secret_agreement - suite = x25519_suite - sender, receiver = paired_contexts(suite) + sender, receiver = paired_contexts(fips_compatible_suite) sender_secret = sender.export(32, "context label") receiver_secret = receiver.export(32, "context label") assert_equal(32, sender_secret.bytesize) @@ -112,12 +114,13 @@ def test_export_secret_agreement end def test_export_different_labels_differ - suite = x25519_suite - sender, = paired_contexts(suite) + sender, = paired_contexts(fips_compatible_suite) assert_not_equal(sender.export(32, "label a"), sender.export(32, "label b")) end def test_export_only_suite + # The export-only suite here uses X25519, which is not FIPS-approved. + omit_on_fips suite = OpenSSL::HPKE::Suite.new_with_names( :dhkem_x25519_hkdf_sha256, :hkdf_sha256, :export_only) sender, receiver = paired_contexts(suite) @@ -127,7 +130,7 @@ def test_export_only_suite end def test_context_cannot_be_reinitialized - suite = x25519_suite + suite = fips_compatible_suite sender = OpenSSL::HPKE::Context::Sender.new(:base, suite) assert_raise(OpenSSL::HPKE::HPKEError) do sender.send(:initialize, :base, suite) @@ -140,7 +143,7 @@ def test_context_cannot_be_reinitialized end def test_string_arguments_are_required - suite = x25519_suite + suite = fips_compatible_suite pkey = OpenSSL::HPKE.keygen_with_suite(suite) sender = OpenSSL::HPKE::Context::Sender.new(:base, suite) assert_raise(TypeError) { sender.encap(12345, "info") } @@ -149,9 +152,11 @@ def test_string_arguments_are_required private - def x25519_suite + # DHKEM(P-256, HKDF-SHA256), HKDF-SHA256 and AES-128-GCM are all + # FIPS-approved, so this suite works both with and without the FIPS provider. + def fips_compatible_suite OpenSSL::HPKE::Suite.new_with_names( - :dhkem_x25519_hkdf_sha256, :hkdf_sha256, :aes_128_gcm) + :dhkem_p256_hkdf_sha256, :hkdf_sha256, :aes_128_gcm) end # The KEM public key passed to #encap is the recipient's public key in the From bd4cde328e1da95d204c6b216184677978a631ef Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 5 Jun 2026 12:11:19 +0900 Subject: [PATCH 42/45] Omit HPKE altogether under FIPS mode --- ext/openssl/ossl_hpke_ctx.c | 4 ++++ test/openssl/test_hpke.rb | 46 ++++++++++++++++--------------------- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index a732b1476..0627faae7 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -129,6 +129,8 @@ ossl_hpke_encap(VALUE self, VALUE pub, VALUE info) enc_obj = rb_str_new(0, enclen); if (OSSL_HPKE_encap(sctx, (unsigned char *)RSTRING_PTR(enc_obj), &enclen, (unsigned char*)RSTRING_PTR(pub), publen, (unsigned char*)RSTRING_PTR(info), infolen) != 1) { + if (EVP_default_properties_is_fips_enabled(NULL)) + ossl_raise(eHPKEError, "could not encap; HPKE is not supported by the FIPS provider"); ossl_raise(eHPKEError, "could not encap"); } @@ -190,6 +192,8 @@ ossl_hpke_decap(VALUE self, VALUE enc, VALUE priv, VALUE info) infolen = RSTRING_LEN(info); if (OSSL_HPKE_decap(rctx, (unsigned char *)RSTRING_PTR(enc), enclen, pkey, (unsigned char *)RSTRING_PTR(info), infolen) != 1) { + if (EVP_default_properties_is_fips_enabled(NULL)) + ossl_raise(eHPKEError, "could not decap; HPKE is not supported by the FIPS provider"); ossl_raise(eHPKEError, "could not decap"); } diff --git a/test/openssl/test_hpke.rb b/test/openssl/test_hpke.rb index 28f7a8417..f11ec3350 100644 --- a/test/openssl/test_hpke.rb +++ b/test/openssl/test_hpke.rb @@ -6,6 +6,10 @@ class OpenSSL::TestHPKE < OpenSSL::TestCase def setup super + # OpenSSL's FIPS provider does not implement the DHKEM KEM encapsulation + # used by HPKE, so no HPKE operation can complete a round-trip under FIPS. + # The whole feature is therefore omitted in FIPS mode. + omit_on_fips # The HPKE API was added in OpenSSL 3.2.0. LibreSSL and AWS-LC do not # provide it, and openssl? returns false for those. unless openssl?(3, 2, 0) @@ -35,13 +39,11 @@ def test_suite_new_with_ids end def test_keygen_returns_pkey - pkey = OpenSSL::HPKE.keygen_with_suite(fips_compatible_suite) + pkey = OpenSSL::HPKE.keygen_with_suite(x25519_suite) assert_kind_of(OpenSSL::PKey::PKey, pkey) end def test_keygen_for_all_kems - # X25519 and X448 are not FIPS-approved. - omit_on_fips OpenSSL::HPKE::Suite::KEMS.each_key do |kem| suite = OpenSSL::HPKE::Suite.new_with_names(kem, :hkdf_sha256, :aes_128_gcm) assert_kind_of(OpenSSL::PKey::PKey, @@ -56,31 +58,27 @@ def test_keygen_with_suite_rejects_non_suite end end - def test_base_mode_roundtrip_p256 - assert_hpke_roundtrip(OpenSSL::HPKE::Suite.new_with_names( - :dhkem_p256_hkdf_sha256, :hkdf_sha256, :aes_128_gcm)) - end - def test_base_mode_roundtrip_x25519 - omit_on_fips # X25519 is not FIPS-approved - assert_hpke_roundtrip(OpenSSL::HPKE::Suite.new_with_names( - :dhkem_x25519_hkdf_sha256, :hkdf_sha256, :aes_128_gcm)) + assert_hpke_roundtrip(x25519_suite) end def test_base_mode_roundtrip_x448 - omit_on_fips # X448 is not FIPS-approved assert_hpke_roundtrip(OpenSSL::HPKE::Suite.new_with_names( :dhkem_x448_hkdf_sha512, :hkdf_sha512, :aes_256_gcm)) end + def test_base_mode_roundtrip_p256 + assert_hpke_roundtrip(OpenSSL::HPKE::Suite.new_with_names( + :dhkem_p256_hkdf_sha256, :hkdf_sha256, :aes_128_gcm)) + end + def test_base_mode_roundtrip_chacha20poly1305 - omit_on_fips # ChaCha20-Poly1305 is not FIPS-approved assert_hpke_roundtrip(OpenSSL::HPKE::Suite.new_with_names( :dhkem_x25519_hkdf_sha256, :hkdf_sha256, :chacha20poly1305)) end def test_seal_open_multiple_messages_in_order - sender, receiver = paired_contexts(fips_compatible_suite) + sender, receiver = paired_contexts(x25519_suite) messages = ["first", "second", "third"] ciphertexts = messages.map { |m| sender.seal("aad", m) } opened = ciphertexts.map { |c| receiver.open("aad", c) } @@ -88,7 +86,7 @@ def test_seal_open_multiple_messages_in_order end def test_open_fails_with_wrong_aad - sender, receiver = paired_contexts(fips_compatible_suite) + sender, receiver = paired_contexts(x25519_suite) ct = sender.seal("correct aad", "secret") assert_raise(OpenSSL::HPKE::HPKEError) do receiver.open("wrong aad", ct) @@ -96,7 +94,7 @@ def test_open_fails_with_wrong_aad end def test_open_fails_on_tampered_ciphertext - sender, receiver = paired_contexts(fips_compatible_suite) + sender, receiver = paired_contexts(x25519_suite) ct = sender.seal("aad", "secret message") tampered = ct.dup tampered.setbyte(0, tampered.getbyte(0) ^ 0xff) @@ -106,7 +104,7 @@ def test_open_fails_on_tampered_ciphertext end def test_export_secret_agreement - sender, receiver = paired_contexts(fips_compatible_suite) + sender, receiver = paired_contexts(x25519_suite) sender_secret = sender.export(32, "context label") receiver_secret = receiver.export(32, "context label") assert_equal(32, sender_secret.bytesize) @@ -114,13 +112,11 @@ def test_export_secret_agreement end def test_export_different_labels_differ - sender, = paired_contexts(fips_compatible_suite) + sender, = paired_contexts(x25519_suite) assert_not_equal(sender.export(32, "label a"), sender.export(32, "label b")) end def test_export_only_suite - # The export-only suite here uses X25519, which is not FIPS-approved. - omit_on_fips suite = OpenSSL::HPKE::Suite.new_with_names( :dhkem_x25519_hkdf_sha256, :hkdf_sha256, :export_only) sender, receiver = paired_contexts(suite) @@ -130,7 +126,7 @@ def test_export_only_suite end def test_context_cannot_be_reinitialized - suite = fips_compatible_suite + suite = x25519_suite sender = OpenSSL::HPKE::Context::Sender.new(:base, suite) assert_raise(OpenSSL::HPKE::HPKEError) do sender.send(:initialize, :base, suite) @@ -143,7 +139,7 @@ def test_context_cannot_be_reinitialized end def test_string_arguments_are_required - suite = fips_compatible_suite + suite = x25519_suite pkey = OpenSSL::HPKE.keygen_with_suite(suite) sender = OpenSSL::HPKE::Context::Sender.new(:base, suite) assert_raise(TypeError) { sender.encap(12345, "info") } @@ -152,11 +148,9 @@ def test_string_arguments_are_required private - # DHKEM(P-256, HKDF-SHA256), HKDF-SHA256 and AES-128-GCM are all - # FIPS-approved, so this suite works both with and without the FIPS provider. - def fips_compatible_suite + def x25519_suite OpenSSL::HPKE::Suite.new_with_names( - :dhkem_p256_hkdf_sha256, :hkdf_sha256, :aes_128_gcm) + :dhkem_x25519_hkdf_sha256, :hkdf_sha256, :aes_128_gcm) end # The KEM public key passed to #encap is the recipient's public key in the From 9ec86d7329019dcbec3b987c6c64b0c0b150f27e Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 5 Jun 2026 16:00:24 +0900 Subject: [PATCH 43/45] Reorder statements in extconf.rb --- ext/openssl/extconf.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index a862a61a4..a1cea0b53 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -166,6 +166,7 @@ def find_openssl_library # added in 3.2.0 have_func("SSL_get0_group_name(NULL)", ssl_h) +have_header("openssl/hpke.h") # added in 3.4.0 have_func("TS_VERIFY_CTX_set0_certs(NULL, NULL)", ts_h) @@ -176,9 +177,6 @@ def find_openssl_library # added in 4.0.0 have_func("ASN1_BIT_STRING_set1(NULL, NULL, 0, 0)", "openssl/asn1.h") -# added in 3.2.0 -have_header("openssl/hpke.h") - Logging::message "=== Checking done. ===\n" # Append flags from environment variables. From 2f8cf4bdb23432e3de10ea492824552fa5b536ff Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 5 Jun 2026 19:31:51 +0900 Subject: [PATCH 44/45] Remove comment It's obvious for people who would edit this code... --- lib/openssl/hpke.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/openssl/hpke.rb b/lib/openssl/hpke.rb index a6cfd7b6f..cfcee79db 100644 --- a/lib/openssl/hpke.rb +++ b/lib/openssl/hpke.rb @@ -20,7 +20,7 @@ class Suite KEMS = { dhkem_p256_hkdf_sha256: 0x0010, dhkem_p384_hkdf_sha384: 0x0011, - dhkem_p521_hkdf_sha512: 0x0012, # yes this is not a typo of p512 + dhkem_p521_hkdf_sha512: 0x0012, dhkem_x25519_hkdf_sha256: 0x0020, dhkem_x448_hkdf_sha512: 0x0021 }.freeze From 161d7d794fdf7bc709f34e0c9e3b77459694fd1b Mon Sep 17 00:00:00 2001 From: Ryo Kajiwara Date: Fri, 5 Jun 2026 19:33:39 +0900 Subject: [PATCH 45/45] Fix comment --- ext/openssl/ossl_hpke_ctx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/openssl/ossl_hpke_ctx.c b/ext/openssl/ossl_hpke_ctx.c index 0627faae7..85c331507 100644 --- a/ext/openssl/ossl_hpke_ctx.c +++ b/ext/openssl/ossl_hpke_ctx.c @@ -271,7 +271,7 @@ ossl_hpke_keygen(VALUE self, VALUE kem_id, VALUE kdf_id, VALUE aead_id) #else EVP_PKEY *pkey; VALUE pkey_obj; - unsigned char pub[133]; // as per RFC9810 section 7.1, the maximum size of Npk possible is 133 + unsigned char pub[133]; // as per RFC9180 section 7.1, the maximum size of Npk possible is 133 size_t publen; OSSL_HPKE_SUITE hpke_suite = { NUM2INT(kem_id), NUM2INT(kdf_id), NUM2INT(aead_id)