57#if !defined(SIGNET_ENABLE_COMMERCIAL) || !SIGNET_ENABLE_COMMERCIAL
58#error "signet/crypto/post_quantum.hpp requires SIGNET_ENABLE_COMMERCIAL=ON (AGPL-3.0 commercial tier). See LICENSE_COMMERCIAL."
61#if defined(SIGNET_REQUIRE_REAL_PQ) && SIGNET_REQUIRE_REAL_PQ && !defined(SIGNET_HAS_LIBOQS)
62#error "SIGNET_REQUIRE_REAL_PQ=1 forbids bundled PQ stubs. Reconfigure with -DSIGNET_ENABLE_PQ=ON and install liboqs."
65#if !defined(SIGNET_HAS_LIBOQS)
66#pragma message("WARNING: Signet post-quantum crypto is using BUNDLED Kyber/Dilithium STUBS (NOT post-quantum secure). HybridKem X25519 provides real classical ECDH security; only the Kyber-768 lattice portion is a structural placeholder. Build with -DSIGNET_ENABLE_PQ=ON for real post-quantum resistance.")
79#ifdef SIGNET_HAS_LIBOQS
94#ifdef SIGNET_HAS_LIBOQS
101#ifdef SIGNET_HAS_LIBOQS
102#if defined(OQS_KEM_alg_kyber_768)
103inline constexpr const char* kOqsKemAlgMlKem768 = OQS_KEM_alg_kyber_768;
104#elif defined(OQS_KEM_alg_ml_kem_768)
105inline constexpr const char* kOqsKemAlgMlKem768 = OQS_KEM_alg_ml_kem_768;
107#error "liboqs is missing ML-KEM-768/Kyber-768 symbols required by SIGNET_REQUIRE_REAL_PQ"
110#if defined(OQS_SIG_alg_dilithium_3)
111inline constexpr const char* kOqsSigAlgMlDsa65 = OQS_SIG_alg_dilithium_3;
112#elif defined(OQS_SIG_alg_ml_dsa_65)
113inline constexpr const char* kOqsSigAlgMlDsa65 = OQS_SIG_alg_ml_dsa_65;
115#error "liboqs is missing ML-DSA-65/Dilithium-3 symbols required by SIGNET_REQUIRE_REAL_PQ"
122namespace detail::pq {
134 std::vector<uint8_t> buf(size);
153namespace detail::x25519 {
163#if defined(__GNUC__) || defined(__clang__)
167using u128 =
unsigned __int128;
171using Fe = std::array<uint64_t, 5>;
174inline Fe fe_carry(
Fe a) {
175 constexpr uint64_t MASK51 = (1ULL << 51) - 1;
176 for (
int pass = 0; pass < 2; ++pass) {
178 c = a[0] >> 51; a[0] &= MASK51; a[1] += c;
179 c = a[1] >> 51; a[1] &= MASK51; a[2] += c;
180 c = a[2] >> 51; a[2] &= MASK51; a[3] += c;
181 c = a[3] >> 51; a[3] &= MASK51; a[4] += c;
182 c = a[4] >> 51; a[4] &= MASK51; a[0] += c * 19;
188 for (
int i = 0; i < 5; ++i) a[i] += b[i];
194 a[0] += 0xFFFFFFFFFFFDAULL - b[0];
195 a[1] += 0xFFFFFFFFFFFFEULL - b[1];
196 a[2] += 0xFFFFFFFFFFFFEULL - b[2];
197 a[3] += 0xFFFFFFFFFFFFEULL - b[3];
198 a[4] += 0xFFFFFFFFFFFFEULL - b[4];
205 u128 t0 = (u128)a[0]*b[0] + 19*((u128)a[1]*b[4] + (u128)a[2]*b[3] + (u128)a[3]*b[2] + (u128)a[4]*b[1]);
206 u128 t1 = (u128)a[0]*b[1] + (u128)a[1]*b[0] + 19*((u128)a[2]*b[4] + (u128)a[3]*b[3] + (u128)a[4]*b[2]);
207 u128 t2 = (u128)a[0]*b[2] + (u128)a[1]*b[1] + (u128)a[2]*b[0] + 19*((u128)a[3]*b[4] + (u128)a[4]*b[3]);
208 u128 t3 = (u128)a[0]*b[3] + (u128)a[1]*b[2] + (u128)a[2]*b[1] + (u128)a[3]*b[0] + 19*(u128)a[4]*b[4];
209 u128 t4 = (u128)a[0]*b[4] + (u128)a[1]*b[3] + (u128)a[2]*b[2] + (u128)a[3]*b[1] + (u128)a[4]*b[0];
211 constexpr uint64_t MASK51 = (1ULL << 51) - 1;
214 r[0] = (uint64_t)t0 & MASK51; c = (uint64_t)(t0 >> 51); t1 += c;
215 r[1] = (uint64_t)t1 & MASK51; c = (uint64_t)(t1 >> 51); t2 += c;
216 r[2] = (uint64_t)t2 & MASK51; c = (uint64_t)(t2 >> 51); t3 += c;
217 r[3] = (uint64_t)t3 & MASK51; c = (uint64_t)(t3 >> 51); t4 += c;
218 r[4] = (uint64_t)t4 & MASK51; c = (uint64_t)(t4 >> 51);
220 c = r[0] >> 51; r[0] &= MASK51; r[1] += c;
228 constexpr uint64_t MASK51 = (1ULL << 51) - 1;
229 auto load8 = [&](
int i) -> uint64_t {
231 for (
int j = 0; j < 8 && i+j < 32; ++j)
232 v |= (uint64_t)b[i+j] << (8*j);
236 f[0] = load8( 0) & MASK51;
237 f[1] = (load8( 6) >> 3) & MASK51;
238 f[2] = (load8(12) >> 6) & MASK51;
239 f[3] = (load8(19) >> 1) & MASK51;
240 f[4] = (load8(24) >> 12) & MASK51;
246 constexpr uint64_t MASK51 = (1ULL << 51) - 1;
253 uint64_t g0 = f[0] + 19;
254 uint64_t c = g0 >> 51; g0 &= MASK51;
255 uint64_t g1 = f[1] + c; c = g1 >> 51; g1 &= MASK51;
256 uint64_t g2 = f[2] + c; c = g2 >> 51; g2 &= MASK51;
257 uint64_t g3 = f[3] + c; c = g3 >> 51; g3 &= MASK51;
258 uint64_t g4 = f[4] + c; c = g4 >> 51; g4 &= MASK51;
261 uint64_t mask = 0 - c;
262 f[0] ^= mask & (f[0] ^ g0);
263 f[1] ^= mask & (f[1] ^ g1);
264 f[2] ^= mask & (f[2] ^ g2);
265 f[3] ^= mask & (f[3] ^ g3);
266 f[4] ^= mask & (f[4] ^ g4);
269 const uint64_t w0 = f[0] | ((f[1] & ((1ULL << 13) - 1)) << 51);
270 const uint64_t w1 = (f[1] >> 13) | ((f[2] & ((1ULL << 26) - 1)) << 38);
271 const uint64_t w2 = (f[2] >> 26) | ((f[3] & ((1ULL << 39) - 1)) << 25);
272 const uint64_t w3 = (f[3] >> 39) | (f[4] << 12);
274 auto store8 = [&](uint8_t* p, uint64_t v) {
275 for (
int i = 0; i < 8; ++i) p[i] = static_cast<uint8_t>(v >> (8 * i));
279 store8(out + 16, w2);
280 store8(out + 24, w3);
286 uint64_t mask = 0 - swap;
287 for (
int i = 0; i < 5; ++i) {
288 uint64_t t = mask & (a[i] ^ b[i]);
307 Fe a = t2;
for (
int i = 0; i < 5; ++i) a =
fe_sq(a);
310 Fe b = a;
for (
int i = 0; i < 10; ++i) b =
fe_sq(b);
313 Fe c = b;
for (
int i = 0; i < 20; ++i) c =
fe_sq(c);
316 for (
int i = 0; i < 10; ++i) c =
fe_sq(c);
319 Fe d = c;
for (
int i = 0; i < 50; ++i) d =
fe_sq(d);
322 Fe e = d;
for (
int i = 0; i < 100; ++i) e =
fe_sq(e);
325 for (
int i = 0; i < 50; ++i) e =
fe_sq(e);
328 for (
int i = 0; i < 5; ++i) e =
fe_sq(e);
335using Fe = std::array<int32_t, 10>;
339 auto load4 = [&](
int i) -> int32_t {
340 return (int32_t)b[i] | ((int32_t)b[i+1]<<8) | ((int32_t)b[i+2]<<16) | ((int32_t)b[i+3]<<24);
343 h[0] = load4( 0) & 0x3FFFFFF;
344 h[1] = (load4( 3) >> 2) & 0x1FFFFFF;
345 h[2] = (load4( 6) >> 3) & 0x3FFFFFF;
346 h[3] = (load4( 9) >> 5) & 0x1FFFFFF;
347 h[4] = (load4(12) >> 6) & 0x3FFFFFF;
348 h[5] = load4(16) & 0x1FFFFFF;
349 h[6] = (load4(19) >> 1) & 0x3FFFFFF;
350 h[7] = (load4(22) >> 3) & 0x1FFFFFF;
351 h[8] = (load4(25) >> 4) & 0x3FFFFFF;
352 h[9] = (load4(28) >> 6) & 0x1FFFFFF;
357 for (
int i = 0; i < 10; ++i) {
358 int bits = (i % 2 == 0) ? 26 : 25;
359 int32_t carry = h[i] >> bits;
360 h[i] -= carry << bits;
361 if (i < 9) h[i+1] += carry;
362 else h[0] += carry * 19;
367 for (
int i = 0; i < 10; ++i) a[i] += b[i];
376 int32_t two_p[10] = {
377 2 * (0x3FFFFFF - 18),
388 for (
int i = 0; i < 10; ++i) a[i] = a[i] + two_p[i] - b[i];
404 for (
int i = 0; i < 5; ++i) g19[i] = (int64_t)g[i+5] * 19;
405 for (
int i = 0; i < 5; ++i) {
406 for (
int j = 0; j < 5; ++j) {
407 h[i+j] += (int64_t)f[i] * g[j];
408 h[i+j+5] += (int64_t)f[i] * g[j+5];
409 h[i+j] += (int64_t)f[i+5] * g19[j];
413 for (
int i = 0; i < 10; ++i) r[i] = (int32_t)h[i];
429 int32_t q = (19 * h[0] + (1<<25)) >> 26;
430 for (
int i = 0; i < 9; ++i) {
431 q = h[i] + q * (i%2==0 ? 19 : 1);
432 q >>= (i%2==0 ? 26 : 25);
438 b[0] = (uint32_t)h[0] | ((uint32_t)h[1]<<26);
439 b[1] = ((uint32_t)h[1]>>6) | ((uint32_t)h[2]<<19);
440 b[2] = ((uint32_t)h[2]>>13) | ((uint32_t)h[3]<<13);
441 b[3] = ((uint32_t)h[3]>>19) | ((uint32_t)h[4]<<6);
442 b[4] = (uint32_t)h[5] | ((uint32_t)h[6]<<25);
443 b[5] = ((uint32_t)h[6]>>7) | ((uint32_t)h[7]<<18);
444 b[6] = ((uint32_t)h[7]>>14) | ((uint32_t)h[8]<<11);
445 b[7] = ((uint32_t)h[8]>>21) | ((uint32_t)h[9]<<4);
446 for (
int i = 0; i < 8; ++i) {
447 out[i*4+0] = (uint8_t)(b[i]);
448 out[i*4+1] = (uint8_t)(b[i]>>8);
449 out[i*4+2] = (uint8_t)(b[i]>>16);
450 out[i*4+3] = (uint8_t)(b[i]>>24);
455 int32_t mask = (int32_t)(0 - swap);
456 for (
int i = 0; i < 10; ++i) {
457 int32_t t = mask & (a[i] ^ b[i]);
458 a[i] ^= t; b[i] ^= t;
471 for (
int i=0;i<5;i++) a=
fe_sq(a);
479 for(
int i=0;i<5;i++) e=
fe_sq(e);
491inline std::array<uint8_t, 32>
clamp_scalar(std::array<uint8_t, 32> k) {
502 const std::array<uint8_t, 32>& scalar,
503 const std::array<uint8_t, 32>& point_u)
506 std::array<uint8_t, 32> u_masked = point_u;
507 u_masked[31] &= 0x7F;
513#if defined(__GNUC__) || defined(__clang__)
516 x_2 = {1,0,0,0,0,0,0,0,0,0};
519#if defined(__GNUC__) || defined(__clang__)
522 z_2 = {0,0,0,0,0,0,0,0,0,0};
526#if defined(__GNUC__) || defined(__clang__)
529 z_3 = {1,0,0,0,0,0,0,0,0,0};
534#if defined(__GNUC__) || defined(__clang__)
535 a24 = {121665, 0, 0, 0, 0};
537 a24 = {121665, 0, 0, 0, 0, 0, 0, 0, 0, 0};
543 for (
int i = 254; i >= 0; --i) {
544 uint64_t k_bit = (scalar[i / 8] >> (i % 8)) & 1;
575 std::array<uint8_t, 32> out;
584 const std::array<uint8_t, 32>& scalar,
585 const std::array<uint8_t, 32>& u_coord)
587 auto license = commercial::require_feature(
"PQ x25519");
588 if (!license)
return license.error();
592 std::array<uint8_t, 32> clamped = scalar;
600 for (
size_t i = 0; i < 32; ++i) acc |= result[i];
604 "X25519: degenerate output (all-zero) — invalid input point"};
611 static const std::array<uint8_t, 32> BP = {
612 9,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
613 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0
623 auto license = commercial::require_feature(
"PQ x25519 keypair");
624 if (!license)
return license.error();
626 std::array<uint8_t,32> sk;
630 if (!pk_result)
return pk_result.error();
631 return std::make_pair(sk, *pk_result);
665#if defined(SIGNET_HAS_LIBOQS) && defined(OQS_KEM_ml_kem_768_length_public_key)
666 static constexpr size_t PUBLIC_KEY_SIZE = OQS_KEM_ml_kem_768_length_public_key;
667 static constexpr size_t SECRET_KEY_SIZE = OQS_KEM_ml_kem_768_length_secret_key;
668 static constexpr size_t CIPHERTEXT_SIZE = OQS_KEM_ml_kem_768_length_ciphertext;
670#elif defined(SIGNET_HAS_LIBOQS) && defined(OQS_KEM_kyber_768_length_public_key)
671 static constexpr size_t PUBLIC_KEY_SIZE = OQS_KEM_kyber_768_length_public_key;
672 static constexpr size_t SECRET_KEY_SIZE = OQS_KEM_kyber_768_length_secret_key;
673 static constexpr size_t CIPHERTEXT_SIZE = OQS_KEM_kyber_768_length_ciphertext;
691 for (
size_t i = 0; i <
secret_key.size(); ++i) p[i] = 0;
716 auto license = commercial::require_feature(
"Kyber generate_keypair");
717 if (!license)
return license.error();
719#ifdef SIGNET_HAS_LIBOQS
721 OQS_KEM* kem = OQS_KEM_new(kOqsKemAlgMlKem768);
724 "KyberKem: failed to initialize OQS Kyber-768"};
731 OQS_STATUS rc = OQS_KEM_keypair(kem,
736 if (rc != OQS_SUCCESS) {
738 "KyberKem: OQS keypair generation failed"};
786 const uint8_t* public_key,
size_t pk_size) {
788 auto license = commercial::require_feature(
"Kyber encapsulate");
789 if (!license)
return license.error();
793 "KyberKem: public key must be "
795 + std::to_string(pk_size)};
798#ifdef SIGNET_HAS_LIBOQS
800 OQS_KEM* kem = OQS_KEM_new(kOqsKemAlgMlKem768);
803 "KyberKem: failed to initialize OQS Kyber-768"};
807 result.
ciphertext.resize(kem->length_ciphertext);
810 OQS_STATUS rc = OQS_KEM_encaps(kem,
816 if (rc != OQS_SUCCESS) {
818 "KyberKem: OQS encapsulation failed"};
834#ifndef SIGNET_ALLOW_STUB_PQ
836 "KyberKem: stub PQ implementation provides zero security. "
837 "Build with -DSIGNET_ENABLE_PQ=ON (liboqs) for real Kyber-768, "
838 "or define SIGNET_ALLOW_STUB_PQ to allow the insecure stub."};
844 std::array<uint8_t, 32> seed;
853 seed.data(), seed.size(), pk_hash.data(), pk_hash.size());
861 for (
size_t i = 0; i < 32; ++i) {
868 result.
ciphertext[i] = pk_hash[i % 32] ^
static_cast<uint8_t
>(i);
899 const uint8_t* ciphertext,
size_t ct_size,
900 const uint8_t* secret_key,
size_t sk_size) {
902 auto license = commercial::require_feature(
"Kyber decapsulate");
903 if (!license)
return license.error();
907 "KyberKem: ciphertext must be "
909 + std::to_string(ct_size)};
913 "KyberKem: secret key must be "
915 + std::to_string(sk_size)};
918#ifdef SIGNET_HAS_LIBOQS
920 OQS_KEM* kem = OQS_KEM_new(kOqsKemAlgMlKem768);
923 "KyberKem: failed to initialize OQS Kyber-768"};
926 std::vector<uint8_t> shared_secret(kem->length_shared_secret);
928 OQS_STATUS rc = OQS_KEM_decaps(kem,
929 shared_secret.data(),
934 if (rc != OQS_SUCCESS) {
936 "KyberKem: OQS decapsulation failed"};
939 return shared_secret;
949#ifndef SIGNET_ALLOW_STUB_PQ
951 "KyberKem: stub PQ implementation provides zero security. "
952 "Build with -DSIGNET_ENABLE_PQ=ON (liboqs) for real Kyber-768, "
953 "or define SIGNET_ALLOW_STUB_PQ to allow the insecure stub."};
957 std::array<uint8_t, 32> pk_hash;
961 std::array<uint8_t, 32> seed;
962 for (
size_t i = 0; i < 32; ++i) {
963 seed[i] = ciphertext[i] ^ pk_hash[i];
968 seed.data(), seed.size(), pk_hash.data(), pk_hash.size());
973 return shared_secret;
1006#if defined(SIGNET_HAS_LIBOQS) && defined(OQS_SIG_ml_dsa_65_length_public_key)
1007 static constexpr size_t PUBLIC_KEY_SIZE = OQS_SIG_ml_dsa_65_length_public_key;
1008 static constexpr size_t SECRET_KEY_SIZE = OQS_SIG_ml_dsa_65_length_secret_key;
1010#elif defined(SIGNET_HAS_LIBOQS) && defined(OQS_SIG_dilithium_3_length_public_key)
1011 static constexpr size_t PUBLIC_KEY_SIZE = OQS_SIG_dilithium_3_length_public_key;
1012 static constexpr size_t SECRET_KEY_SIZE = OQS_SIG_dilithium_3_length_secret_key;
1029 for (
size_t i = 0; i <
secret_key.size(); ++i) p[i] = 0;
1047 auto license = commercial::require_feature(
"Dilithium generate_keypair");
1048 if (!license)
return license.error();
1050#ifdef SIGNET_HAS_LIBOQS
1052 OQS_SIG* sig = OQS_SIG_new(kOqsSigAlgMlDsa65);
1055 "DilithiumSign: failed to initialize OQS Dilithium-3"};
1059 kp.
public_key.resize(sig->length_public_key);
1060 kp.
secret_key.resize(sig->length_secret_key);
1062 OQS_STATUS rc = OQS_SIG_keypair(sig,
1067 if (rc != OQS_SUCCESS) {
1069 "DilithiumSign: OQS keypair generation failed"};
1074#ifndef SIGNET_ALLOW_STUB_PQ
1076 "DilithiumSign: stub PQ implementation provides zero security. "
1077 "Install liboqs and rebuild with -DSIGNET_ENABLE_PQ=ON, "
1078 "or define SIGNET_ALLOW_STUB_PQ to allow the insecure stub."};
1104 pk_seed_hash.data(), 32);
1133 const uint8_t* message,
size_t msg_size,
1134 const uint8_t* secret_key,
size_t sk_size) {
1136 auto license = commercial::require_feature(
"Dilithium sign");
1137 if (!license)
return license.error();
1141 "DilithiumSign: secret key must be "
1143 + std::to_string(sk_size)};
1146#ifdef SIGNET_HAS_LIBOQS
1148 OQS_SIG* sig = OQS_SIG_new(kOqsSigAlgMlDsa65);
1151 "DilithiumSign: failed to initialize OQS Dilithium-3"};
1154 std::vector<uint8_t> signature(sig->length_signature);
1157 OQS_STATUS rc = OQS_SIG_sign(sig,
1158 signature.data(), &sig_len,
1163 if (rc != OQS_SUCCESS) {
1165 "DilithiumSign: OQS signing failed"};
1168 signature.resize(sig_len);
1171#ifndef SIGNET_ALLOW_STUB_PQ
1173 "DilithiumSign: stub PQ implementation provides zero security. "
1174 "Install liboqs and rebuild with -DSIGNET_ENABLE_PQ=ON, "
1175 "or define SIGNET_ALLOW_STUB_PQ to allow the insecure stub."};
1188 std::array<uint8_t, 32> pk_seed_hash;
1189 std::memcpy(pk_seed_hash.data(),
1194 pk_seed_hash.data(), pk_seed_hash.size(),
1200 std::memcpy(signature.data(), sig_hash.data(), 32);
1205 signature[i] = sig_hash[i % 32] ^
static_cast<uint8_t
>(i & 0xFF);
1235 const uint8_t* message,
size_t msg_size,
1236 const uint8_t* signature,
size_t sig_size,
1237 const uint8_t* public_key,
size_t pk_size) {
1239 auto license = commercial::require_feature(
"Dilithium verify");
1240 if (!license)
return license.error();
1244 "DilithiumSign: public key must be "
1246 + std::to_string(pk_size)};
1250 "DilithiumSign: invalid signature size "
1251 + std::to_string(sig_size)};
1254#ifdef SIGNET_HAS_LIBOQS
1256 OQS_SIG* sig_ctx = OQS_SIG_new(kOqsSigAlgMlDsa65);
1259 "DilithiumSign: failed to initialize OQS Dilithium-3"};
1262 OQS_STATUS rc = OQS_SIG_verify(sig_ctx,
1264 signature, sig_size,
1266 OQS_SIG_free(sig_ctx);
1268 return (rc == OQS_SUCCESS);
1270#ifndef SIGNET_ALLOW_STUB_PQ
1272 "DilithiumSign: stub PQ implementation provides zero security. "
1273 "Install liboqs and rebuild with -DSIGNET_ENABLE_PQ=ON, "
1274 "or define SIGNET_ALLOW_STUB_PQ to allow the insecure stub."};
1286 pk_seed_hash.data(), pk_seed_hash.size(),
1291 if (sig_size < 32) {
1296 for (
size_t i = 0; i < 32; ++i) {
1297 diff |= signature[i] ^ expected_hash[i];
1379 auto license = commercial::require_feature(
"HybridKem generate_keypair");
1380 if (!license)
return license.error();
1384 if (!kyber_result)
return kyber_result.error();
1392 if (!x25519_kp)
return x25519_kp.error();
1395 hkp.
x25519_public_key.assign(x25519_kp->second.begin(), x25519_kp->second.end());
1421 auto license = commercial::require_feature(
"HybridKem encapsulate");
1422 if (!license)
return license.error();
1428 if (!kyber_result)
return kyber_result.error();
1432 if (!eph_kp)
return eph_kp.error();
1433 const auto& eph_sk = eph_kp->first;
1434 const auto& eph_pk = eph_kp->second;
1439 "HybridKem: recipient X25519 public key must be 32 bytes"};
1441 std::array<uint8_t, 32> recip_x25519_pk;
1442 std::memcpy(recip_x25519_pk.data(),
1446 if (!x25519_ss_result)
return x25519_ss_result.error();
1451 kyber_result->shared_secret.data(),
1452 kyber_result->shared_secret.size(),
1453 x25519_ss_result->data(),
1454 x25519_ss_result->size());
1459 result.
shared_secret.assign(combined.begin(), combined.end());
1487 auto license = commercial::require_feature(
"HybridKem decapsulate");
1488 if (!license)
return license.error();
1496 if (!kyber_ss)
return kyber_ss.error();
1502 "HybridKem: X25519 key sizes must be 32 bytes"};
1504 std::array<uint8_t, 32> recip_sk_arr, eph_pk_arr;
1509 if (!x25519_ss_result)
return x25519_ss_result.error();
1514 kyber_ss->data(), kyber_ss->size(),
1515 x25519_ss_result->data(), x25519_ss_result->size());
1517 return std::vector<uint8_t>(combined.begin(), combined.end());
1594 auto zero_vec = [](std::vector<uint8_t>& v) {
1596 volatile uint8_t* p = v.data();
1597 for (
size_t i = 0; i < v.size(); ++i) p[i] = 0;
Abstract cipher interface, GCM/CTR adapters, CipherFactory, and platform CSPRNG.
Dilithium-3 digital signature scheme (NIST FIPS 204 / ML-DSA-65).
static expected< std::vector< uint8_t > > sign(const uint8_t *message, size_t msg_size, const uint8_t *secret_key, size_t sk_size)
Sign a message with the secret key.
static expected< bool > verify(const uint8_t *message, size_t msg_size, const uint8_t *signature, size_t sig_size, const uint8_t *public_key, size_t pk_size)
Verify a signature against a message and public key.
static constexpr size_t SIGNATURE_MAX_SIZE
Maximum Dilithium-3 signature size (stub default).
static constexpr size_t SECRET_KEY_SIZE
Dilithium-3 secret key size (stub default).
static constexpr size_t PUBLIC_KEY_SIZE
Dilithium-3 public key size (stub default).
static expected< SignKeyPair > generate_keypair()
Generate a Dilithium-3 signing keypair.
Hybrid Key Encapsulation combining Kyber-768 (post-quantum) and X25519 (classical).
static expected< HybridKeyPair > generate_keypair()
Generate a hybrid Kyber-768 + X25519 (RFC 7748) keypair.
static expected< HybridEncapsResult > encapsulate(const HybridKeyPair &recipient_pk)
Hybrid encapsulation: Kyber-768 + X25519 DH key agreement.
static constexpr size_t HYBRID_SHARED_SECRET_SIZE
Combined shared secret size (SHA-256 output).
static constexpr size_t X25519_PUBLIC_KEY_SIZE
X25519 public key size in bytes.
static constexpr size_t X25519_SECRET_KEY_SIZE
X25519 secret key size in bytes.
static expected< std::vector< uint8_t > > decapsulate(const HybridEncapsResult &encaps, const HybridKeyPair &recipient_sk)
Hybrid decapsulation: recovers the same shared secret as encapsulate().
Kyber-768 Key Encapsulation Mechanism (NIST FIPS 203 / ML-KEM-768).
static constexpr size_t SECRET_KEY_SIZE
Kyber-768 secret key size (stub default).
static expected< KeyPair > generate_keypair()
Generate a Kyber-768 keypair.
static expected< std::vector< uint8_t > > decapsulate(const uint8_t *ciphertext, size_t ct_size, const uint8_t *secret_key, size_t sk_size)
Recover the shared secret from ciphertext + secret key (decapsulation).
static constexpr size_t PUBLIC_KEY_SIZE
Kyber-768 public key size (stub default).
static constexpr size_t CIPHERTEXT_SIZE
Kyber-768 ciphertext size (stub default).
static expected< EncapsulationResult > encapsulate(const uint8_t *public_key, size_t pk_size)
Generate a shared secret from a recipient's public key (encapsulation).
static constexpr size_t SHARED_SECRET_SIZE
Shared secret size (256 bits, for AES-256).
A lightweight result type that holds either a success value of type T or an Error.
void fill_random_bytes(uint8_t *buf, size_t size)
Fill a buffer with cryptographically random bytes using the best available OS-level CSPRNG (CWE-338: ...
void random_bytes(uint8_t *buf, size_t size)
Fill a buffer with cryptographically random bytes.
std::array< uint8_t, 32 > sha256_concat(const uint8_t *a, size_t a_size, const uint8_t *b, size_t b_size)
Hash concatenation of two byte spans with domain separation: SHA-256(label || a || b).
std::array< uint8_t, 32 > sha256(const uint8_t *data, size_t size)
Compute SHA-256 hash of arbitrary-length input.
std::array< uint8_t, 32 > x25519_raw(const std::array< uint8_t, 32 > &scalar, const std::array< uint8_t, 32 > &point_u)
X25519 scalar multiplication: result = scalar * point.
std::array< int32_t, 10 > Fe
GF(2^255-19) field element: 10 limbs, radix 2^25.5 (alternating 26 and 25 bits).
Fe fe_sub(Fe a, const Fe &b)
Fe fe_from_bytes(const uint8_t *b)
Load 32 little-endian bytes into a 10-limb field element.
expected< std::array< uint8_t, 32 > > x25519(const std::array< uint8_t, 32 > &scalar, const std::array< uint8_t, 32 > &u_coord)
Compute X25519(scalar, u_coord).
void fe_cswap(Fe &a, Fe &b, uint64_t swap)
void fe_to_bytes(uint8_t *out, Fe h)
std::array< uint8_t, 32 > clamp_scalar(std::array< uint8_t, 32 > k)
Clamp a 32-byte scalar per RFC 7748 §5.
const std::array< uint8_t, 32 > & base_point()
The X25519 base point u=9, encoded as 32 LE bytes.
Fe fe_add(Fe a, const Fe &b)
Fe fe_mul(const Fe &f, const Fe &g)
expected< std::pair< std::array< uint8_t, 32 >, std::array< uint8_t, 32 > > > generate_keypair()
Generate a new X25519 keypair.
bool is_real_pq_crypto() noexcept
Runtime query: returns true if post-quantum crypto is backed by real liboqs implementations (Kyber-76...
@ ENCRYPTION_ERROR
An encryption or decryption operation failed (bad key, tampered ciphertext, PME error).
SHA-256 hash function (NIST FIPS 180-4).
Lightweight error value carrying an ErrorCode and a human-readable message.
Dilithium-3 signing keypair: public key for verification, secret key for signing.
std::vector< uint8_t > public_key
PUBLIC_KEY_SIZE bytes.
std::vector< uint8_t > secret_key
SECRET_KEY_SIZE bytes.
~SignKeyPair()
Zeroing destructor (CWE-244: heap inspection).
Result of hybrid encapsulation.
std::vector< uint8_t > x25519_public_key
Ephemeral X25519 public key (32 bytes, sent to recipient).
std::vector< uint8_t > kyber_ciphertext
Kyber ciphertext (1088 bytes, sent to recipient).
std::vector< uint8_t > shared_secret
32 bytes = SHA-256(kyber_ss || x25519_ss).
Hybrid keypair: Kyber-768 + X25519 components.
std::vector< uint8_t > x25519_secret_key
X25519 clamped secret scalar (32 bytes).
~HybridKeyPair()
Zeroing destructor (CWE-244: heap inspection).
std::vector< uint8_t > kyber_secret_key
Kyber-768 secret key (2400 bytes).
std::vector< uint8_t > kyber_public_key
Kyber-768 public key (1184 bytes).
std::vector< uint8_t > x25519_public_key
X25519 public key (32 bytes).
Result of Kyber-768 encapsulation: ciphertext to send + shared secret to keep.
std::vector< uint8_t > ciphertext
CIPHERTEXT_SIZE bytes (sent to recipient).
std::vector< uint8_t > shared_secret
32 bytes (used as AES-256 key).
Kyber-768 keypair: public key for encapsulation, secret key for decapsulation.
~KeyPair()
Zeroing destructor (CWE-244: heap inspection, NIST SP 800-38D §8.3).
std::vector< uint8_t > public_key
PUBLIC_KEY_SIZE bytes.
std::vector< uint8_t > secret_key
SECRET_KEY_SIZE bytes.
Configuration for post-quantum encryption in Parquet Modular Encryption.
std::vector< uint8_t > recipient_secret_key
Recipient's Kyber-768 secret key (KyberKem::SECRET_KEY_SIZE bytes).
std::vector< uint8_t > signing_secret_key
Dilithium-3/ML-DSA-65 secret key (DilithiumSign::SECRET_KEY_SIZE bytes).
bool enabled
Master enable for post-quantum features.
std::vector< uint8_t > signing_public_key
Dilithium-3/ML-DSA-65 public key (DilithiumSign::PUBLIC_KEY_SIZE bytes).
~PostQuantumConfig()
Zeroing destructor (CWE-244: heap inspection).
bool hybrid_mode
When true, use hybrid KEM (Kyber-768 + X25519) for key exchange.
std::vector< uint8_t > recipient_public_key
Recipient's Kyber-768 public key (KyberKem::PUBLIC_KEY_SIZE bytes).