pq_crypto 0.5.0 → 0.5.1
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +27 -2
- data/CHANGELOG.md +34 -0
- data/ext/pqcrypto/extconf.rb +56 -21
- data/ext/pqcrypto/pqcrypto_native_api.h +3 -0
- data/ext/pqcrypto/pqcrypto_ruby_secure.c +234 -9
- data/ext/pqcrypto/pqcrypto_secure.c +295 -218
- data/ext/pqcrypto/pqcrypto_secure.h +12 -3
- data/ext/pqcrypto/pqcrypto_version.h +1 -1
- data/ext/pqcrypto/vendor/.vendored +4 -2
- data/lib/pq_crypto/hybrid_kem.rb +10 -1
- data/lib/pq_crypto/version.rb +1 -1
- data/lib/pq_crypto.rb +4 -0
- data/script/vendor_libs.rb +218 -73
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d5949078149668609462a1e9a3df7b7f767499311b998e4dd651be1d7865ddfc
|
|
4
|
+
data.tar.gz: 600fc91b9614aec5f7f61d8c3ab6d11643bbbf919941faf0508384e7ede9dce0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2ac70b6f37460e0a56b1601b59ba5660dda7e4c0c87cc03211c9497261b06f632856079622546f6c8b6db6cfff97b1a9355db879958985f48dfe296c5d729b80
|
|
7
|
+
data.tar.gz: 9e89256c60b8153518520438b77632eb232660ff88d0b0668cfb8aeea7a50875c2c7297773f3d469952129ccceb6e604e4f619cc4cc2ebbd458fdca9d50e837f
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -1,12 +1,30 @@
|
|
|
1
1
|
name: CI
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: ["**"]
|
|
6
4
|
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
7
|
|
|
8
8
|
jobs:
|
|
9
|
+
vendor-verify:
|
|
10
|
+
name: vendor-verify
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout
|
|
15
|
+
uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Set up Ruby
|
|
18
|
+
uses: ruby/setup-ruby@v1
|
|
19
|
+
with:
|
|
20
|
+
ruby-version: "3.4"
|
|
21
|
+
bundler-cache: true
|
|
22
|
+
|
|
23
|
+
- name: Verify vendored sources against pinned tree_sha256
|
|
24
|
+
run: bundle exec rake vendor:verify
|
|
25
|
+
|
|
9
26
|
test:
|
|
27
|
+
needs: vendor-verify
|
|
10
28
|
runs-on: ${{ matrix.os }}
|
|
11
29
|
|
|
12
30
|
strategy:
|
|
@@ -30,6 +48,9 @@ jobs:
|
|
|
30
48
|
with:
|
|
31
49
|
go-version: "1.26.0"
|
|
32
50
|
|
|
51
|
+
- name: Verify vendored sources
|
|
52
|
+
run: bundle exec rake vendor:verify
|
|
53
|
+
|
|
33
54
|
- name: Compile extension
|
|
34
55
|
run: bundle exec rake compile
|
|
35
56
|
|
|
@@ -37,6 +58,7 @@ jobs:
|
|
|
37
58
|
run: bundle exec rake test
|
|
38
59
|
|
|
39
60
|
interop-openssl-3-5-required:
|
|
61
|
+
needs: vendor-verify
|
|
40
62
|
name: interop-openssl-3.5-required
|
|
41
63
|
runs-on: ubuntu-24.04
|
|
42
64
|
|
|
@@ -77,6 +99,9 @@ jobs:
|
|
|
77
99
|
with:
|
|
78
100
|
go-version: "1.26.0"
|
|
79
101
|
|
|
102
|
+
- name: Verify vendored sources
|
|
103
|
+
run: bundle exec rake vendor:verify
|
|
104
|
+
|
|
80
105
|
- name: Compile extension
|
|
81
106
|
run: bundle exec rake compile
|
|
82
107
|
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.5.1] - 2026-05-04
|
|
4
|
+
|
|
5
|
+
### Performance
|
|
6
|
+
|
|
7
|
+
- Enabled native asm/SIMD paths for ML-DSA/ML-KEM where available.
|
|
8
|
+
- Reduced Hybrid KEM X25519 overhead by reusing expanded/native key state and avoiding repeated private/public key reconstruction.
|
|
9
|
+
- Moved hot crypto calls under `rb_nogvl` where applicable.
|
|
10
|
+
- Optimized PEM export path by replacing BIO/streaming base64 with direct encode.
|
|
11
|
+
- Reduced small-buffer streaming overhead by avoiding unnecessary `malloc`/`nogvl` work for tiny chunks.
|
|
12
|
+
|
|
13
|
+
### Vendoring
|
|
14
|
+
|
|
15
|
+
- Committed `mlkem-native` and `mldsa-native` sources to the repository. Builds no longer require git or network.
|
|
16
|
+
- Pinned upstream by `commit` + `tree_sha256` in `script/vendor_libs.rb`; manifest signed with `manifest_sha256`.
|
|
17
|
+
- Deterministic vendor tree (no symlinks, dotfiles, normalized mtime/permissions).
|
|
18
|
+
- `script/vendor_libs.rb` now supports `--verify`, `--sync`, `--bump`.
|
|
19
|
+
|
|
20
|
+
### Build
|
|
21
|
+
|
|
22
|
+
- `extconf.rb` no longer auto-fetches upstream. Missing vendor aborts the build; opt in with `PQCRYPTO_AUTO_VENDOR=1`.
|
|
23
|
+
|
|
24
|
+
### Rakefile
|
|
25
|
+
|
|
26
|
+
- Added `vendor:verify`, `vendor:sync`, `vendor:bump`. Default task runs `vendor:verify` before `compile` and `test`.
|
|
27
|
+
|
|
28
|
+
### CI
|
|
29
|
+
|
|
30
|
+
- Added `vendor-verify` gating job; `vendor:verify` also runs in each test job before compile.
|
|
31
|
+
|
|
32
|
+
### Repository
|
|
33
|
+
|
|
34
|
+
- `.gitattributes` enforces `eol=lf` and marks vendor as binary (CRLF protection).
|
|
35
|
+
- `.gitignore` no longer hides `sempls/`.
|
|
36
|
+
|
|
3
37
|
## [0.5.0] - 2026-05-04
|
|
4
38
|
|
|
5
39
|
### Changed — native backend migration
|
data/ext/pqcrypto/extconf.rb
CHANGED
|
@@ -39,7 +39,45 @@ if SANITIZE && !SANITIZE.strip.empty?
|
|
|
39
39
|
$LDFLAGS << " -fsanitize=#{sanitize}"
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
def native_asm_supported_by_default?
|
|
43
|
+
host_cpu = RbConfig::CONFIG.fetch("host_cpu", "")
|
|
44
|
+
host_os = RbConfig::CONFIG.fetch("host_os", "")
|
|
45
|
+
return false if host_os =~ /mswin|mingw|cygwin/i
|
|
46
|
+
|
|
47
|
+
host_cpu =~ /\A(?:arm64|aarch64)\z/i
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def parse_native_asm_env(value)
|
|
51
|
+
return native_asm_supported_by_default? if value.nil? || value.strip.empty? || value == "auto"
|
|
52
|
+
|
|
53
|
+
case value.strip.downcase
|
|
54
|
+
when "1", "true", "yes", "on", "auto"
|
|
55
|
+
true
|
|
56
|
+
when "0", "false", "no", "off"
|
|
57
|
+
false
|
|
58
|
+
else
|
|
59
|
+
abort "Invalid PQCRYPTO_NATIVE_ASM=#{value.inspect}; use 1, 0, or auto"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
NATIVE_ASM = parse_native_asm_env(ENV["PQCRYPTO_NATIVE_ASM"])
|
|
64
|
+
|
|
65
|
+
def parse_native_backend_env(name)
|
|
66
|
+
value = ENV[name]
|
|
67
|
+
return NATIVE_ASM if value.nil? || value.strip.empty? || value == "auto"
|
|
68
|
+
|
|
69
|
+
case value.strip.downcase
|
|
70
|
+
when "1", "true", "yes", "on"
|
|
71
|
+
true
|
|
72
|
+
when "0", "false", "no", "off"
|
|
73
|
+
false
|
|
74
|
+
else
|
|
75
|
+
abort "Invalid #{name}=#{value.inspect}; use 1, 0, or auto"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
NATIVE_ARITH = parse_native_backend_env("PQCRYPTO_NATIVE_ARITH")
|
|
80
|
+
NATIVE_FIPS202 = parse_native_backend_env("PQCRYPTO_NATIVE_FIPS202")
|
|
43
81
|
|
|
44
82
|
def configure_compiler_environment
|
|
45
83
|
return unless RUBY_PLATFORM.include?("darwin")
|
|
@@ -66,28 +104,25 @@ def vendor_script_path
|
|
|
66
104
|
end
|
|
67
105
|
|
|
68
106
|
def run_vendor_script!(vendor_dir)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
PQ Code Package vendored sources are missing and script/vendor_libs.rb was not packaged.
|
|
107
|
+
abort <<~MSG if ENV["PQCRYPTO_AUTO_VENDOR"] != "1"
|
|
108
|
+
PQ Code Package vendored sources are missing.
|
|
72
109
|
|
|
73
110
|
Expected:
|
|
74
111
|
#{native_vendor_sources_for(vendor_dir).join("\n ")}
|
|
75
112
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
MSG
|
|
79
|
-
|
|
80
|
-
abort <<~MSG if ENV["PQCRYPTO_AUTO_VENDOR"] == "0"
|
|
81
|
-
PQ Code Package vendored sources are missing and PQCRYPTO_AUTO_VENDOR=0 was set.
|
|
82
|
-
|
|
83
|
-
Expected:
|
|
84
|
-
#{native_vendor_sources_for(vendor_dir).join("\n ")}
|
|
113
|
+
The vendor tree is committed to the repository and shipped with the gem.
|
|
114
|
+
If it is missing, the source tree is incomplete or corrupted.
|
|
85
115
|
|
|
86
|
-
|
|
116
|
+
To fetch upstream sources at the pinned commits run:
|
|
87
117
|
ruby script/vendor_libs.rb
|
|
118
|
+
|
|
119
|
+
Or to allow extconf.rb to do this for you set PQCRYPTO_AUTO_VENDOR=1.
|
|
88
120
|
MSG
|
|
89
121
|
|
|
90
|
-
|
|
122
|
+
script = vendor_script_path
|
|
123
|
+
abort "PQ Code Package vendored sources are missing and script/vendor_libs.rb was not packaged." unless File.exist?(script)
|
|
124
|
+
|
|
125
|
+
puts "PQ Code Package native sources are missing; vendoring now (PQCRYPTO_AUTO_VENDOR=1)..."
|
|
91
126
|
ok = system(RbConfig.ruby, script)
|
|
92
127
|
abort <<~MSG unless ok
|
|
93
128
|
Failed to vendor PQ Code Package native sources.
|
|
@@ -210,10 +245,8 @@ def native_flags(kind, level, shared:)
|
|
|
210
245
|
flags << "-D#{prefix}_CONFIG_NAMESPACE_PREFIX=#{ns}"
|
|
211
246
|
flags << "-D#{prefix}_CONFIG_NO_SUPERCOP"
|
|
212
247
|
flags << (shared ? "-D#{prefix}_CONFIG_MULTILEVEL_WITH_SHARED" : "-D#{prefix}_CONFIG_MULTILEVEL_NO_SHARED")
|
|
213
|
-
if
|
|
214
|
-
|
|
215
|
-
flags << "-D#{prefix}_CONFIG_USE_NATIVE_BACKEND_FIPS202"
|
|
216
|
-
end
|
|
248
|
+
flags << "-D#{prefix}_CONFIG_USE_NATIVE_BACKEND_ARITH" if NATIVE_ARITH
|
|
249
|
+
flags << "-D#{prefix}_CONFIG_USE_NATIVE_BACKEND_FIPS202" if NATIVE_FIPS202
|
|
217
250
|
flags.join(" ")
|
|
218
251
|
end
|
|
219
252
|
|
|
@@ -241,7 +274,7 @@ def inject_native_sources!(config)
|
|
|
241
274
|
RULE
|
|
242
275
|
end
|
|
243
276
|
|
|
244
|
-
if
|
|
277
|
+
if NATIVE_ARITH || NATIVE_FIPS202
|
|
245
278
|
[
|
|
246
279
|
[:mlkem, "512", config[:mlkem_asm], true],
|
|
247
280
|
[:mlkem, "768", config[:mlkem_asm], false],
|
|
@@ -288,7 +321,9 @@ native_config = native_vendor_config(vendor_dir)
|
|
|
288
321
|
puts "OpenSSL: system"
|
|
289
322
|
puts "ML-KEM: mlkem-native vendored"
|
|
290
323
|
puts "ML-DSA: mldsa-native vendored"
|
|
291
|
-
puts "Native asm
|
|
324
|
+
puts "Native asm auto/forced: #{NATIVE_ASM ? 'enabled' : 'disabled'}"
|
|
325
|
+
puts "Native arithmetic backend: #{NATIVE_ARITH ? 'enabled' : 'disabled'}"
|
|
326
|
+
puts "Native FIPS202 backend: #{NATIVE_FIPS202 ? 'enabled' : 'disabled'}"
|
|
292
327
|
puts "PQClean fallback: removed"
|
|
293
328
|
puts "Output: pqcrypto/pqcrypto_secure"
|
|
294
329
|
puts "===================================="
|
|
@@ -78,6 +78,9 @@ int pqcr_mlkem1024_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk);
|
|
|
78
78
|
int pqcr_mlkem1024_enc_derand(uint8_t *ct, uint8_t *ss, const uint8_t *pk, const uint8_t *coins);
|
|
79
79
|
int pqcr_mlkem1024_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk);
|
|
80
80
|
|
|
81
|
+
void pqcr_mlkem_shake256(uint8_t *output, size_t outlen, const uint8_t *input, size_t inlen);
|
|
82
|
+
void pqcr_mlkem_sha3_256(uint8_t *output, const uint8_t *input, size_t inlen);
|
|
83
|
+
|
|
81
84
|
/* mldsa-native symbols: namespace prefix pqcr_mldsa + level suffix. */
|
|
82
85
|
int pqcr_mldsa44_keypair(uint8_t *pk, uint8_t *sk);
|
|
83
86
|
int pqcr_mldsa44_keypair_internal(uint8_t *pk, uint8_t *sk, const uint8_t seed[MLDSA_SEEDBYTES]);
|
|
@@ -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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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(
|
|
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);
|