pq_crypto 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,7 @@
5
5
  #include <string.h>
6
6
 
7
7
  #include <openssl/crypto.h>
8
+ #include <openssl/evp.h>
8
9
 
9
10
  #include "pqcrypto_secure.h"
10
11
 
@@ -12,6 +13,8 @@
12
13
  #define RB_NOGVL_OFFLOAD_SAFE 0
13
14
  #endif
14
15
 
16
+ #define PQ_MU_ABSORB_NOGVL_MIN_BYTES 16384
17
+
15
18
  typedef struct {
16
19
  int result;
17
20
  uint8_t *public_key;
@@ -36,6 +39,25 @@ typedef struct {
36
39
  const uint8_t *secret_key;
37
40
  } kem_decapsulate_call_t;
38
41
 
42
+ typedef struct {
43
+ int result;
44
+ uint8_t *expanded_secret_key;
45
+ const uint8_t *secret_key;
46
+ } hybrid_expand_call_t;
47
+
48
+ typedef struct {
49
+ int result;
50
+ uint8_t *shared_secret;
51
+ const uint8_t *ciphertext;
52
+ const uint8_t *expanded_secret_key;
53
+ void *x25519_private_pkey;
54
+ } hybrid_decapsulate_expanded_pkey_call_t;
55
+
56
+ typedef struct {
57
+ uint8_t expanded_secret_key[PQ_HYBRID_EXPANDED_SECRETKEYBYTES];
58
+ EVP_PKEY *x25519_private_pkey;
59
+ } hybrid_expanded_key_wrapper_t;
60
+
39
61
  typedef struct {
40
62
  int result;
41
63
  uint8_t *public_key;
@@ -68,6 +90,49 @@ static VALUE mPQCrypto;
68
90
  static VALUE ePQCryptoError;
69
91
  static VALUE ePQCryptoVerificationError;
70
92
 
93
+ static void hybrid_expanded_key_wrapper_free(void *ptr) {
94
+ hybrid_expanded_key_wrapper_t *wrapper = (hybrid_expanded_key_wrapper_t *)ptr;
95
+ if (!wrapper) {
96
+ return;
97
+ }
98
+ pq_secure_wipe(wrapper->expanded_secret_key, sizeof(wrapper->expanded_secret_key));
99
+ if (wrapper->x25519_private_pkey) {
100
+ EVP_PKEY_free(wrapper->x25519_private_pkey);
101
+ wrapper->x25519_private_pkey = NULL;
102
+ }
103
+ xfree(wrapper);
104
+ }
105
+
106
+ static size_t hybrid_expanded_key_wrapper_size(const void *ptr) {
107
+ (void)ptr;
108
+ return sizeof(hybrid_expanded_key_wrapper_t);
109
+ }
110
+
111
+ static const rb_data_type_t hybrid_expanded_key_data_type = {
112
+ .wrap_struct_name = "PQCrypto::HybridKEM::ExpandedSecretKey",
113
+ .function =
114
+ {
115
+ .dmark = NULL,
116
+ .dfree = hybrid_expanded_key_wrapper_free,
117
+ .dsize = hybrid_expanded_key_wrapper_size,
118
+ .dcompact = NULL,
119
+ .reserved = {NULL},
120
+ },
121
+ .parent = NULL,
122
+ .data = NULL,
123
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
124
+ };
125
+
126
+ static hybrid_expanded_key_wrapper_t *hybrid_expanded_key_unwrap(VALUE obj) {
127
+ hybrid_expanded_key_wrapper_t *wrapper;
128
+ TypedData_Get_Struct(obj, hybrid_expanded_key_wrapper_t, &hybrid_expanded_key_data_type,
129
+ wrapper);
130
+ if (!wrapper || !wrapper->x25519_private_pkey) {
131
+ rb_raise(ePQCryptoError, "hybrid expanded secret key used after release");
132
+ }
133
+ return wrapper;
134
+ }
135
+
71
136
  __attribute__((noreturn)) static void pq_raise_general_error(int err);
72
137
 
73
138
  static const char *const PQC_CONTAINER_ALGORITHMS[] = {
@@ -76,18 +141,35 @@ static const char *const PQC_CONTAINER_ALGORITHMS[] = {
76
141
  "ml_dsa_65",
77
142
  };
78
143
 
144
+ static ID pqc_container_algorithm_ids[sizeof(PQC_CONTAINER_ALGORITHMS) /
145
+ sizeof(PQC_CONTAINER_ALGORITHMS[0])];
146
+
147
+ static void pq_init_algorithm_ids(void) {
148
+ for (size_t i = 0; i < sizeof(PQC_CONTAINER_ALGORITHMS) / sizeof(PQC_CONTAINER_ALGORITHMS[0]);
149
+ ++i) {
150
+ pqc_container_algorithm_ids[i] = rb_intern(PQC_CONTAINER_ALGORITHMS[i]);
151
+ }
152
+ }
153
+
79
154
  static const char *pq_algorithm_symbol_to_cstr(VALUE algorithm) {
80
- ID id;
81
155
  if (SYMBOL_P(algorithm)) {
82
- id = SYM2ID(algorithm);
156
+ ID id = SYM2ID(algorithm);
157
+ for (size_t i = 0; i < sizeof(PQC_CONTAINER_ALGORITHMS) / sizeof(PQC_CONTAINER_ALGORITHMS[0]);
158
+ ++i) {
159
+ if (id == pqc_container_algorithm_ids[i]) {
160
+ return PQC_CONTAINER_ALGORITHMS[i];
161
+ }
162
+ }
83
163
  } else {
84
164
  VALUE str = StringValue(algorithm);
85
- id = rb_intern_str(str);
86
- }
87
- for (size_t i = 0; i < sizeof(PQC_CONTAINER_ALGORITHMS) / sizeof(PQC_CONTAINER_ALGORITHMS[0]);
88
- ++i) {
89
- if (id == rb_intern(PQC_CONTAINER_ALGORITHMS[i])) {
90
- return PQC_CONTAINER_ALGORITHMS[i];
165
+ const char *ptr = RSTRING_PTR(str);
166
+ size_t len = (size_t)RSTRING_LEN(str);
167
+ for (size_t i = 0; i < sizeof(PQC_CONTAINER_ALGORITHMS) / sizeof(PQC_CONTAINER_ALGORITHMS[0]);
168
+ ++i) {
169
+ size_t algorithm_len = strlen(PQC_CONTAINER_ALGORITHMS[i]);
170
+ if (len == algorithm_len && memcmp(ptr, PQC_CONTAINER_ALGORITHMS[i], len) == 0) {
171
+ return PQC_CONTAINER_ALGORITHMS[i];
172
+ }
91
173
  }
92
174
  }
93
175
  rb_raise(rb_eArgError, "Unsupported serialization algorithm");
@@ -97,7 +179,7 @@ static VALUE pq_algorithm_cstr_to_symbol(const char *algorithm) {
97
179
  for (size_t i = 0; i < sizeof(PQC_CONTAINER_ALGORITHMS) / sizeof(PQC_CONTAINER_ALGORITHMS[0]);
98
180
  ++i) {
99
181
  if (strcmp(algorithm, PQC_CONTAINER_ALGORITHMS[i]) == 0) {
100
- return ID2SYM(rb_intern(PQC_CONTAINER_ALGORITHMS[i]));
182
+ return ID2SYM(pqc_container_algorithm_ids[i]);
101
183
  }
102
184
  }
103
185
  rb_raise(rb_eArgError, "Unsupported serialization algorithm");
@@ -175,6 +257,12 @@ static void *pq_hybrid_kem_encapsulate_nogvl(void *arg) {
175
257
  return NULL;
176
258
  }
177
259
 
260
+ static void *pq_hybrid_kem_expand_secret_key_nogvl(void *arg) {
261
+ hybrid_expand_call_t *call = (hybrid_expand_call_t *)arg;
262
+ call->result = pq_hybrid_kem_expand_secret_key(call->expanded_secret_key, call->secret_key);
263
+ return NULL;
264
+ }
265
+
178
266
  static void *pq_hybrid_kem_decapsulate_nogvl(void *arg) {
179
267
  kem_decapsulate_call_t *call = (kem_decapsulate_call_t *)arg;
180
268
  call->result =
@@ -182,6 +270,21 @@ static void *pq_hybrid_kem_decapsulate_nogvl(void *arg) {
182
270
  return NULL;
183
271
  }
184
272
 
273
+ static void *pq_hybrid_kem_decapsulate_expanded_nogvl(void *arg) {
274
+ kem_decapsulate_call_t *call = (kem_decapsulate_call_t *)arg;
275
+ call->result = pq_hybrid_kem_decapsulate_expanded(call->shared_secret, call->ciphertext,
276
+ call->secret_key);
277
+ return NULL;
278
+ }
279
+
280
+ static void *pq_hybrid_kem_decapsulate_expanded_pkey_nogvl(void *arg) {
281
+ hybrid_decapsulate_expanded_pkey_call_t *call =
282
+ (hybrid_decapsulate_expanded_pkey_call_t *)arg;
283
+ call->result = pq_hybrid_kem_decapsulate_expanded_pkey(
284
+ call->shared_secret, call->ciphertext, call->expanded_secret_key, call->x25519_private_pkey);
285
+ return NULL;
286
+ }
287
+
185
288
  #define PQ_DEFINE_SIGN_KEYPAIR_NOGVL(rb_name, c_call) \
186
289
  static void *pq_##rb_name##_nogvl(void *arg) { \
187
290
  sign_keypair_call_t *call = (sign_keypair_call_t *)arg; \
@@ -640,6 +743,66 @@ static VALUE pqcrypto_hybrid_kem_encapsulate(VALUE self, VALUE public_key) {
640
743
  PQ_HYBRID_SHAREDSECRETBYTES);
641
744
  }
642
745
 
746
+ static VALUE pqcrypto_hybrid_kem_expand_secret_key(VALUE self, VALUE secret_key) {
747
+ (void)self;
748
+ hybrid_expand_call_t call = {0};
749
+ VALUE result;
750
+ size_t copied_secret_key_len = 0;
751
+
752
+ pq_validate_bytes_argument(secret_key, PQ_HYBRID_SECRETKEYBYTES, "hybrid secret key");
753
+
754
+ call.secret_key = pq_copy_ruby_string(secret_key, &copied_secret_key_len);
755
+ call.expanded_secret_key = pq_alloc_buffer(PQ_HYBRID_EXPANDED_SECRETKEYBYTES);
756
+
757
+ rb_thread_call_without_gvl(pq_hybrid_kem_expand_secret_key_nogvl, &call, NULL, NULL);
758
+ pq_wipe_and_free((uint8_t *)call.secret_key, copied_secret_key_len);
759
+
760
+ if (call.result != PQ_SUCCESS) {
761
+ pq_wipe_and_free(call.expanded_secret_key, PQ_HYBRID_EXPANDED_SECRETKEYBYTES);
762
+ pq_raise_general_error(call.result);
763
+ }
764
+
765
+ result = pq_string_from_buffer(call.expanded_secret_key, PQ_HYBRID_EXPANDED_SECRETKEYBYTES);
766
+ pq_wipe_and_free(call.expanded_secret_key, PQ_HYBRID_EXPANDED_SECRETKEYBYTES);
767
+ return result;
768
+ }
769
+
770
+ static VALUE pqcrypto_hybrid_kem_expand_secret_key_object(VALUE self, VALUE secret_key) {
771
+ (void)self;
772
+ hybrid_expand_call_t call = {0};
773
+ size_t copied_secret_key_len = 0;
774
+
775
+ pq_validate_bytes_argument(secret_key, PQ_HYBRID_SECRETKEYBYTES, "hybrid secret key");
776
+
777
+ hybrid_expanded_key_wrapper_t *wrapper;
778
+ VALUE obj = TypedData_Make_Struct(rb_cObject, hybrid_expanded_key_wrapper_t,
779
+ &hybrid_expanded_key_data_type, wrapper);
780
+ memset(wrapper->expanded_secret_key, 0, sizeof(wrapper->expanded_secret_key));
781
+ wrapper->x25519_private_pkey = NULL;
782
+
783
+ call.secret_key = pq_copy_ruby_string(secret_key, &copied_secret_key_len);
784
+ call.expanded_secret_key = wrapper->expanded_secret_key;
785
+
786
+ rb_thread_call_without_gvl(pq_hybrid_kem_expand_secret_key_nogvl, &call, NULL, NULL);
787
+ pq_wipe_and_free((uint8_t *)call.secret_key, copied_secret_key_len);
788
+
789
+ if (call.result != PQ_SUCCESS) {
790
+ pq_secure_wipe(wrapper->expanded_secret_key, sizeof(wrapper->expanded_secret_key));
791
+ pq_raise_general_error(call.result);
792
+ }
793
+
794
+ const hybrid_expanded_secret_key_t *expanded =
795
+ (const hybrid_expanded_secret_key_t *)wrapper->expanded_secret_key;
796
+ wrapper->x25519_private_pkey = EVP_PKEY_new_raw_private_key(
797
+ EVP_PKEY_X25519, NULL, expanded->x25519_sk, X25519_SECRETKEYBYTES);
798
+ if (!wrapper->x25519_private_pkey) {
799
+ pq_secure_wipe(wrapper->expanded_secret_key, sizeof(wrapper->expanded_secret_key));
800
+ pq_raise_general_error(PQ_ERROR_OPENSSL);
801
+ }
802
+
803
+ return obj;
804
+ }
805
+
643
806
  static VALUE pqcrypto_hybrid_kem_decapsulate(VALUE self, VALUE ciphertext, VALUE secret_key) {
644
807
  (void)self;
645
808
  return pq_run_kem_decapsulate(pq_hybrid_kem_decapsulate_nogvl, ciphertext,
@@ -647,6 +810,50 @@ static VALUE pqcrypto_hybrid_kem_decapsulate(VALUE self, VALUE ciphertext, VALUE
647
810
  PQ_HYBRID_SHAREDSECRETBYTES);
648
811
  }
649
812
 
813
+ static VALUE pqcrypto_hybrid_kem_decapsulate_expanded(VALUE self, VALUE ciphertext,
814
+ VALUE expanded_secret_key) {
815
+ (void)self;
816
+ return pq_run_kem_decapsulate(pq_hybrid_kem_decapsulate_expanded_nogvl, ciphertext,
817
+ PQ_HYBRID_CIPHERTEXTBYTES, expanded_secret_key,
818
+ PQ_HYBRID_EXPANDED_SECRETKEYBYTES,
819
+ PQ_HYBRID_SHAREDSECRETBYTES);
820
+ }
821
+
822
+ static VALUE pqcrypto_hybrid_kem_decapsulate_expanded_object(VALUE self, VALUE ciphertext,
823
+ VALUE expanded_secret_key_obj) {
824
+ (void)self;
825
+ hybrid_expanded_key_wrapper_t *wrapper = hybrid_expanded_key_unwrap(expanded_secret_key_obj);
826
+ hybrid_decapsulate_expanded_pkey_call_t call = {0};
827
+ VALUE result;
828
+ size_t copied_ciphertext_len = 0;
829
+
830
+ pq_validate_bytes_argument(ciphertext, PQ_HYBRID_CIPHERTEXTBYTES, "ciphertext");
831
+
832
+ call.ciphertext = pq_copy_ruby_string(ciphertext, &copied_ciphertext_len);
833
+ call.expanded_secret_key = wrapper->expanded_secret_key;
834
+ call.shared_secret = pq_alloc_buffer(PQ_HYBRID_SHAREDSECRETBYTES);
835
+
836
+ if (EVP_PKEY_up_ref(wrapper->x25519_private_pkey) != 1) {
837
+ pq_wipe_and_free((uint8_t *)call.ciphertext, copied_ciphertext_len);
838
+ pq_wipe_and_free(call.shared_secret, PQ_HYBRID_SHAREDSECRETBYTES);
839
+ pq_raise_general_error(PQ_ERROR_OPENSSL);
840
+ }
841
+ call.x25519_private_pkey = wrapper->x25519_private_pkey;
842
+
843
+ rb_thread_call_without_gvl(pq_hybrid_kem_decapsulate_expanded_pkey_nogvl, &call, NULL, NULL);
844
+ EVP_PKEY_free((EVP_PKEY *)call.x25519_private_pkey);
845
+ pq_wipe_and_free((uint8_t *)call.ciphertext, copied_ciphertext_len);
846
+
847
+ if (call.result != PQ_SUCCESS) {
848
+ pq_wipe_and_free(call.shared_secret, PQ_HYBRID_SHAREDSECRETBYTES);
849
+ pq_raise_general_error(call.result);
850
+ }
851
+
852
+ result = pq_string_from_buffer(call.shared_secret, PQ_HYBRID_SHAREDSECRETBYTES);
853
+ pq_wipe_and_free(call.shared_secret, PQ_HYBRID_SHAREDSECRETBYTES);
854
+ return result;
855
+ }
856
+
650
857
  static VALUE pqcrypto__test_ml_kem_keypair_from_seed(VALUE self, VALUE seed) {
651
858
  (void)self;
652
859
  StringValue(seed);
@@ -1220,6 +1427,15 @@ static VALUE pqcrypto__native_mldsa_mu_builder_update(VALUE self, VALUE builder_
1220
1427
  return Qnil;
1221
1428
  }
1222
1429
 
1430
+ if (chunk_len < PQ_MU_ABSORB_NOGVL_MIN_BYTES) {
1431
+ int rc = pq_mu_builder_absorb(wrapper->builder, (const uint8_t *)RSTRING_PTR(chunk),
1432
+ chunk_len);
1433
+ if (rc != PQ_SUCCESS) {
1434
+ pq_raise_general_error(rc);
1435
+ }
1436
+ return Qnil;
1437
+ }
1438
+
1223
1439
  uint8_t *copy = pq_alloc_buffer(chunk_len);
1224
1440
  memcpy(copy, RSTRING_PTR(chunk), chunk_len);
1225
1441
 
@@ -1432,6 +1648,7 @@ static VALUE pqcrypto_secret_key_from_pqc_container_pem(VALUE self, VALUE pem) {
1432
1648
 
1433
1649
  void Init_pqcrypto_secure(void) {
1434
1650
  mPQCrypto = rb_define_module("PQCrypto");
1651
+ pq_init_algorithm_ids();
1435
1652
  ePQCryptoError = rb_define_class_under(mPQCrypto, "Error", rb_eStandardError);
1436
1653
 
1437
1654
  ePQCryptoVerificationError =
@@ -1478,8 +1695,16 @@ void Init_pqcrypto_secure(void) {
1478
1695
  rb_define_module_function(mPQCrypto, "hybrid_kem_keypair", pqcrypto_hybrid_kem_keypair, 0);
1479
1696
  rb_define_module_function(mPQCrypto, "hybrid_kem_encapsulate", pqcrypto_hybrid_kem_encapsulate,
1480
1697
  1);
1698
+ rb_define_module_function(mPQCrypto, "hybrid_kem_expand_secret_key",
1699
+ pqcrypto_hybrid_kem_expand_secret_key, 1);
1700
+ rb_define_module_function(mPQCrypto, "hybrid_kem_expand_secret_key_object",
1701
+ pqcrypto_hybrid_kem_expand_secret_key_object, 1);
1481
1702
  rb_define_module_function(mPQCrypto, "hybrid_kem_decapsulate", pqcrypto_hybrid_kem_decapsulate,
1482
1703
  2);
1704
+ rb_define_module_function(mPQCrypto, "hybrid_kem_decapsulate_expanded",
1705
+ pqcrypto_hybrid_kem_decapsulate_expanded, 2);
1706
+ rb_define_module_function(mPQCrypto, "hybrid_kem_decapsulate_expanded_object",
1707
+ pqcrypto_hybrid_kem_decapsulate_expanded_object, 2);
1483
1708
  rb_define_module_function(mPQCrypto, "sign_keypair", pqcrypto_sign_keypair, 0);
1484
1709
  rb_define_module_function(mPQCrypto, "sign", pqcrypto_sign, 2);
1485
1710
  rb_define_module_function(mPQCrypto, "verify", pqcrypto_verify, 3);