pq_crypto 0.6.5 → 0.6.7
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/CHANGELOG.md +233 -0
- data/README.md +16 -3
- data/SECURITY.md +46 -0
- data/ext/pqcrypto/extconf.rb +8 -1
- data/ext/pqcrypto/pq_externalmu.c +35 -0
- data/ext/pqcrypto/pqcrypto_native_api.h +91 -75
- data/ext/pqcrypto/pqcrypto_ruby_secure.c +97 -9
- data/ext/pqcrypto/pqcrypto_secure.c +95 -33
- data/ext/pqcrypto/pqcrypto_secure.h +66 -48
- data/ext/pqcrypto/pqcrypto_version.h +1 -1
- data/ext/pqcrypto/vendor/.vendored +7 -7
- data/ext/pqcrypto/vendor/mldsa-native/BUILDING.md +5 -2
- data/ext/pqcrypto/vendor/mldsa-native/LICENSE +21 -2
- data/ext/pqcrypto/vendor/mldsa-native/README.md +20 -7
- data/ext/pqcrypto/vendor/mldsa-native/RELEASE.md +160 -0
- data/ext/pqcrypto/vendor/mldsa-native/SECURITY.md +1 -1
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/README.md +2 -2
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/mldsa_native.c +85 -59
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/mldsa_native.h +292 -348
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/mldsa_native_asm.S +122 -76
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/mldsa_native_config.h +184 -86
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/cbmc.h +49 -4
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/common.h +49 -81
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/context.h +152 -0
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/ct.h +25 -12
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/debug.c +2 -0
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/debug.h +2 -0
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/fips202x4.c +2 -2
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/keccakf1600.c +9 -11
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/auto.h +19 -11
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x1_scalar_aarch64_asm.S +6 -4
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x1_v84a_aarch64_asm.S +7 -4
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x2_v84a_aarch64_asm.S +7 -4
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x4_v8a_scalar_hybrid_aarch64_asm.S +12 -9
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x4_v8a_v84a_scalar_hybrid_aarch64_asm.S +12 -9
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x1_scalar.h +1 -1
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x1_v84a.h +3 -2
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x2_v84a.h +3 -2
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x4_v8a_scalar.h +6 -1
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x4_v8a_v84a_scalar.h +3 -2
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/api.h +11 -11
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/mve.h +9 -22
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/keccak_f1600_x4_mve.S +8 -5
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/keccak_f1600_x4_mve.c +1 -0
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/{state_extract_bytes_x4_mve.S → keccak_f1600_x4_state_extract_bytes_mve.S} +14 -14
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/{state_xor_bytes_x4_mve.S → keccak_f1600_x4_state_xor_bytes_mve.S} +12 -12
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/auto.h +5 -4
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/x86_64/keccak_f1600_x4_avx2.h +2 -2
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/x86_64/src/fips202_native_x86_64.h +1 -0
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/x86_64/src/keccak_f1600_x4_avx2_asm.S +36 -2
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/meta.h +62 -4
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/arith_native_aarch64.h +87 -54
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{intt_aarch64_asm.S → mldsa_intt_aarch64_asm.S} +39 -6
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{ntt_aarch64_asm.S → mldsa_ntt_aarch64_asm.S} +39 -6
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{pointwise_montgomery_aarch64_asm.S → mldsa_pointwise_montgomery_aarch64_asm.S} +25 -3
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{poly_caddq_aarch64_asm.S → mldsa_poly_caddq_aarch64_asm.S} +19 -3
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{poly_chknorm_aarch64_asm.S → mldsa_poly_chknorm_aarch64_asm.S} +24 -3
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{poly_decompose_32_aarch64_asm.S → mldsa_poly_decompose_32_aarch64_asm.S} +25 -3
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{poly_decompose_88_aarch64_asm.S → mldsa_poly_decompose_88_aarch64_asm.S} +25 -3
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{poly_use_hint_32_aarch64_asm.S → mldsa_poly_use_hint_32_aarch64_asm.S} +25 -3
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{poly_use_hint_88_aarch64_asm.S → mldsa_poly_use_hint_88_aarch64_asm.S} +25 -3
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{mld_polyvecl_pointwise_acc_montgomery_l4_aarch64_asm.S → mldsa_polyvecl_pointwise_acc_montgomery_l4_aarch64_asm.S} +31 -3
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{mld_polyvecl_pointwise_acc_montgomery_l5_aarch64_asm.S → mldsa_polyvecl_pointwise_acc_montgomery_l5_aarch64_asm.S} +31 -3
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{mld_polyvecl_pointwise_acc_montgomery_l7_aarch64_asm.S → mldsa_polyvecl_pointwise_acc_montgomery_l7_aarch64_asm.S} +31 -3
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{polyz_unpack_17_aarch64_asm.S → mldsa_polyz_unpack_17_aarch64_asm.S} +31 -3
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{polyz_unpack_19_aarch64_asm.S → mldsa_polyz_unpack_19_aarch64_asm.S} +31 -3
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{rej_uniform_aarch64_asm.S → mldsa_rej_uniform_aarch64_asm.S} +48 -15
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{rej_uniform_eta2_aarch64_asm.S → mldsa_rej_uniform_eta2_aarch64_asm.S} +42 -9
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{rej_uniform_eta4_aarch64_asm.S → mldsa_rej_uniform_eta4_aarch64_asm.S} +42 -9
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/api.h +11 -3
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/meta.h +3 -2
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/meta.h +28 -28
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/arith_native_x86_64.h +171 -49
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/{intt_avx2_asm.S → mldsa_intt_avx2_asm.S} +23 -1
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/{ntt_avx2_asm.S → mldsa_ntt_avx2_asm.S} +23 -1
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/{nttunpack_avx2_asm.S → mldsa_nttunpack_avx2_asm.S} +17 -1
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/{pointwise_acc_l4_avx2_asm.S → mldsa_pointwise_acc_l4_avx2_asm.S} +37 -3
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/{pointwise_acc_l5_avx2_asm.S → mldsa_pointwise_acc_l5_avx2_asm.S} +37 -3
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/{pointwise_acc_l7_avx2_asm.S → mldsa_pointwise_acc_l7_avx2_asm.S} +37 -3
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/{pointwise_avx2_asm.S → mldsa_pointwise_avx2_asm.S} +31 -3
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/{poly_caddq_avx2_asm.S → mldsa_poly_caddq_avx2_asm.S} +18 -9
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_chknorm_avx2_asm.S +176 -0
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_decompose_32_avx2_asm.S +490 -0
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_decompose_88_avx2_asm.S +489 -0
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_use_hint_32_avx2_asm.S +123 -0
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_use_hint_88_avx2_asm.S +125 -0
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_polyz_unpack_17_avx2_asm.S +355 -0
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_polyz_unpack_19_avx2_asm.S +355 -0
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_rej_uniform_avx2_asm.S +132 -0
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_rej_uniform_eta2_avx2_asm.S +205 -0
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_rej_uniform_eta4_avx2_asm.S +176 -0
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/packing.c +27 -36
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/packing.h +42 -8
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/params.h +93 -17
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/poly.c +74 -15
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/poly.h +97 -11
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/poly_kl.c +7 -38
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/poly_kl.h +49 -7
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/polyvec.c +16 -17
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/polyvec.h +26 -9
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/polyvec_lazy.c +3 -0
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/polyvec_lazy.h +18 -19
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/reduce.h +15 -3
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/rounding.h +28 -6
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/sign.c +311 -246
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/sign.h +245 -240
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/sys.h +64 -5
- data/ext/pqcrypto/vendor/mlkem-native/BUILDING.md +5 -2
- data/ext/pqcrypto/vendor/mlkem-native/LICENSE +21 -3
- data/ext/pqcrypto/vendor/mlkem-native/README.md +2 -2
- data/ext/pqcrypto/vendor/mlkem-native/RELEASE.md +113 -0
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/README.md +2 -2
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/mlkem_native.c +17 -27
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/mlkem_native.h +68 -151
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/mlkem_native_asm.S +17 -27
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/mlkem_native_config.h +46 -44
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/cbmc.h +25 -0
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/common.h +37 -6
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/context.h +9 -0
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/fips202.h +2 -2
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/keccakf1600.c +8 -8
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/x1_scalar.h +1 -1
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/x1_v84a.h +3 -3
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/x2_v84a.h +3 -3
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/x4_v8a_scalar.h +2 -2
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/x4_v8a_v84a_scalar.h +3 -3
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/api.h +11 -11
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/armv81m/mve.h +3 -3
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/x86_64/keccak_f1600_x4_avx2.h +2 -2
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/indcpa.h +14 -11
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/kem.c +28 -11
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/kem.h +39 -14
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/aarch64/meta.h +10 -10
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/meta.h +20 -20
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/sys.h +5 -5
- data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/verify.h +11 -10
- data/lib/pq_crypto/internal.rb +10 -0
- data/lib/pq_crypto/kem.rb +47 -5
- data/lib/pq_crypto/key.rb +14 -8
- data/lib/pq_crypto/pkcs8.rb +13 -5
- data/lib/pq_crypto/signature.rb +34 -9
- data/lib/pq_crypto/spki.rb +4 -2
- data/lib/pq_crypto/version.rb +1 -1
- data/lib/pq_crypto.rb +9 -0
- data/script/vendor_libs.rb +6 -6
- metadata +40 -38
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_chknorm_avx2.c +0 -52
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_decompose_32_avx2.c +0 -157
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_decompose_88_avx2.c +0 -157
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_use_hint_32_avx2.c +0 -103
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_use_hint_88_avx2.c +0 -105
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/polyz_unpack_17_avx2.c +0 -94
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/polyz_unpack_19_avx2.c +0 -96
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/rej_uniform_avx2.c +0 -126
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/rej_uniform_eta2_avx2.c +0 -157
- data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/rej_uniform_eta4_avx2.c +0 -141
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cf8d3b17b9579ef551ef337aab16a9eb356846a36ff492577e2d728ec0e285e2
|
|
4
|
+
data.tar.gz: 26c3c3646d066617e37cb2cb1219e7ee5ca640488c84b480b6259461999b48e5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bd58150566a566af1c3083ef2c72538acafb6b15c5ebe954d0d7af35f96cdf41a5f1fe17c8d3c956e29b3c51bd89b29ec24245749efbca82389b1d2dc3c91ede
|
|
7
|
+
data.tar.gz: 5d974df3a99f9284da5c5cdfff25ba1808dc083063c12f97db6b803a34aa15687a0cda9565c8617edd95015c8d23139181d56d69d2483a3ac17b2239bec3cba3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,238 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.6.7] - 2026-08-10
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **DER parsers could raise exceptions outside the `PQCrypto::Error` hierarchy.**
|
|
8
|
+
`OpenSSL::ASN1.decode` raises more than `OpenSSL::ASN1::ASN1Error`, and
|
|
9
|
+
re-encoding a successfully decoded object can fail as well. Three shapes
|
|
10
|
+
escaped the previous handling in `PQCrypto::SPKI`:
|
|
11
|
+
|
|
12
|
+
- `"\x0A\x01\xFF"` (ENUMERATED) raised `OpenSSL::OpenSSLError`
|
|
13
|
+
- `"\x17\x00"` / `"\x18\x00"` (empty UTCTIME / GENERALIZEDTIME) raised `TypeError`
|
|
14
|
+
- `"\x10\x00"` (universal SEQUENCE tag without the constructed bit) decoded
|
|
15
|
+
cleanly but raised `TypeError` from the `to_der` round trip used for the
|
|
16
|
+
trailing-data check
|
|
17
|
+
|
|
18
|
+
`TypeError` does not descend from `OpenSSL::OpenSSLError`, so widening the
|
|
19
|
+
rescue to the OpenSSL base class alone was not sufficient. Both rescue sites
|
|
20
|
+
now catch `OpenSSL::OpenSSLError, TypeError`, and `SPKI.decode_der` is guarded
|
|
21
|
+
end to end rather than only around the decode call.
|
|
22
|
+
|
|
23
|
+
Impact: code written as `rescue PQCrypto::Error` around key loading could be
|
|
24
|
+
bypassed by malformed input. No memory-safety consequence. Covered by a
|
|
25
|
+
systematic tag sweep over 1,280 DER shapes × three entry points
|
|
26
|
+
(`Key.from_der`, `SPKI.decode_der`, `PKCS8.decode_der`).
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- **FIPS 203 key validation for ML-KEM (boundary diagnostics).** mlkem-native
|
|
31
|
+
v2 exposes `check_pk` and `check_sk`, which this gem did not use. Both are
|
|
32
|
+
now wired in and run when a `PQCrypto::KEM::PublicKey` or `SecretKey` is
|
|
33
|
+
constructed:
|
|
34
|
+
|
|
35
|
+
- public keys get the Section 7.2 modulus check (coefficients in `[0,q-1]`)
|
|
36
|
+
- secret keys get the Section 7.3 hash check (`H(pk)` embedded in the sk)
|
|
37
|
+
|
|
38
|
+
Scope limits (intentional, matching FIPS 203):
|
|
39
|
+
|
|
40
|
+
- these checks are **diagnostics at import**, not a substitute for the
|
|
41
|
+
checks already performed inside encapsulation / decapsulation
|
|
42
|
+
- Section 7.3 does **not** detect a corrupted IND-CPA secret (`dk`) when
|
|
43
|
+
the embedded `H(pk)` is still intact — `#valid?` can still be true
|
|
44
|
+
- X-Wing public keys validate only the ML-KEM-768 half; the X25519 half is
|
|
45
|
+
unrestricted. X-Wing secret keys are 32-byte seeds and are not structure-
|
|
46
|
+
checked
|
|
47
|
+
|
|
48
|
+
`#valid?` is exposed on both key classes. Failures raise
|
|
49
|
+
`PQCrypto::InvalidKeyError`. Public-key internal buffers are frozen after
|
|
50
|
+
construction to block post-validation mutation; secret-key buffers stay
|
|
51
|
+
mutable so `#wipe!` can still zero them in place.
|
|
52
|
+
|
|
53
|
+
New C API: `pq_mlkem{,512,1024}_check_public_key` / `_check_secret_key`,
|
|
54
|
+
plus `PQ_ERROR_INVALID_PUBLIC_KEY` (-11) and `PQ_ERROR_INVALID_SECRET_KEY`
|
|
55
|
+
(-12). Upstream OOM (`-2`) is mapped to `PQ_ERROR_NOMEM` rather than
|
|
56
|
+
“invalid key”.
|
|
57
|
+
|
|
58
|
+
- **ML-DSA secret-key structure check on import.** Expanded ML-DSA secret
|
|
59
|
+
keys are validated via upstream `pk_from_sk` (coefficient norms, recomputed
|
|
60
|
+
`t0`, and `tr = H(pk)`). This matches the check mldsa-native documents for
|
|
61
|
+
importers and is enforced on `Signature::SecretKey` construction with
|
|
62
|
+
`#valid?` exposed. Public keys remain length-checked only (FIPS 204 has no
|
|
63
|
+
ML-KEM-style modulus check for the public key).
|
|
64
|
+
|
|
65
|
+
The check runs on import only. Keys produced by this process's own key
|
|
66
|
+
generation are valid by construction, and `pk_from_sk` re-derives the public
|
|
67
|
+
key, so validating them would roughly double the cost of every ML-DSA
|
|
68
|
+
keypair. Upstream also documents `pk_from_sk` as leaking secret-key validity
|
|
69
|
+
through its return value and timing.
|
|
70
|
+
|
|
71
|
+
New C API: `pq_mldsa{,44,87}_check_secret_key`.
|
|
72
|
+
|
|
73
|
+
- **`PQCRYPTO_KEYGEN_PCT=1` build option.** Enables the FIPS 140-3 pairwise
|
|
74
|
+
consistency test in both backends (`MLK_CONFIG_KEYGEN_PCT` /
|
|
75
|
+
`MLD_CONFIG_KEYGEN_PCT`): after generating a keypair it is exercised to catch
|
|
76
|
+
a faulty RNG or memory corruption at generation time. Upstream ships this
|
|
77
|
+
opt-in because it makes key generation noticeably more expensive, and it stays
|
|
78
|
+
opt-in here for the same reason. The selected setting is reported in the
|
|
79
|
+
build configuration banner.
|
|
80
|
+
|
|
81
|
+
- **`require_encrypted:` for PKCS#8 loading.** Supplying a passphrase states an
|
|
82
|
+
expectation that the key at rest is encrypted, but an unencrypted
|
|
83
|
+
PrivateKeyInfo was previously accepted and the passphrase left unused. An
|
|
84
|
+
encrypted key file replaced with a plaintext one therefore loaded without
|
|
85
|
+
complaint.
|
|
86
|
+
|
|
87
|
+
`require_encrypted: true` enforces that the input is an
|
|
88
|
+
EncryptedPrivateKeyInfo (DER content, not PEM label — relabeling a plaintext
|
|
89
|
+
key as `ENCRYPTED PRIVATE KEY` does not satisfy the policy). Available on
|
|
90
|
+
`PKCS8.decode_der` / `decode_pem`, `Key.from_der` / `from_pem`, and
|
|
91
|
+
`secret_key_from_pkcs8_der` / `_pem` on both `KEM` and `Signature`.
|
|
92
|
+
|
|
93
|
+
Limits (documented so the flag is not over-read):
|
|
94
|
+
|
|
95
|
+
- default is `false`; other loaders (`from_bytes`, `pqc_container_*`) are
|
|
96
|
+
unchanged
|
|
97
|
+
- the flag checks **format**, not passphrase strength (empty passphrase and
|
|
98
|
+
low PBKDF iterations still count as encrypted)
|
|
99
|
+
- the value must be a real boolean (`true`/`false`); strings like `"false"`
|
|
100
|
+
raise `ArgumentError` instead of being treated as truthy
|
|
101
|
+
|
|
102
|
+
## [0.6.6] - 2026-08-10
|
|
103
|
+
|
|
104
|
+
### Changed
|
|
105
|
+
|
|
106
|
+
- update native mlkem `v1.3.0` -> `v2.0.0` and mldsa `v1.0.0-beta2` -> `v2.0.0`
|
|
107
|
+
|
|
108
|
+
`mldsa-native` v2.0.0 is that project's first stable release; it is numbered
|
|
109
|
+
`2` only to align its API and configuration with `mlkem-native` v2. Upstream
|
|
110
|
+
now guarantees that non-experimental APIs and configuration options stay
|
|
111
|
+
stable until a `v3`.
|
|
112
|
+
|
|
113
|
+
No cryptographic output changed. All six `kat-sha256` values in the upstream
|
|
114
|
+
`META.yml` files are identical to the previously vendored versions, and the
|
|
115
|
+
vendored ML-KEM-768 keygen, ML-DSA-65 keygen and ML-DSA-65 siggen KAT vectors
|
|
116
|
+
reproduce byte-for-byte. This release is purely an API migration.
|
|
117
|
+
|
|
118
|
+
### Breaking upstream changes handled
|
|
119
|
+
|
|
120
|
+
- **`siglen` removed from the ML-DSA signing and verification APIs.** Upstream
|
|
121
|
+
signatures are always exactly `MLDSA_BYTES(level)`, so `signature`,
|
|
122
|
+
`signature_internal`, `verify`, `signature_extmu` and `verify_extmu` no longer
|
|
123
|
+
carry a length parameter. Fifteen declarations in
|
|
124
|
+
`ext/pqcrypto/pqcrypto_native_api.h` were updated accordingly.
|
|
125
|
+
|
|
126
|
+
The `pq_crypto` C API is deliberately *not* changed to match: `pq_sign`,
|
|
127
|
+
`pq_verify`, `pq_sign_mu` and `pq_verify_mu` keep their length parameters.
|
|
128
|
+
The adaptation happens at the vendor boundary instead.
|
|
129
|
+
|
|
130
|
+
- **`pq_crypto` now owns the signing output length.** Because the backend no
|
|
131
|
+
longer reports it, every signing wrapper sets `*signature_len` itself from a
|
|
132
|
+
compile-time constant and clears it to `0` when the signing operation fails,
|
|
133
|
+
reproducing the pre-v2 behaviour. This affects `pq_sign`, `pq_mldsa44_sign`,
|
|
134
|
+
`pq_mldsa87_sign`, the level-agnostic deterministic helper
|
|
135
|
+
`pq_testing_mldsa_sign_from_seed_with`, and `pq_sign_mu`.
|
|
136
|
+
|
|
137
|
+
Argument-validation failures (`PQ_ERROR_BUFFER`) still leave `*signature_len`
|
|
138
|
+
untouched, exactly as before, since the pointer itself may be the invalid one.
|
|
139
|
+
|
|
140
|
+
- **`pq_crypto` now validates signature length before verifying (security
|
|
141
|
+
relevant).** Prior to v2, `mldsa-native` rejected a wrongly sized signature
|
|
142
|
+
itself via `if (siglen != MLDSA_BYTES) return -1;`, and `pq_crypto` relied on
|
|
143
|
+
that. In v2 the verification entry points take a fixed-size array and read
|
|
144
|
+
`MLDSA_BYTES(level)` bytes unconditionally, so a short caller-supplied buffer
|
|
145
|
+
would be an out-of-bounds read. `pq_verify`, `pq_mldsa44_verify` and
|
|
146
|
+
`pq_mldsa87_verify` now enforce the exact length and return `PQ_ERROR_VERIFY`
|
|
147
|
+
otherwise; the Ruby bindings additionally reject the wrong length before
|
|
148
|
+
copying the buffer.
|
|
149
|
+
|
|
150
|
+
This was not exploitable in 0.6.5 or earlier: the previously vendored
|
|
151
|
+
`mldsa-native` performed the check. It is a latent contract dependency that a
|
|
152
|
+
naive port to v2 would have turned into a real memory-safety bug. Verified
|
|
153
|
+
with AddressSanitizer: removing this check alone reproduces a
|
|
154
|
+
`heap-buffer-overflow` read inside `pqcr_mldsa65_verify_internal`.
|
|
155
|
+
|
|
156
|
+
Observable Ruby behaviour is unchanged: a wrongly sized signature is a failed
|
|
157
|
+
verification (`false`), never an `ArgumentError`.
|
|
158
|
+
|
|
159
|
+
- **ExternalMu callbacks adapted rather than re-typed.** Six new adapters,
|
|
160
|
+
`pq_mldsa{44,65,87}_{signature,verify}_extmu_compat`, sit between the
|
|
161
|
+
pre-v2 callback contract and the fixed-size v2 API. `pq_sign_mu` and
|
|
162
|
+
`pq_verify_mu` therefore keep their existing signatures and no consumer of the
|
|
163
|
+
C interface has to change.
|
|
164
|
+
|
|
165
|
+
- **`MLK_CONFIG_NO_SUPERCOP` / `MLD_CONFIG_NO_SUPERCOP` removed upstream**
|
|
166
|
+
along with the SUPERCOP `crypto_*` aliases themselves. The define is no longer
|
|
167
|
+
passed by `extconf.rb`. Upstream has no guard against the stale macro, so it
|
|
168
|
+
would have been a silently dead `-D` rather than a build failure.
|
|
169
|
+
|
|
170
|
+
- **Legacy `MLK_CONFIG_API_*` / `MLD_CONFIG_API_*` configuration removed
|
|
171
|
+
upstream.** The extension build already used the modern `*_CONFIG_*` macros;
|
|
172
|
+
only `test/native_api_conformance` still used the legacy path and has been
|
|
173
|
+
converted.
|
|
174
|
+
|
|
175
|
+
Upstream breaking changes that do not affect this gem, checked explicitly: the
|
|
176
|
+
SUPERCOP `crypto_*` and signed-message APIs (never used), the split of
|
|
177
|
+
`*_ERR_FAIL` into specific error codes (all call sites compare against `0`), the
|
|
178
|
+
`*_SYS_CAP_*` and `*_USE_NATIVE_FIPS202_*` renames (only relevant to custom
|
|
179
|
+
backends), and the raised `MLD_CONFIG_MAX_SIGNING_ATTEMPTS` floor of 821, which
|
|
180
|
+
this build does not set. `MLD_CONFIG_KEYGEN_PCT` / `MLK_CONFIG_KEYGEN_PCT`
|
|
181
|
+
remain opt-in upstream and are not enabled here, so key generation gains no new
|
|
182
|
+
failure mode or cost.
|
|
183
|
+
|
|
184
|
+
### Testing
|
|
185
|
+
|
|
186
|
+
- `test/native_api_conformance` reworked. It previously proved size agreement
|
|
187
|
+
via `_Static_assert` and, through the double declaration of upstream and
|
|
188
|
+
gem headers, prototype agreement. It could not detect a symbol that upstream
|
|
189
|
+
removed entirely, because it only ever compiled to an object file and both a
|
|
190
|
+
plain compile and a relocatable link tolerate unresolved symbols. The harness
|
|
191
|
+
now also builds the vendored objects with the same flags `extconf.rb` uses
|
|
192
|
+
(including which level owns the shared code) and diffs `nm -u` on the probe
|
|
193
|
+
object against the symbols the vendored build actually defines.
|
|
194
|
+
|
|
195
|
+
- Conformance now covers `pqcr_mlkem_shake256` and `pqcr_mlkem_sha3_256`, the
|
|
196
|
+
extension's only dependency on a non-public part of `mlkem-native` (they back
|
|
197
|
+
the X-Wing key expansion and combiner). They were absent from `probes[]`
|
|
198
|
+
entirely, and because they are not declared in `mlkem_native.h` the
|
|
199
|
+
double-declaration check could not reach them either. The shared-level
|
|
200
|
+
conformance unit now includes the internal `src/fips202/fips202.h` so that
|
|
201
|
+
prototype drift in these two is caught as well.
|
|
202
|
+
|
|
203
|
+
- Added `test/c_api`, direct tests for the `pq_crypto` C API, runnable under
|
|
204
|
+
AddressSanitizer via `PQCRYPTO_SANITIZE=address test/c_api/run.sh`. The Ruby
|
|
205
|
+
bindings pre-set `call.signature_len` before invoking the signing entry points
|
|
206
|
+
and read the same field back afterwards, so a wrapper that stopped writing
|
|
207
|
+
`*signature_len` would leave the entire Ruby suite green. That contract, and
|
|
208
|
+
the new signature-length validation, now have coverage that does not go
|
|
209
|
+
through Ruby.
|
|
210
|
+
|
|
211
|
+
- `rake test` now runs the native API conformance and C API checks alongside the
|
|
212
|
+
Ruby suite.
|
|
213
|
+
|
|
214
|
+
- The C API tests were themselves audited by mutation testing: each contract
|
|
215
|
+
this release changes was deliberately broken in turn and the suite had to
|
|
216
|
+
fail. That found three checks which passed against broken code and have been
|
|
217
|
+
rewritten:
|
|
218
|
+
|
|
219
|
+
- The ML-DSA-44 output-length check was phrased as "signing failed OR the
|
|
220
|
+
length is right" and reused the ML-DSA-65 secret key, so a
|
|
221
|
+
`pq_mldsa44_sign` that always returned an error satisfied it. ML-DSA-44 and
|
|
222
|
+
-87 now have their own key material and unconditional assertions.
|
|
223
|
+
- The ExternalMu failure check used a stub callback that cleared `*siglen`
|
|
224
|
+
itself, so it passed even when the real adapter had stopped doing so. It now
|
|
225
|
+
drives the genuine backend error path by exhausting the thread-local test
|
|
226
|
+
seed in `pq_randombytes.c`, which makes `randombytes()` fail. The same
|
|
227
|
+
technique gives `pq_sign` real coverage of its own error path.
|
|
228
|
+
- The verify adapters' length guards were unreachable through `pq_verify_mu`,
|
|
229
|
+
which validates the length first, so they were never exercised. They are now
|
|
230
|
+
called directly.
|
|
231
|
+
|
|
232
|
+
Two further gaps were closed: `pq_sign`'s recovery after a transient RNG
|
|
233
|
+
failure is now asserted, and a poisoned `*signature_len` that was set but
|
|
234
|
+
never checked is now checked.
|
|
235
|
+
|
|
3
236
|
## [0.6.5] - 2026-08-04
|
|
4
237
|
|
|
5
238
|
### Changed
|
data/README.md
CHANGED
|
@@ -88,9 +88,11 @@ bundle exec rake compile
|
|
|
88
88
|
```
|
|
89
89
|
|
|
90
90
|
Upstream pins are recorded in `ext/pqcrypto/vendor/.vendored` and enforced by
|
|
91
|
-
`bundle exec rake vendor:verify`. `mlkem-native`
|
|
92
|
-
|
|
93
|
-
the
|
|
91
|
+
`bundle exec rake vendor:verify`. Both `mlkem-native` and `mldsa-native` are
|
|
92
|
+
pinned at `v2.0.0`. For `mldsa-native` this is the first stable release; it
|
|
93
|
+
carries the major version `2` only so that its API and configuration line up
|
|
94
|
+
with `mlkem-native` v2. Upstream guarantees that APIs and configuration options
|
|
95
|
+
not marked experimental stay stable until a `v3`.
|
|
94
96
|
|
|
95
97
|
The default build uses the portable native source path. Upstream native assembly
|
|
96
98
|
can be tested explicitly with:
|
|
@@ -99,6 +101,13 @@ can be tested explicitly with:
|
|
|
99
101
|
PQCRYPTO_NATIVE_ASM=1 bundle exec rake compile
|
|
100
102
|
```
|
|
101
103
|
|
|
104
|
+
The FIPS 140-3 pairwise consistency test is available but off by default,
|
|
105
|
+
because it makes key generation noticeably more expensive:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
PQCRYPTO_KEYGEN_PCT=1 bundle exec rake compile
|
|
109
|
+
```
|
|
110
|
+
|
|
102
111
|
## Security status
|
|
103
112
|
|
|
104
113
|
`pq_crypto` is experimental and not audited. Treat it as a low-level primitive
|
|
@@ -123,8 +132,12 @@ PQCrypto::Signature.secret_key_from_seed(:ml_dsa_65, seed_32_bytes)
|
|
|
123
132
|
|
|
124
133
|
PQCrypto::HybridKEM.generate(:ml_kem_768_x25519_xwing)
|
|
125
134
|
PQCrypto::Key.from_pem(pem, passphrase: passphrase)
|
|
135
|
+
PQCrypto::Key.from_pem(pem, passphrase: passphrase, require_encrypted: true)
|
|
136
|
+
keypair.public_key.valid? # ML-KEM structure check; ML-DSA public keys are length-only
|
|
137
|
+
keypair.secret_key.valid?
|
|
126
138
|
```
|
|
127
139
|
|
|
140
|
+
|
|
128
141
|
## More examples
|
|
129
142
|
|
|
130
143
|
Detailed usage examples live in [`GET_STARTED.md`](GET_STARTED.md):
|
data/SECURITY.md
CHANGED
|
@@ -127,6 +127,52 @@ into `mldsa-native` `signature_internal`; for an empty context this prefix is
|
|
|
127
127
|
Outside of test-only deterministic calls, production randomness delegates
|
|
128
128
|
directly to OpenSSL `RAND_bytes`.
|
|
129
129
|
|
|
130
|
+
## Key import validation (0.6.7+)
|
|
131
|
+
|
|
132
|
+
ML-KEM public/secret keys and ML-DSA secret keys are structure-checked when
|
|
133
|
+
the corresponding Ruby key objects are constructed:
|
|
134
|
+
|
|
135
|
+
| Object | Check | Source |
|
|
136
|
+
| --- | --- | --- |
|
|
137
|
+
| `KEM::PublicKey` | FIPS 203 §7.2 modulus check | `check_pk` |
|
|
138
|
+
| `KEM::SecretKey` | FIPS 203 §7.3 `H(pk)` check | `check_sk` |
|
|
139
|
+
| `Signature::SecretKey` | norms / `t0` / `tr` consistency | `pk_from_sk` |
|
|
140
|
+
| `Signature::PublicKey` | length only | — |
|
|
141
|
+
| X-Wing public key | ML-KEM-768 half only | `check_pk` |
|
|
142
|
+
| X-Wing secret key | none (32-byte seed) | — |
|
|
143
|
+
|
|
144
|
+
These are **import diagnostics**. Encapsulation, decapsulation, signing, and
|
|
145
|
+
verification still apply their own backend checks. A true `#valid?` result
|
|
146
|
+
does not prove full cryptographic integrity of every field (for example an
|
|
147
|
+
ML-KEM secret key with a corrupted IND-CPA `dk` but intact `H(pk)` still
|
|
148
|
+
passes §7.3).
|
|
149
|
+
|
|
150
|
+
The checks run on import. Keys produced by this process's own key generation
|
|
151
|
+
are valid by construction and are not re-checked, because the ML-DSA check
|
|
152
|
+
re-derives the public key and would roughly double the cost of every keypair.
|
|
153
|
+
|
|
154
|
+
The ML-DSA check (`pk_from_sk`) is documented upstream as leaking whether the
|
|
155
|
+
secret key is valid, through both its return value and its timing.
|
|
156
|
+
|
|
157
|
+
`require_encrypted:` applies only to the PKCS#8 branch. `PQCrypto::Key.from_der`
|
|
158
|
+
and `.from_pem` also accept SPKI public keys, and for those inputs the flag has
|
|
159
|
+
no effect and is silently ignored.
|
|
160
|
+
|
|
161
|
+
Public-key internal buffers are frozen after construction. Secret-key buffers
|
|
162
|
+
remain mutable so `#wipe!` can zero them.
|
|
163
|
+
|
|
164
|
+
### `require_encrypted:`
|
|
165
|
+
|
|
166
|
+
`require_encrypted: true` on PKCS#8 loaders rejects plaintext PrivateKeyInfo
|
|
167
|
+
and requires EncryptedPrivateKeyInfo (classified from DER, not PEM labels).
|
|
168
|
+
It does **not**:
|
|
169
|
+
|
|
170
|
+
- apply to `from_bytes` / `pqc_container_*` loaders
|
|
171
|
+
- enforce passphrase quality or PBKDF iteration count
|
|
172
|
+
- default on (default remains `false`)
|
|
173
|
+
|
|
174
|
+
The flag must be a boolean; non-boolean values raise `ArgumentError`.
|
|
175
|
+
|
|
130
176
|
## Memory wiping
|
|
131
177
|
|
|
132
178
|
`PQCrypto.secure_wipe` clears mutable Ruby strings in place. Ruby key objects
|
data/ext/pqcrypto/extconf.rb
CHANGED
|
@@ -84,6 +84,8 @@ def env_bool(name, default)
|
|
|
84
84
|
end
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
+
KEYGEN_PCT = env_bool("PQCRYPTO_KEYGEN_PCT", false)
|
|
88
|
+
|
|
87
89
|
NATIVE_ASM = env_bool("PQCRYPTO_NATIVE_ASM", native_asm_supported_by_default?)
|
|
88
90
|
NATIVE_ARITH = env_bool("PQCRYPTO_NATIVE_ARITH", NATIVE_ASM)
|
|
89
91
|
NATIVE_FIPS202 = env_bool("PQCRYPTO_NATIVE_FIPS202", NATIVE_ASM)
|
|
@@ -524,8 +526,12 @@ def native_flags(kind, level, shared:)
|
|
|
524
526
|
flags << "-D#{prefix}_CONFIG_MULTILEVEL_BUILD"
|
|
525
527
|
flags << "-D#{prefix}_CONFIG_PARAMETER_SET=#{level}"
|
|
526
528
|
flags << "-D#{prefix}_CONFIG_NAMESPACE_PREFIX=#{ns}"
|
|
527
|
-
|
|
529
|
+
# NOTE: MLK/MLD_CONFIG_NO_SUPERCOP was removed in mlkem-native v2.0.0 and
|
|
530
|
+
# mldsa-native v2.0.0 along with the SUPERCOP crypto_* aliases themselves.
|
|
531
|
+
# Upstream has no guard against the stale define, so passing it would be a
|
|
532
|
+
# silently dead -D rather than a build error. Do not reintroduce it.
|
|
528
533
|
flags << (shared ? "-D#{prefix}_CONFIG_MULTILEVEL_WITH_SHARED" : "-D#{prefix}_CONFIG_MULTILEVEL_NO_SHARED")
|
|
534
|
+
flags << "-D#{prefix}_CONFIG_KEYGEN_PCT" if KEYGEN_PCT
|
|
529
535
|
flags << "-D#{prefix}_CONFIG_USE_NATIVE_BACKEND_ARITH" if NATIVE_ARITH
|
|
530
536
|
flags << "-D#{prefix}_CONFIG_USE_NATIVE_BACKEND_FIPS202" if NATIVE_FIPS202
|
|
531
537
|
flags.join(" ")
|
|
@@ -612,6 +618,7 @@ puts "Vendor ASM arch flags: #{VENDOR_ASM_ARCH_FLAGS.empty? ? '(none)' : VENDOR_
|
|
|
612
618
|
if x86_64_host? && (NATIVE_ARITH || NATIVE_FIPS202)
|
|
613
619
|
puts "x86_64 native backend: AVX2 build flags enabled"
|
|
614
620
|
end
|
|
621
|
+
puts "Keygen PCT (FIPS 140-3): #{KEYGEN_PCT ? 'enabled' : 'disabled'}"
|
|
615
622
|
puts "PQClean fallback: removed"
|
|
616
623
|
puts "Output: pqcrypto/pqcrypto_secure"
|
|
617
624
|
puts "===================================="
|
|
@@ -66,6 +66,41 @@ int pq_mldsa_compute_tr_from_public_key(uint8_t *tr_out, const uint8_t *public_k
|
|
|
66
66
|
return pq_shake256(tr_out, PQ_MLDSA_TRBYTES, public_key, public_key_len);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
#define PQ_DEFINE_SIGNATURE_EXTMU_COMPAT(level, native, sig_bytes) \
|
|
70
|
+
int pq_mldsa##level##_signature_extmu_compat(uint8_t *sig, size_t *siglen, const uint8_t *mu, \
|
|
71
|
+
const uint8_t *sk) { \
|
|
72
|
+
if (sig == NULL || siglen == NULL || mu == NULL || sk == NULL) { \
|
|
73
|
+
return -1; \
|
|
74
|
+
} \
|
|
75
|
+
if (native##_signature_extmu(sig, mu, sk) != 0) { \
|
|
76
|
+
*siglen = 0; \
|
|
77
|
+
return -1; \
|
|
78
|
+
} \
|
|
79
|
+
*siglen = (size_t)(sig_bytes); \
|
|
80
|
+
return 0; \
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
PQ_DEFINE_SIGNATURE_EXTMU_COMPAT(44, pqcr_mldsa44, MLDSA44_BYTES)
|
|
84
|
+
PQ_DEFINE_SIGNATURE_EXTMU_COMPAT(65, pqcr_mldsa65, MLDSA65_BYTES)
|
|
85
|
+
PQ_DEFINE_SIGNATURE_EXTMU_COMPAT(87, pqcr_mldsa87, MLDSA87_BYTES)
|
|
86
|
+
|
|
87
|
+
#undef PQ_DEFINE_SIGNATURE_EXTMU_COMPAT
|
|
88
|
+
|
|
89
|
+
#define PQ_DEFINE_VERIFY_EXTMU_COMPAT(level, native, sig_bytes) \
|
|
90
|
+
int pq_mldsa##level##_verify_extmu_compat(const uint8_t *sig, size_t siglen, \
|
|
91
|
+
const uint8_t *mu, const uint8_t *pk) { \
|
|
92
|
+
if (sig == NULL || mu == NULL || pk == NULL || siglen != (size_t)(sig_bytes)) { \
|
|
93
|
+
return -1; \
|
|
94
|
+
} \
|
|
95
|
+
return native##_verify_extmu(sig, mu, pk); \
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
PQ_DEFINE_VERIFY_EXTMU_COMPAT(44, pqcr_mldsa44, MLDSA44_BYTES)
|
|
99
|
+
PQ_DEFINE_VERIFY_EXTMU_COMPAT(65, pqcr_mldsa65, MLDSA65_BYTES)
|
|
100
|
+
PQ_DEFINE_VERIFY_EXTMU_COMPAT(87, pqcr_mldsa87, MLDSA87_BYTES)
|
|
101
|
+
|
|
102
|
+
#undef PQ_DEFINE_VERIFY_EXTMU_COMPAT
|
|
103
|
+
|
|
69
104
|
int pq_sign_mu(uint8_t *signature, size_t *signature_len, const uint8_t *mu,
|
|
70
105
|
const uint8_t *secret_key,
|
|
71
106
|
int (*signature_extmu)(uint8_t *, size_t *, const uint8_t *, const uint8_t *)) {
|
|
@@ -15,49 +15,62 @@
|
|
|
15
15
|
* MLD_CONFIG_NAMESPACE_PREFIX=pqcr_mldsa
|
|
16
16
|
* and MLK/MLD_CONFIG_MULTILEVEL_BUILD enabled. Do not add PQClean aliases here:
|
|
17
17
|
* we want one backend only so build/runtime failures point at the new stack.
|
|
18
|
+
*
|
|
19
|
+
* mldsa-native v2.0.0 removed the `siglen` in/out parameter from the signing
|
|
20
|
+
* and verification APIs: signatures are always exactly MLDSA_BYTES(level) and
|
|
21
|
+
* the buffer size is part of the API contract rather than a runtime value.
|
|
22
|
+
*
|
|
23
|
+
* Two consequences for every caller in this extension:
|
|
24
|
+
* 1. Signing no longer reports the produced length. Each pq_crypto wrapper
|
|
25
|
+
* sets its own *signature_len from the compile-time constant, and clears
|
|
26
|
+
* it to 0 on failure, preserving the pre-v2 pq_crypto contract.
|
|
27
|
+
* 2. Verification no longer rejects a wrongly sized signature. The caller
|
|
28
|
+
* MUST check the length itself before handing the buffer to
|
|
29
|
+
* pqcr_mldsa*_verify / pqcr_mldsa*_verify_extmu, which read
|
|
30
|
+
* MLDSA_BYTES(level) bytes unconditionally.
|
|
18
31
|
*/
|
|
19
32
|
|
|
20
|
-
#define MLKEM512_SECRETKEYBYTES
|
|
21
|
-
#define MLKEM512_PUBLICKEYBYTES
|
|
22
|
-
#define MLKEM512_CIPHERTEXTBYTES
|
|
23
|
-
#define MLKEM512_SHAREDSECRETBYTES
|
|
33
|
+
#define MLKEM512_SECRETKEYBYTES 1632
|
|
34
|
+
#define MLKEM512_PUBLICKEYBYTES 800
|
|
35
|
+
#define MLKEM512_CIPHERTEXTBYTES 768
|
|
36
|
+
#define MLKEM512_SHAREDSECRETBYTES 32
|
|
24
37
|
|
|
25
|
-
#define MLKEM768_SECRETKEYBYTES
|
|
26
|
-
#define MLKEM768_PUBLICKEYBYTES
|
|
27
|
-
#define MLKEM768_CIPHERTEXTBYTES
|
|
28
|
-
#define MLKEM768_SHAREDSECRETBYTES
|
|
38
|
+
#define MLKEM768_SECRETKEYBYTES 2400
|
|
39
|
+
#define MLKEM768_PUBLICKEYBYTES 1184
|
|
40
|
+
#define MLKEM768_CIPHERTEXTBYTES 1088
|
|
41
|
+
#define MLKEM768_SHAREDSECRETBYTES 32
|
|
29
42
|
|
|
30
43
|
#define MLKEM1024_SECRETKEYBYTES 3168
|
|
31
44
|
#define MLKEM1024_PUBLICKEYBYTES 1568
|
|
32
45
|
#define MLKEM1024_CIPHERTEXTBYTES 1568
|
|
33
46
|
#define MLKEM1024_SHAREDSECRETBYTES 32
|
|
34
47
|
|
|
35
|
-
#define MLKEM_PUBLICKEYBYTES
|
|
36
|
-
#define MLKEM_SECRETKEYBYTES
|
|
37
|
-
#define MLKEM_CIPHERTEXTBYTES
|
|
38
|
-
#define MLKEM_SHAREDSECRETBYTES
|
|
39
|
-
|
|
40
|
-
#define MLDSA44_SECRETKEYBYTES
|
|
41
|
-
#define MLDSA44_PUBLICKEYBYTES
|
|
42
|
-
#define MLDSA44_BYTES
|
|
43
|
-
|
|
44
|
-
#define MLDSA65_SECRETKEYBYTES
|
|
45
|
-
#define MLDSA65_PUBLICKEYBYTES
|
|
46
|
-
#define MLDSA65_BYTES
|
|
47
|
-
|
|
48
|
-
#define MLDSA87_SECRETKEYBYTES
|
|
49
|
-
#define MLDSA87_PUBLICKEYBYTES
|
|
50
|
-
#define MLDSA87_BYTES
|
|
51
|
-
|
|
52
|
-
#define MLDSA_PUBLICKEYBYTES
|
|
53
|
-
#define MLDSA_SECRETKEYBYTES
|
|
54
|
-
#define MLDSA_BYTES
|
|
55
|
-
#define MLDSA_SEEDBYTES
|
|
56
|
-
#define MLDSA_RNDBYTES
|
|
57
|
-
#define MLDSA_TRBYTES
|
|
58
|
-
#define MLDSA_CRHBYTES
|
|
48
|
+
#define MLKEM_PUBLICKEYBYTES MLKEM768_PUBLICKEYBYTES
|
|
49
|
+
#define MLKEM_SECRETKEYBYTES MLKEM768_SECRETKEYBYTES
|
|
50
|
+
#define MLKEM_CIPHERTEXTBYTES MLKEM768_CIPHERTEXTBYTES
|
|
51
|
+
#define MLKEM_SHAREDSECRETBYTES MLKEM768_SHAREDSECRETBYTES
|
|
52
|
+
|
|
53
|
+
#define MLDSA44_SECRETKEYBYTES 2560
|
|
54
|
+
#define MLDSA44_PUBLICKEYBYTES 1312
|
|
55
|
+
#define MLDSA44_BYTES 2420
|
|
56
|
+
|
|
57
|
+
#define MLDSA65_SECRETKEYBYTES 4032
|
|
58
|
+
#define MLDSA65_PUBLICKEYBYTES 1952
|
|
59
|
+
#define MLDSA65_BYTES 3309
|
|
60
|
+
|
|
61
|
+
#define MLDSA87_SECRETKEYBYTES 4896
|
|
62
|
+
#define MLDSA87_PUBLICKEYBYTES 2592
|
|
63
|
+
#define MLDSA87_BYTES 4627
|
|
64
|
+
|
|
65
|
+
#define MLDSA_PUBLICKEYBYTES MLDSA65_PUBLICKEYBYTES
|
|
66
|
+
#define MLDSA_SECRETKEYBYTES MLDSA65_SECRETKEYBYTES
|
|
67
|
+
#define MLDSA_BYTES MLDSA65_BYTES
|
|
68
|
+
#define MLDSA_SEEDBYTES 32
|
|
69
|
+
#define MLDSA_RNDBYTES 32
|
|
70
|
+
#define MLDSA_TRBYTES 64
|
|
71
|
+
#define MLDSA_CRHBYTES 64
|
|
59
72
|
#define MLDSA_DOMAIN_SEPARATION_MAX_BYTES (2 + 255 + 11 + 64)
|
|
60
|
-
#define MLDSA_PREHASH_NONE
|
|
73
|
+
#define MLDSA_PREHASH_NONE 0
|
|
61
74
|
|
|
62
75
|
/* mlkem-native symbols: namespace prefix pqcr_mlkem + level suffix. */
|
|
63
76
|
int pqcr_mlkem512_keypair(uint8_t *pk, uint8_t *sk);
|
|
@@ -65,18 +78,24 @@ int pqcr_mlkem512_keypair_derand(uint8_t *pk, uint8_t *sk, const uint8_t *coins)
|
|
|
65
78
|
int pqcr_mlkem512_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk);
|
|
66
79
|
int pqcr_mlkem512_enc_derand(uint8_t *ct, uint8_t *ss, const uint8_t *pk, const uint8_t *coins);
|
|
67
80
|
int pqcr_mlkem512_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk);
|
|
81
|
+
int pqcr_mlkem512_check_pk(const uint8_t *pk);
|
|
82
|
+
int pqcr_mlkem512_check_sk(const uint8_t *sk);
|
|
68
83
|
|
|
69
84
|
int pqcr_mlkem768_keypair(uint8_t *pk, uint8_t *sk);
|
|
70
85
|
int pqcr_mlkem768_keypair_derand(uint8_t *pk, uint8_t *sk, const uint8_t *coins);
|
|
71
86
|
int pqcr_mlkem768_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk);
|
|
72
87
|
int pqcr_mlkem768_enc_derand(uint8_t *ct, uint8_t *ss, const uint8_t *pk, const uint8_t *coins);
|
|
73
88
|
int pqcr_mlkem768_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk);
|
|
89
|
+
int pqcr_mlkem768_check_pk(const uint8_t *pk);
|
|
90
|
+
int pqcr_mlkem768_check_sk(const uint8_t *sk);
|
|
74
91
|
|
|
75
92
|
int pqcr_mlkem1024_keypair(uint8_t *pk, uint8_t *sk);
|
|
76
93
|
int pqcr_mlkem1024_keypair_derand(uint8_t *pk, uint8_t *sk, const uint8_t *coins);
|
|
77
94
|
int pqcr_mlkem1024_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk);
|
|
78
95
|
int pqcr_mlkem1024_enc_derand(uint8_t *ct, uint8_t *ss, const uint8_t *pk, const uint8_t *coins);
|
|
79
96
|
int pqcr_mlkem1024_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk);
|
|
97
|
+
int pqcr_mlkem1024_check_pk(const uint8_t *pk);
|
|
98
|
+
int pqcr_mlkem1024_check_sk(const uint8_t *sk);
|
|
80
99
|
|
|
81
100
|
void pqcr_mlkem_shake256(uint8_t *output, size_t outlen, const uint8_t *input, size_t inlen);
|
|
82
101
|
void pqcr_mlkem_sha3_256(uint8_t *output, const uint8_t *input, size_t inlen);
|
|
@@ -84,58 +103,55 @@ void pqcr_mlkem_sha3_256(uint8_t *output, const uint8_t *input, size_t inlen);
|
|
|
84
103
|
/* mldsa-native symbols: namespace prefix pqcr_mldsa + level suffix. */
|
|
85
104
|
int pqcr_mldsa44_keypair(uint8_t *pk, uint8_t *sk);
|
|
86
105
|
int pqcr_mldsa44_keypair_internal(uint8_t *pk, uint8_t *sk, const uint8_t seed[MLDSA_SEEDBYTES]);
|
|
87
|
-
int pqcr_mldsa44_signature(uint8_t *sig,
|
|
88
|
-
|
|
89
|
-
int pqcr_mldsa44_signature_internal(uint8_t *sig,
|
|
90
|
-
size_t
|
|
91
|
-
const uint8_t
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
int pqcr_mldsa44_signature_extmu(uint8_t *sig,
|
|
99
|
-
|
|
100
|
-
int pqcr_mldsa44_verify_extmu(const uint8_t *sig, size_t siglen, const uint8_t mu[MLDSA_CRHBYTES],
|
|
106
|
+
int pqcr_mldsa44_signature(uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *ctx,
|
|
107
|
+
size_t ctxlen, const uint8_t *sk);
|
|
108
|
+
int pqcr_mldsa44_signature_internal(uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *pre,
|
|
109
|
+
size_t prelen, const uint8_t rnd[MLDSA_RNDBYTES],
|
|
110
|
+
const uint8_t *sk, int externalmu);
|
|
111
|
+
int pqcr_mldsa44_verify(const uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *ctx,
|
|
112
|
+
size_t ctxlen, const uint8_t *pk);
|
|
113
|
+
size_t
|
|
114
|
+
pqcr_mldsa44_prepare_domain_separation_prefix(uint8_t prefix[MLDSA_DOMAIN_SEPARATION_MAX_BYTES],
|
|
115
|
+
const uint8_t *ph, size_t phlen, const uint8_t *ctx,
|
|
116
|
+
size_t ctxlen, int hashalg);
|
|
117
|
+
int pqcr_mldsa44_signature_extmu(uint8_t *sig, const uint8_t mu[MLDSA_CRHBYTES], const uint8_t *sk);
|
|
118
|
+
int pqcr_mldsa44_verify_extmu(const uint8_t *sig, const uint8_t mu[MLDSA_CRHBYTES],
|
|
101
119
|
const uint8_t *pk);
|
|
102
120
|
int pqcr_mldsa44_pk_from_sk(uint8_t *pk, const uint8_t *sk);
|
|
103
121
|
|
|
104
122
|
int pqcr_mldsa65_keypair(uint8_t *pk, uint8_t *sk);
|
|
105
123
|
int pqcr_mldsa65_keypair_internal(uint8_t *pk, uint8_t *sk, const uint8_t seed[MLDSA_SEEDBYTES]);
|
|
106
|
-
int pqcr_mldsa65_signature(uint8_t *sig,
|
|
107
|
-
|
|
108
|
-
int pqcr_mldsa65_signature_internal(uint8_t *sig,
|
|
109
|
-
size_t
|
|
110
|
-
const uint8_t
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
int pqcr_mldsa65_signature_extmu(uint8_t *sig,
|
|
118
|
-
|
|
119
|
-
int pqcr_mldsa65_verify_extmu(const uint8_t *sig, size_t siglen, const uint8_t mu[MLDSA_CRHBYTES],
|
|
124
|
+
int pqcr_mldsa65_signature(uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *ctx,
|
|
125
|
+
size_t ctxlen, const uint8_t *sk);
|
|
126
|
+
int pqcr_mldsa65_signature_internal(uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *pre,
|
|
127
|
+
size_t prelen, const uint8_t rnd[MLDSA_RNDBYTES],
|
|
128
|
+
const uint8_t *sk, int externalmu);
|
|
129
|
+
int pqcr_mldsa65_verify(const uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *ctx,
|
|
130
|
+
size_t ctxlen, const uint8_t *pk);
|
|
131
|
+
size_t
|
|
132
|
+
pqcr_mldsa65_prepare_domain_separation_prefix(uint8_t prefix[MLDSA_DOMAIN_SEPARATION_MAX_BYTES],
|
|
133
|
+
const uint8_t *ph, size_t phlen, const uint8_t *ctx,
|
|
134
|
+
size_t ctxlen, int hashalg);
|
|
135
|
+
int pqcr_mldsa65_signature_extmu(uint8_t *sig, const uint8_t mu[MLDSA_CRHBYTES], const uint8_t *sk);
|
|
136
|
+
int pqcr_mldsa65_verify_extmu(const uint8_t *sig, const uint8_t mu[MLDSA_CRHBYTES],
|
|
120
137
|
const uint8_t *pk);
|
|
121
138
|
int pqcr_mldsa65_pk_from_sk(uint8_t *pk, const uint8_t *sk);
|
|
122
139
|
|
|
123
140
|
int pqcr_mldsa87_keypair(uint8_t *pk, uint8_t *sk);
|
|
124
141
|
int pqcr_mldsa87_keypair_internal(uint8_t *pk, uint8_t *sk, const uint8_t seed[MLDSA_SEEDBYTES]);
|
|
125
|
-
int pqcr_mldsa87_signature(uint8_t *sig,
|
|
126
|
-
|
|
127
|
-
int pqcr_mldsa87_signature_internal(uint8_t *sig,
|
|
128
|
-
size_t
|
|
129
|
-
const uint8_t
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
int pqcr_mldsa87_signature_extmu(uint8_t *sig,
|
|
137
|
-
|
|
138
|
-
int pqcr_mldsa87_verify_extmu(const uint8_t *sig, size_t siglen, const uint8_t mu[MLDSA_CRHBYTES],
|
|
142
|
+
int pqcr_mldsa87_signature(uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *ctx,
|
|
143
|
+
size_t ctxlen, const uint8_t *sk);
|
|
144
|
+
int pqcr_mldsa87_signature_internal(uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *pre,
|
|
145
|
+
size_t prelen, const uint8_t rnd[MLDSA_RNDBYTES],
|
|
146
|
+
const uint8_t *sk, int externalmu);
|
|
147
|
+
int pqcr_mldsa87_verify(const uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *ctx,
|
|
148
|
+
size_t ctxlen, const uint8_t *pk);
|
|
149
|
+
size_t
|
|
150
|
+
pqcr_mldsa87_prepare_domain_separation_prefix(uint8_t prefix[MLDSA_DOMAIN_SEPARATION_MAX_BYTES],
|
|
151
|
+
const uint8_t *ph, size_t phlen, const uint8_t *ctx,
|
|
152
|
+
size_t ctxlen, int hashalg);
|
|
153
|
+
int pqcr_mldsa87_signature_extmu(uint8_t *sig, const uint8_t mu[MLDSA_CRHBYTES], const uint8_t *sk);
|
|
154
|
+
int pqcr_mldsa87_verify_extmu(const uint8_t *sig, const uint8_t mu[MLDSA_CRHBYTES],
|
|
139
155
|
const uint8_t *pk);
|
|
140
156
|
int pqcr_mldsa87_pk_from_sk(uint8_t *pk, const uint8_t *sk);
|
|
141
157
|
|