(base) lilyana.g@Stefkas-MacBook-Pro new % caffeinate python vpr_all_in_one.py --sizes 2048 4096 --keys 1000 --workers 4 ============================================================================== RSA KEY GENERATION - fair comparison ============================================================================== CPU cores visible: 8 | macOS-14.7.6-x86_64-i386-64bit-Mach-O C sieve kernel: active OpenSSL bundled in `cryptography`: OpenSSL 3.6.5 29 Sep 2026 OpenSSL system baseline (ctypes): OpenSSL 3.6.5 29 Sep 2026 (Library: OpenSSL 3.6.5 29 Sep 2026) Per size: 1000 keys x 4 contenders, 5 interleaved blocks ------------------------------------------------------------------------------ RSA-2048 (vpr FIPS profile uses 5 MR rounds) ------------------------------------------------------------------------------ SERIAL: one process per contender (this table does NOT depend on --workers) contender n mean ms median ms p10-p90 ms keys/s vpr (FIPS rounds) 1000 20.2 17.5 8.7-34.7 49.46 vpr (gmpy2 BPSW+1MR) 1000 20.5 17.7 8.4-36.3 48.83 OpenSSL bundled 1000 39.3 34.6 15.6-71.3 25.44 OpenSSL system 1000 38.0 32.9 13.3-69.5 26.29 Speed ratio = (contender time) / (vpr time); >1 means vpr is faster vpr (FIPS rounds) vs OpenSSL bundled 1.94x (95% CI 1.85-2.04) -> vpr faster vpr (FIPS rounds) vs OpenSSL system 1.88x (95% CI 1.79-1.98) -> vpr faster vpr (gmpy2 BPSW+1MR) vs OpenSSL bundled 1.92x (95% CI 1.83-2.02) -> vpr faster vpr (gmpy2 BPSW+1MR) vs OpenSSL system 1.86x (95% CI 1.77-1.96) -> vpr faster Parallel throughput, 4 worker processes (keys/s, mean of blocks): vpr (FIPS rounds) 130.67 vpr (gmpy2 BPSW+1MR) 156.95 OpenSSL bundled 79.62 OpenSSL system 79.53 ratio vpr(FIPS)/OpenSSL bundled: 1.64x ratio vpr(FIPS)/OpenSSL system: 1.64x ratio vpr(BPSW+1MR)/OpenSSL bundled: 1.97x ratio vpr(BPSW+1MR)/OpenSSL system: 1.97x scaling vpr(FIPS): 130.7 keys/s with 4 workers vs 49.5 serial = 2.64x scaling vpr(BPSW+1MR): 156.9 keys/s with 4 workers vs 48.8 serial = 3.21x Keys saved (test keys, plain text): ./vpr_method_bpsw_2048_single.txt ./openssl_2048_single.txt ./vpr_method_fips_2048_single.txt ./vpr_method_bpsw_2048_parallel.txt ./openssl_2048_parallel.txt ./vpr_method_fips_2048_parallel.txt Verification of 100 keys from [vpr (gmpy2 BPSW+1MR)] (structure + OpenSSL RSA_check_key + sign/verify + OAEP + `openssl rsa -check`): 100 OK, 0 failed Verification of 100 keys from [vpr (FIPS rounds)] (structure + OpenSSL RSA_check_key + sign/verify + OAEP + `openssl rsa -check`): 100 OK, 0 failed ------------------------------------------------------------------------------ RSA-4096 (vpr FIPS profile uses 4 MR rounds) ------------------------------------------------------------------------------ SERIAL: one process per contender (this table does NOT depend on --workers) contender n mean ms median ms p10-p90 ms keys/s vpr (FIPS rounds) 1000 219.4 191.0 83.1-392.5 4.56 vpr (gmpy2 BPSW+1MR) 1000 214.0 188.6 76.3-383.7 4.67 OpenSSL bundled 1000 374.5 311.2 120.2-707.8 2.67 OpenSSL system 1000 373.5 320.8 120.1-703.0 2.68 Speed ratio = (contender time) / (vpr time); >1 means vpr is faster vpr (FIPS rounds) vs OpenSSL bundled 1.71x (95% CI 1.61-1.81) -> vpr faster vpr (FIPS rounds) vs OpenSSL system 1.70x (95% CI 1.61-1.80) -> vpr faster vpr (gmpy2 BPSW+1MR) vs OpenSSL bundled 1.75x (95% CI 1.65-1.84) -> vpr faster vpr (gmpy2 BPSW+1MR) vs OpenSSL system 1.75x (95% CI 1.65-1.84) -> vpr faster Parallel throughput, 4 worker processes (keys/s, mean of blocks): vpr (FIPS rounds) 13.38 vpr (gmpy2 BPSW+1MR) 13.29 OpenSSL bundled 7.86 OpenSSL system 8.13 ratio vpr(FIPS)/OpenSSL bundled: 1.70x ratio vpr(FIPS)/OpenSSL system: 1.65x ratio vpr(BPSW+1MR)/OpenSSL bundled: 1.69x ratio vpr(BPSW+1MR)/OpenSSL system: 1.63x scaling vpr(FIPS): 13.4 keys/s with 4 workers vs 4.6 serial = 2.94x scaling vpr(BPSW+1MR): 13.3 keys/s with 4 workers vs 4.7 serial = 2.85x Keys saved (test keys, plain text): ./vpr_method_bpsw_4096_single.txt ./openssl_4096_single.txt ./vpr_method_fips_4096_single.txt ./vpr_method_bpsw_4096_parallel.txt ./vpr_method_fips_4096_parallel.txt ./openssl_4096_parallel.txt Verification of 100 keys from [vpr (gmpy2 BPSW+1MR)] (structure + OpenSSL RSA_check_key + sign/verify + OAEP + `openssl rsa -check`): 100 OK, 0 failed Verification of 100 keys from [vpr (FIPS rounds)] (structure + OpenSSL RSA_check_key + sign/verify + OAEP + `openssl rsa -check`): 100 OK, 0 failed Notes: timings are single-process wall-clock on this machine only; re-run on the target hardware. Key generation time is random, so compare means with their confidence intervals, not single runs. (base) lilyana.g@Stefkas-MacBook-Pro new % (base) lilyana.g@Stefkas-MacBook-Pro new % (base) lilyana.g@Stefkas-MacBook-Pro new % (base) lilyana.g@Stefkas-MacBook-Pro new % caffeinate python3 rsa_key_audit.py \ --vpr-fips-files vpr_method_fips_2048_single.txt vpr_method_fips_2048_parallel.txt \ --vpr-bpsw-files vpr_method_bpsw_2048_single.txt vpr_method_bpsw_2048_parallel.txt \ --openssl-files openssl_2048_single.txt openssl_2048_parallel.txt \ --sidechannel ==================================================================================================== 0. Environment and scope ==================================================================================================== python=3.13.2; platform=macOS-14.7.6-x86_64-i386-64bit-Mach-O; cpus=8; gmpy2=2.2.1; cryptography=45.0.3; openssl=OpenSSL 3.6.5 29 Sep 2026 The audit is a smoke test: not detecting a defect is NOT proof of security. ==================================================================================================== 0a. Loading already generated keys from files ( ) ==================================================================================================== ✅ [L01] [VPR-FIPS] vpr_method_fips_2048_single.txt: loaded 1000 keys - all blocks intact ✅ [L01] [VPR-FIPS] vpr_method_fips_2048_parallel.txt: loaded 1000 keys - all blocks intact ✅ [L01] [VPR-BPSW] vpr_method_bpsw_2048_single.txt: loaded 1000 keys - all blocks intact ✅ [L01] [VPR-BPSW] vpr_method_bpsw_2048_parallel.txt: loaded 1000 keys - all blocks intact ✅ [L01] [OpenSSL] openssl_2048_single.txt: loaded 1000 keys - all blocks intact ✅ [L01] [OpenSSL] openssl_2048_parallel.txt: loaded 1000 keys - all blocks intact RSA-2048; keys: VPR-FIPS=2000, VPR-BPSW=2000, OpenSSL=2000; alpha=0.001; MR rounds=5 (FIPS 186-5 Table B.1) ==================================================================================================== 3. Source of randomness - static AST audit of the module (not proof) ==================================================================================================== ⏭️ [R01] static audit - no --vpr-module supplied ==================================================================================================== 1. [VPR-FIPS] Structural checks on 2000 keys (RSA-2048) ==================================================================================================== ✅ [K01] p != q - 2000/2000 ✅ [K02] n = p*q - 2000/2000 ✅ [K03] exact length: p,q = b bits; n = 2b bits - 2000/2000 ✅ [K04] p,q >= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 2000/2000 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 2000/2000 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 2000/2000 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 2000/2000 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 2000/2000 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 2000/2000 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 2000/2000 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 2000/2000 ✅ [K12] Pollard p-1 does not factor n - 2000/2000 ✅ [K13] no ROCA-like fingerprint - 2000/2000 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 2000/2000 ==================================================================================================== 2. [VPR-FIPS] Population tests on 2000 keys (4000 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 1999000 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.0197 (r=13), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.483 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=0.248; chi2 p=0.177; single position detectable at deviation from 0.5 above ~0.039 (N=4000) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=0.328 ✅ [T03] [VPR-FIPS:vpr_method_fips_2048_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(p) rho=-0.020 p=0.525; threshold 2.0e-04; detectable |rho| >~0.12 at N=1000 ✅ [T03] [VPR-FIPS:vpr_method_fips_2048_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(n) rho=+0.035 p=0.272; threshold 2.0e-04; detectable |rho| >~0.12 at N=1000 ✅ [R03] [VPR-FIPS] vpr_method_fips_2048_single.txt: 1000 keys - no duplicates and no shared factors - none ✅ [R03] [VPR-FIPS] vpr_method_fips_2048_parallel.txt: 1000 keys - no duplicates and no shared factors - none ==================================================================================================== 1. [VPR-BPSW] Structural checks on 2000 keys (RSA-2048) ==================================================================================================== ✅ [K01] p != q - 2000/2000 ✅ [K02] n = p*q - 2000/2000 ✅ [K03] exact length: p,q = b bits; n = 2b bits - 2000/2000 ✅ [K04] p,q >= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 2000/2000 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 2000/2000 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 2000/2000 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 2000/2000 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 2000/2000 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 2000/2000 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 2000/2000 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 2000/2000 ✅ [K12] Pollard p-1 does not factor n - 2000/2000 ✅ [K13] no ROCA-like fingerprint - 2000/2000 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 2000/2000 ==================================================================================================== 2. [VPR-BPSW] Population tests on 2000 keys (4000 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 1999000 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.0924 (r=23), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.961 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=1; chi2 p=0.462; single position detectable at deviation from 0.5 above ~0.039 (N=4000) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=0.367 ✅ [T03] [VPR-BPSW:vpr_method_bpsw_2048_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: q mod 2^16 rho=-0.016 p=0.604; threshold 2.0e-04; detectable |rho| >~0.12 at N=1000 ✅ [T03] [VPR-BPSW:vpr_method_bpsw_2048_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: p mod 2^16 rho=+0.079 p=0.0129; threshold 2.0e-04; detectable |rho| >~0.12 at N=1000 ✅ [R03] [VPR-BPSW] vpr_method_bpsw_2048_single.txt: 1000 keys - no duplicates and no shared factors - none ✅ [R03] [VPR-BPSW] vpr_method_bpsw_2048_parallel.txt: 1000 keys - no duplicates and no shared factors - none ==================================================================================================== 1. [OpenSSL] Structural checks on 2000 keys (RSA-2048) ==================================================================================================== ✅ [K01] p != q - 2000/2000 ✅ [K02] n = p*q - 2000/2000 ✅ [K03] exact length: p,q = b bits; n = 2b bits - 2000/2000 ✅ [K04] p,q >= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 2000/2000 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 2000/2000 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 2000/2000 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 2000/2000 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 2000/2000 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 2000/2000 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 2000/2000 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 2000/2000 ✅ [K12] Pollard p-1 does not factor n - 2000/2000 ✅ [K13] no ROCA-like fingerprint - 2000/2000 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 2000/2000 ==================================================================================================== 2. [OpenSSL] Population tests on 2000 keys (4000 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 1999000 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.0639 (r=19), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.381 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=1; chi2 p=0.556; single position detectable at deviation from 0.5 above ~0.039 (N=4000) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=0.608 ✅ [T03] [OpenSSL:openssl_2048_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: q mod 2^16 rho=-0.051 p=0.105; threshold 2.0e-04; detectable |rho| >~0.12 at N=1000 ✅ [T03] [OpenSSL:openssl_2048_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: p mod 2^16 rho=+0.043 p=0.175; threshold 2.0e-04; detectable |rho| >~0.12 at N=1000 ✅ [R03] [OpenSSL] openssl_2048_single.txt: 1000 keys - no duplicates and no shared factors - none ✅ [R03] [OpenSSL] openssl_2048_parallel.txt: 1000 keys - no duplicates and no shared factors - none ==================================================================================================== 2a. Cross-check between generators ==================================================================================================== ✅ [P06] shared primes between keys of DIFFERENT generators - none ==================================================================================================== 2b. Bias in prime selection (distribution of the gap before the prime) ==================================================================================================== generating 400 reference uniform primes (1024 bits, MR rounds=5)... ✅ [B00] calibration: two independent halves of the reference (expected no difference) - KS p=0.866 ✅ [B01] [VPR-FIPS] mean gap before prime / reference - ratio 1.088 (95% CI 0.943-1.255); KS p=0.211; MWU p=0.103; N=400; minimum detectable effect ~±14% ✅ [B01] [VPR-BPSW] mean gap before prime / reference - ratio 1.044 (95% CI 0.908-1.201); KS p=0.7; MWU p=0.36; N=400; minimum detectable effect ~±14% ✅ [B01] [OpenSSL] mean gap before prime / reference - ratio 1.090 (95% CI 0.935-1.268); KS p=0.181; MWU p=0.272; N=400; minimum detectable effect ~±14% ==================================================================================================== 4. Measurement calibration (mandatory before trusting 4a) ==================================================================================================== ✅ [T00a] null control: identical distributions (should NOT be flagged) - max|t|=1.76; noise CV=4.3% ✅ [T00b] positive control: synthetic leak ~100% (should be flagged) - max|t|=712.00; diff +67.001% ==================================================================================================== 4a. [VPR-FIPS] Timing leak on private-key operations (dudect-style, 2000 measurements/pair) ==================================================================================================== ✅ [T01] gmpy2.powmod (plain, no CRT) | fixed vs random - raw t=0.10; crop max t=0.78; diff -0.013%; noise CV=2.8%; detectable effect >~0.57% ⚠️ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=0.17; crop max t=6.87; diff +0.061%; noise CV=8.1%; detectable effect >~1.68% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=0.97; crop max t=2.71; diff -0.291%; noise CV=6.7%; detectable effect >~1.36% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=0.00; crop max t=1.45; diff +0.001%; noise CV=3.7%; detectable effect >~0.76% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=0.31; crop max t=2.74; diff -0.183%; noise CV=13.4%; detectable effect >~2.70% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (~0.62% ✅ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=0.58; crop max t=4.34; diff -0.137%; noise CV=5.3%; detectable effect >~1.07% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=0.80; crop max t=1.38; diff +0.259%; noise CV=7.2%; detectable effect >~1.45% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=0.37; crop max t=1.63; diff -0.036%; noise CV=2.2%; detectable effect >~0.45% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=0.21; crop max t=0.77; diff -0.140%; noise CV=15.2%; detectable effect >~3.10% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (~0.66% ⚠️ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=1.68; crop max t=4.51; diff +0.329%; noise CV=4.4%; detectable effect >~0.88% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=1.25; crop max t=1.81; diff +0.241%; noise CV=4.3%; detectable effect >~0.88% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=0.33; crop max t=2.29; diff +0.037%; noise CV=2.5%; detectable effect >~0.51% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=1.44; crop max t=1.97; diff +0.797%; noise CV=12.4%; detectable effect >~2.51% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 2000/2000 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 2000/2000 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 2000/2000 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 2000/2000 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 2000/2000 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 2000/2000 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 2000/2000 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 2000/2000 ✅ [K12] Pollard p-1 does not factor n - 2000/2000 ✅ [K13] no ROCA-like fingerprint - 2000/2000 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 2000/2000 ==================================================================================================== 2. [VPR-FIPS] Population tests on 2000 keys (4000 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 1999000 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.0512 (r=23), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.268 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=1; chi2 p=0.389; single position detectable at deviation from 0.5 above ~0.040 (N=4000) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=0.211 ✅ [T03] [VPR-FIPS:vpr_method_fips_4096_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(q) rho=-0.059 p=0.063; threshold 2.0e-04; detectable |rho| >~0.12 at N=1000 ✅ [T03] [VPR-FIPS:vpr_method_fips_4096_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(n) rho=-0.068 p=0.0319; threshold 2.0e-04; detectable |rho| >~0.12 at N=1000 ✅ [R03] [VPR-FIPS] vpr_method_fips_4096_single.txt: 1000 keys - no duplicates and no shared factors - none ✅ [R03] [VPR-FIPS] vpr_method_fips_4096_parallel.txt: 1000 keys - no duplicates and no shared factors - none ==================================================================================================== 1. [VPR-BPSW] Structural checks on 2000 keys (RSA-4096) ==================================================================================================== ✅ [K01] p != q - 2000/2000 ✅ [K02] n = p*q - 2000/2000 ✅ [K03] exact length: p,q = b bits; n = 2b bits - 2000/2000 ✅ [K04] p,q >= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 2000/2000 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 2000/2000 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 2000/2000 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 2000/2000 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 2000/2000 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 2000/2000 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 2000/2000 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 2000/2000 ✅ [K12] Pollard p-1 does not factor n - 2000/2000 ✅ [K13] no ROCA-like fingerprint - 2000/2000 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 2000/2000 ==================================================================================================== 2. [VPR-BPSW] Population tests on 2000 keys (4000 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 1999000 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.0592 (r=17), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.522 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=1; chi2 p=0.734; single position detectable at deviation from 0.5 above ~0.040 (N=4000) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=0.225 ✅ [T03] [VPR-BPSW:vpr_method_bpsw_4096_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(n) rho=-0.072 p=0.0225; threshold 2.0e-04; detectable |rho| >~0.12 at N=1000 ✅ [T03] [VPR-BPSW:vpr_method_bpsw_4096_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(p) rho=-0.028 p=0.373; threshold 2.0e-04; detectable |rho| >~0.12 at N=1000 ✅ [R03] [VPR-BPSW] vpr_method_bpsw_4096_single.txt: 1000 keys - no duplicates and no shared factors - none ✅ [R03] [VPR-BPSW] vpr_method_bpsw_4096_parallel.txt: 1000 keys - no duplicates and no shared factors - none ==================================================================================================== 1. [OpenSSL] Structural checks on 2000 keys (RSA-4096) ==================================================================================================== ✅ [K01] p != q - 2000/2000 ✅ [K02] n = p*q - 2000/2000 ✅ [K03] exact length: p,q = b bits; n = 2b bits - 2000/2000 ✅ [K04] p,q >= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 2000/2000 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 2000/2000 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 2000/2000 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 2000/2000 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 2000/2000 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 2000/2000 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 2000/2000 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 2000/2000 ✅ [K12] Pollard p-1 does not factor n - 2000/2000 ✅ [K13] no ROCA-like fingerprint - 2000/2000 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 2000/2000 ==================================================================================================== 2. [OpenSSL] Population tests on 2000 keys (4000 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 1999000 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.23 (r=11), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.774 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=1; chi2 p=0.5; single position detectable at deviation from 0.5 above ~0.040 (N=4000) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=0.774 ✅ [T03] [OpenSSL:openssl_4096_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: q mod 2^16 rho=+0.062 p=0.049; threshold 2.0e-04; detectable |rho| >~0.12 at N=1000 ✅ [T03] [OpenSSL:openssl_4096_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(n) rho=-0.070 p=0.0271; threshold 2.0e-04; detectable |rho| >~0.12 at N=1000 ✅ [R03] [OpenSSL] openssl_4096_single.txt: 1000 keys - no duplicates and no shared factors - none ✅ [R03] [OpenSSL] openssl_4096_parallel.txt: 1000 keys - no duplicates and no shared factors - none ==================================================================================================== 2a. Cross-check between generators ==================================================================================================== ✅ [P06] shared primes between keys of DIFFERENT generators - none ==================================================================================================== 2b. Bias in prime selection (distribution of the gap before the prime) ==================================================================================================== generating 400 reference uniform primes (2048 bits, MR rounds=4)... ✅ [B00] calibration: two independent halves of the reference (expected no difference) - KS p=0.924 ✅ [B01] [VPR-FIPS] mean gap before prime / reference - ratio 1.145 (95% CI 1.002-1.311); KS p=0.468; MWU p=0.277; N=400; minimum detectable effect ~±14% ✅ [B01] [VPR-BPSW] mean gap before prime / reference - ratio 1.073 (95% CI 0.940-1.228); KS p=0.907; MWU p=0.838; N=400; minimum detectable effect ~±14% ✅ [B01] [OpenSSL] mean gap before prime / reference - ratio 0.962 (95% CI 0.845-1.104); KS p=0.416; MWU p=0.35; N=400; minimum detectable effect ~±14% ==================================================================================================== 4. Measurement calibration (mandatory before trusting 4a) ==================================================================================================== ✅ [T00a] null control: identical distributions (should NOT be flagged) - max|t|=1.69; noise CV=4.3% ✅ [T00b] positive control: synthetic leak ~100% (should be flagged) - max|t|=969.93; diff +67.100% ==================================================================================================== 4a. [VPR-FIPS] Timing leak on private-key operations (dudect-style, 2000 measurements/pair) ==================================================================================================== ✅ [T01] gmpy2.powmod (plain, no CRT) | fixed vs random - raw t=0.39; crop max t=1.71; diff +0.046%; noise CV=2.7%; detectable effect >~0.54% ✅ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=0.32; crop max t=1.99; diff +0.049%; noise CV=3.4%; detectable effect >~0.69% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=1.56; crop max t=1.26; diff +0.308%; noise CV=4.4%; detectable effect >~0.89% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=1.07; crop max t=1.54; diff +0.106%; noise CV=2.2%; detectable effect >~0.45% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=0.98; crop max t=1.40; diff +0.323%; noise CV=7.4%; detectable effect >~1.49% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (~0.47% ✅ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=0.83; crop max t=1.72; diff +0.141%; noise CV=3.8%; detectable effect >~0.77% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=0.89; crop max t=1.77; diff -0.133%; noise CV=3.3%; detectable effect >~0.67% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=1.42; crop max t=1.68; diff -0.161%; noise CV=2.5%; detectable effect >~0.52% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=0.78; crop max t=0.78; diff -0.198%; noise CV=5.7%; detectable effect >~1.15% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (~6.83% ✅ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=0.33; crop max t=0.93; diff +0.069%; noise CV=4.6%; detectable effect >~0.94% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=0.62; crop max t=0.64; diff +0.091%; noise CV=3.3%; detectable effect >~0.66% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=0.57; crop max t=1.56; diff -0.170%; noise CV=6.8%; detectable effect >~1.37% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=0.11; crop max t=1.64; diff +0.033%; noise CV=6.8%; detectable effect >~1.37% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (1 means vpr is faster vpr (FIPS rounds) vs OpenSSL bundled 1.88x (95% CI 1.72-2.06) -> vpr faster vpr (FIPS rounds) vs OpenSSL system 1.82x (95% CI 1.67-1.99) -> vpr faster vpr (gmpy2 BPSW+1MR) vs OpenSSL bundled 1.86x (95% CI 1.68-2.04) -> vpr faster vpr (gmpy2 BPSW+1MR) vs OpenSSL system 1.80x (95% CI 1.64-1.98) -> vpr faster Parallel throughput, 4 worker processes (keys/s, mean of blocks): vpr (FIPS rounds) 161.52 vpr (gmpy2 BPSW+1MR) 158.14 OpenSSL bundled 81.55 OpenSSL system 87.01 ratio vpr(FIPS)/OpenSSL bundled: 1.98x ratio vpr(FIPS)/OpenSSL system: 1.86x ratio vpr(BPSW+1MR)/OpenSSL bundled: 1.94x ratio vpr(BPSW+1MR)/OpenSSL system: 1.82x scaling vpr(FIPS): 161.5 keys/s with 4 workers vs 48.9 serial = 3.30x scaling vpr(BPSW+1MR): 158.1 keys/s with 4 workers vs 48.3 serial = 3.27x Keys saved (test keys, plain text): ./vpr_method_bpsw_2048_single.txt ./openssl_2048_single.txt ./vpr_method_fips_2048_single.txt ./openssl_2048_parallel.txt ./vpr_method_fips_2048_parallel.txt ./vpr_method_bpsw_2048_parallel.txt Verification of 100 keys from [vpr (gmpy2 BPSW+1MR)] (structure + OpenSSL RSA_check_key + sign/verify + OAEP + `openssl rsa -check`): 100 OK, 0 failed Verification of 100 keys from [vpr (FIPS rounds)] (structure + OpenSSL RSA_check_key + sign/verify + OAEP + `openssl rsa -check`): 100 OK, 0 failed ------------------------------------------------------------------------------ RSA-4096 (vpr FIPS profile uses 4 MR rounds) ------------------------------------------------------------------------------ SERIAL: one process per contender (this table does NOT depend on --workers) contender n mean ms median ms p10-p90 ms keys/s vpr (FIPS rounds) 300 210.1 176.7 79.2-374.8 4.76 vpr (gmpy2 BPSW+1MR) 300 208.5 185.0 71.2-375.4 4.80 OpenSSL bundled 300 379.5 315.4 135.8-706.3 2.63 OpenSSL system 300 359.4 304.9 117.2-659.6 2.78 Speed ratio = (contender time) / (vpr time); >1 means vpr is faster vpr (FIPS rounds) vs OpenSSL bundled 1.81x (95% CI 1.63-2.00) -> vpr faster vpr (FIPS rounds) vs OpenSSL system 1.71x (95% CI 1.53-1.90) -> vpr faster vpr (gmpy2 BPSW+1MR) vs OpenSSL bundled 1.82x (95% CI 1.66-2.00) -> vpr faster vpr (gmpy2 BPSW+1MR) vs OpenSSL system 1.72x (95% CI 1.56-1.90) -> vpr faster Parallel throughput, 4 worker processes (keys/s, mean of blocks): vpr (FIPS rounds) 14.71 vpr (gmpy2 BPSW+1MR) 14.57 OpenSSL bundled 8.51 OpenSSL system 8.13 ratio vpr(FIPS)/OpenSSL bundled: 1.73x ratio vpr(FIPS)/OpenSSL system: 1.81x ratio vpr(BPSW+1MR)/OpenSSL bundled: 1.71x ratio vpr(BPSW+1MR)/OpenSSL system: 1.79x scaling vpr(FIPS): 14.7 keys/s with 4 workers vs 4.8 serial = 3.09x scaling vpr(BPSW+1MR): 14.6 keys/s with 4 workers vs 4.8 serial = 3.04x Keys saved (test keys, plain text): ./openssl_4096_single.txt ./vpr_method_bpsw_4096_single.txt ./vpr_method_fips_4096_single.txt ./vpr_method_bpsw_4096_parallel.txt ./vpr_method_fips_4096_parallel.txt ./openssl_4096_parallel.txt Verification of 100 keys from [vpr (gmpy2 BPSW+1MR)] (structure + OpenSSL RSA_check_key + sign/verify + OAEP + `openssl rsa -check`): 100 OK, 0 failed Verification of 100 keys from [vpr (FIPS rounds)] (structure + OpenSSL RSA_check_key + sign/verify + OAEP + `openssl rsa -check`): 100 OK, 0 failed Notes: timings are single-process wall-clock on this machine only; re-run on the target hardware. Key generation time is random, so compare means with their confidence intervals, not single runs. (base) lilyana.g@Stefkas-MacBook-Pro new % caffeinate python3 rsa_key_audit.py \ --vpr-fips-files vpr_method_fips_4096_single.txt vpr_method_fips_4096_parallel.txt \ --vpr-bpsw-files vpr_method_bpsw_4096_single.txt vpr_method_bpsw_4096_parallel.txt \ --openssl-files openssl_4096_single.txt openssl_4096_parallel.txt \ --sidechannel ==================================================================================================== 0. Environment and scope ==================================================================================================== python=3.13.2; platform=macOS-14.7.6-x86_64-i386-64bit-Mach-O; cpus=8; gmpy2=2.2.1; cryptography=45.0.3; openssl=OpenSSL 3.6.5 29 Sep 2026 The audit is a smoke test: not detecting a defect is NOT proof of security. ==================================================================================================== 0a. Loading already generated keys from files ( ) ==================================================================================================== ✅ [L01] [VPR-FIPS] vpr_method_fips_4096_single.txt: loaded 300 keys - all blocks intact ✅ [L01] [VPR-FIPS] vpr_method_fips_4096_parallel.txt: loaded 300 keys - all blocks intact ✅ [L01] [VPR-BPSW] vpr_method_bpsw_4096_single.txt: loaded 300 keys - all blocks intact ✅ [L01] [VPR-BPSW] vpr_method_bpsw_4096_parallel.txt: loaded 300 keys - all blocks intact ✅ [L01] [OpenSSL] openssl_4096_single.txt: loaded 300 keys - all blocks intact ✅ [L01] [OpenSSL] openssl_4096_parallel.txt: loaded 300 keys - all blocks intact RSA-4096; keys: VPR-FIPS=600, VPR-BPSW=600, OpenSSL=600; alpha=0.001; MR rounds=4 (FIPS 186-5 Table B.1) ==================================================================================================== 3. Source of randomness - static AST audit of the module (not proof) ==================================================================================================== ⏭️ [R01] static audit - no --vpr-module supplied ==================================================================================================== 1. [VPR-FIPS] Structural checks on 600 keys (RSA-4096) ==================================================================================================== ✅ [K01] p != q - 600/600 ✅ [K02] n = p*q - 600/600 ✅ [K03] exact length: p,q = b bits; n = 2b bits - 600/600 ✅ [K04] p,q >= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 600/600 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 600/600 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 600/600 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 600/600 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 600/600 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 600/600 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 600/600 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 600/600 ✅ [K12] Pollard p-1 does not factor n - 600/600 ✅ [K13] no ROCA-like fingerprint - 600/600 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 600/600 ==================================================================================================== 2. [VPR-FIPS] Population tests on 600 keys (1200 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 179700 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.0493 (r=23), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.461 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=1; chi2 p=0.895; single position detectable at deviation from 0.5 above ~0.073 (N=1200) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=0.349 ✅ [T03] [VPR-FIPS:vpr_method_fips_4096_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: p mod 2^16 rho=-0.116 p=0.0442; threshold 2.0e-04; detectable |rho| >~0.21 at N=300 ✅ [T03] [VPR-FIPS:vpr_method_fips_4096_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(n) rho=-0.043 p=0.459; threshold 2.0e-04; detectable |rho| >~0.21 at N=300 ✅ [R03] [VPR-FIPS] vpr_method_fips_4096_single.txt: 300 keys - no duplicates and no shared factors - none ✅ [R03] [VPR-FIPS] vpr_method_fips_4096_parallel.txt: 300 keys - no duplicates and no shared factors - none ==================================================================================================== 1. [VPR-BPSW] Structural checks on 600 keys (RSA-4096) ==================================================================================================== ✅ [K01] p != q - 600/600 ✅ [K02] n = p*q - 600/600 ✅ [K03] exact length: p,q = b bits; n = 2b bits - 600/600 ✅ [K04] p,q >= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 600/600 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 600/600 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 600/600 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 600/600 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 600/600 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 600/600 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 600/600 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 600/600 ✅ [K12] Pollard p-1 does not factor n - 600/600 ✅ [K13] no ROCA-like fingerprint - 600/600 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 600/600 ==================================================================================================== 2. [VPR-BPSW] Population tests on 600 keys (1200 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 179700 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.0203 (r=17), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.0783 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=0.283; chi2 p=0.525; single position detectable at deviation from 0.5 above ~0.073 (N=1200) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=0.37 ✅ [T03] [VPR-BPSW:vpr_method_bpsw_4096_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(n) rho=+0.053 p=0.36; threshold 2.0e-04; detectable |rho| >~0.21 at N=300 ✅ [T03] [VPR-BPSW:vpr_method_bpsw_4096_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(q) rho=-0.085 p=0.143; threshold 2.0e-04; detectable |rho| >~0.21 at N=300 ✅ [R03] [VPR-BPSW] vpr_method_bpsw_4096_single.txt: 300 keys - no duplicates and no shared factors - none ✅ [R03] [VPR-BPSW] vpr_method_bpsw_4096_parallel.txt: 300 keys - no duplicates and no shared factors - none ==================================================================================================== 1. [OpenSSL] Structural checks on 600 keys (RSA-4096) ==================================================================================================== ✅ [K01] p != q - 600/600 ✅ [K02] n = p*q - 600/600 ✅ [K03] exact length: p,q = b bits; n = 2b bits - 600/600 ✅ [K04] p,q >= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 600/600 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 600/600 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 600/600 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 600/600 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 600/600 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 600/600 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 600/600 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 600/600 ✅ [K12] Pollard p-1 does not factor n - 600/600 ✅ [K13] no ROCA-like fingerprint - 600/600 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 600/600 ==================================================================================================== 2. [OpenSSL] Population tests on 600 keys (1200 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 179700 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.0591 (r=23), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.925 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=0.448; chi2 p=0.544; single position detectable at deviation from 0.5 above ~0.073 (N=1200) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=0.949 ✅ [T03] [OpenSSL:openssl_4096_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(p) rho=-0.097 p=0.0949; threshold 2.0e-04; detectable |rho| >~0.21 at N=300 ✅ [T03] [OpenSSL:openssl_4096_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(q) rho=+0.076 p=0.186; threshold 2.0e-04; detectable |rho| >~0.21 at N=300 ✅ [R03] [OpenSSL] openssl_4096_single.txt: 300 keys - no duplicates and no shared factors - none ✅ [R03] [OpenSSL] openssl_4096_parallel.txt: 300 keys - no duplicates and no shared factors - none ==================================================================================================== 2a. Cross-check between generators ==================================================================================================== ✅ [P06] shared primes between keys of DIFFERENT generators - none ==================================================================================================== 2b. Bias in prime selection (distribution of the gap before the prime) ==================================================================================================== generating 400 reference uniform primes (2048 bits, MR rounds=4)... ✅ [B00] calibration: two independent halves of the reference (expected no difference) - KS p=0.328 ✅ [B01] [VPR-FIPS] mean gap before prime / reference - ratio 1.090 (95% CI 0.955-1.236); KS p=0.181; MWU p=0.298; N=400; minimum detectable effect ~±14% ✅ [B01] [VPR-BPSW] mean gap before prime / reference - ratio 1.007 (95% CI 0.878-1.152); KS p=0.581; MWU p=0.652; N=400; minimum detectable effect ~±14% ✅ [B01] [OpenSSL] mean gap before prime / reference - ratio 0.862 (95% CI 0.748-0.984); KS p=0.181; MWU p=0.025; N=400; minimum detectable effect ~±14% ==================================================================================================== 4. Measurement calibration (mandatory before trusting 4a) ==================================================================================================== ✅ [T00a] null control: identical distributions (should NOT be flagged) - max|t|=1.22; noise CV=9.2% ✅ [T00b] positive control: synthetic leak ~100% (should be flagged) - max|t|=1007.51; diff +67.036% ==================================================================================================== 4a. [VPR-FIPS] Timing leak on private-key operations (dudect-style, 2000 measurements/pair) ==================================================================================================== ✅ [T01] gmpy2.powmod (plain, no CRT) | fixed vs random - raw t=0.18; crop max t=3.50; diff +0.018%; noise CV=2.2%; detectable effect >~0.45% ✅ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=0.73; crop max t=1.59; diff +0.139%; noise CV=4.3%; detectable effect >~0.87% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=0.83; crop max t=0.69; diff +0.219%; noise CV=5.9%; detectable effect >~1.19% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=2.29; crop max t=2.55; diff -0.322%; noise CV=3.2%; detectable effect >~0.65% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=1.14; crop max t=1.65; diff +0.338%; noise CV=6.6%; detectable effect >~1.33% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (~0.47% ✅ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=1.55; crop max t=1.06; diff +0.202%; noise CV=2.9%; detectable effect >~0.59% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=0.58; crop max t=1.72; diff -0.097%; noise CV=3.8%; detectable effect >~0.77% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=0.69; crop max t=1.80; diff -0.085%; noise CV=2.8%; detectable effect >~0.56% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=0.75; crop max t=2.33; diff -0.197%; noise CV=5.9%; detectable effect >~1.19% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (~0.41% ✅ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=0.14; crop max t=1.57; diff +0.028%; noise CV=4.4%; detectable effect >~0.89% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=0.20; crop max t=1.27; diff -0.035%; noise CV=4.0%; detectable effect >~0.83% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=0.96; crop max t=2.03; diff -0.091%; noise CV=2.1%; detectable effect >~0.43% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=0.85; crop max t=0.34; diff +0.233%; noise CV=6.1%; detectable effect >~1.24% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 600/600 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 600/600 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 600/600 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 600/600 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 600/600 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 600/600 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 600/600 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 600/600 ✅ [K12] Pollard p-1 does not factor n - 600/600 ✅ [K13] no ROCA-like fingerprint - 600/600 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 600/600 ==================================================================================================== 2. [VPR-FIPS] Population tests on 600 keys (1200 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 179700 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.179 (r=5), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.179 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=1; chi2 p=0.239; single position detectable at deviation from 0.5 above ~0.071 (N=1200) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=0.323 ✅ [T03] [VPR-FIPS:vpr_method_fips_2048_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: p mod 2^16 rho=+0.099 p=0.0868; threshold 2.0e-04; detectable |rho| >~0.21 at N=300 ✅ [T03] [VPR-FIPS:vpr_method_fips_2048_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: q mod 2^16 rho=+0.106 p=0.0663; threshold 2.0e-04; detectable |rho| >~0.21 at N=300 ✅ [R03] [VPR-FIPS] vpr_method_fips_2048_single.txt: 300 keys - no duplicates and no shared factors - none ✅ [R03] [VPR-FIPS] vpr_method_fips_2048_parallel.txt: 300 keys - no duplicates and no shared factors - none ==================================================================================================== 1. [VPR-BPSW] Structural checks on 600 keys (RSA-2048) ==================================================================================================== ✅ [K01] p != q - 600/600 ✅ [K02] n = p*q - 600/600 ✅ [K03] exact length: p,q = b bits; n = 2b bits - 600/600 ✅ [K04] p,q >= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 600/600 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 600/600 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 600/600 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 600/600 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 600/600 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 600/600 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 600/600 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 600/600 ✅ [K12] Pollard p-1 does not factor n - 600/600 ✅ [K13] no ROCA-like fingerprint - 600/600 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 600/600 ==================================================================================================== 2. [VPR-BPSW] Population tests on 600 keys (1200 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 179700 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.0257 (r=17), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.363 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=1; chi2 p=0.832; single position detectable at deviation from 0.5 above ~0.071 (N=1200) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=0.12 ✅ [T03] [VPR-BPSW:vpr_method_bpsw_2048_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(q) rho=-0.064 p=0.269; threshold 2.0e-04; detectable |rho| >~0.21 at N=300 ✅ [T03] [VPR-BPSW:vpr_method_bpsw_2048_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: p mod 2^16 rho=-0.089 p=0.125; threshold 2.0e-04; detectable |rho| >~0.21 at N=300 ✅ [R03] [VPR-BPSW] vpr_method_bpsw_2048_single.txt: 300 keys - no duplicates and no shared factors - none ✅ [R03] [VPR-BPSW] vpr_method_bpsw_2048_parallel.txt: 300 keys - no duplicates and no shared factors - none ==================================================================================================== 1. [OpenSSL] Structural checks on 600 keys (RSA-2048) ==================================================================================================== ✅ [K01] p != q - 600/600 ✅ [K02] n = p*q - 600/600 ✅ [K03] exact length: p,q = b bits; n = 2b bits - 600/600 ✅ [K04] p,q >= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 600/600 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 600/600 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 600/600 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 600/600 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 600/600 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 600/600 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 600/600 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 600/600 ✅ [K12] Pollard p-1 does not factor n - 600/600 ✅ [K13] no ROCA-like fingerprint - 600/600 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 600/600 ==================================================================================================== 2. [OpenSSL] Population tests on 600 keys (1200 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 179700 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.00537 (r=5), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.225 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=1; chi2 p=0.992; single position detectable at deviation from 0.5 above ~0.071 (N=1200) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=0.135 ✅ [T03] [OpenSSL:openssl_2048_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(n) rho=+0.084 p=0.145; threshold 2.0e-04; detectable |rho| >~0.21 at N=300 ✅ [T03] [OpenSSL:openssl_2048_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(n) rho=-0.075 p=0.195; threshold 2.0e-04; detectable |rho| >~0.21 at N=300 ✅ [R03] [OpenSSL] openssl_2048_single.txt: 300 keys - no duplicates and no shared factors - none ✅ [R03] [OpenSSL] openssl_2048_parallel.txt: 300 keys - no duplicates and no shared factors - none ==================================================================================================== 2a. Cross-check between generators ==================================================================================================== ✅ [P06] shared primes between keys of DIFFERENT generators - none ==================================================================================================== 2b. Bias in prime selection (distribution of the gap before the prime) ==================================================================================================== generating 400 reference uniform primes (1024 bits, MR rounds=5)... ✅ [B00] calibration: two independent halves of the reference (expected no difference) - KS p=0.545 ✅ [B01] [VPR-FIPS] mean gap before prime / reference - ratio 1.229 (95% CI 1.071-1.409); KS p=0.211; MWU p=0.0486; N=400; minimum detectable effect ~±14% ✅ [B01] [VPR-BPSW] mean gap before prime / reference - ratio 1.266 (95% CI 1.103-1.452); KS p=0.0158; MWU p=0.0119; N=400; minimum detectable effect ~±14% ✅ [B01] [OpenSSL] mean gap before prime / reference - ratio 1.079 (95% CI 0.941-1.231); KS p=0.468; MWU p=0.698; N=400; minimum detectable effect ~±14% ==================================================================================================== 4. Measurement calibration (mandatory before trusting 4a) ==================================================================================================== ✅ [T00a] null control: identical distributions (should NOT be flagged) - max|t|=1.45; noise CV=3.5% ✅ [T00b] positive control: synthetic leak ~100% (should be flagged) - max|t|=625.76; diff +65.851% ==================================================================================================== 4a. [VPR-FIPS] Timing leak on private-key operations (dudect-style, 2000 measurements/pair) ==================================================================================================== ✅ [T01] gmpy2.powmod (plain, no CRT) | fixed vs random - raw t=0.51; crop max t=2.05; diff +0.110%; noise CV=4.8%; detectable effect >~0.98% ✅ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=0.90; crop max t=3.33; diff -0.234%; noise CV=5.8%; detectable effect >~1.18% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=1.31; crop max t=1.57; diff +0.347%; noise CV=5.9%; detectable effect >~1.19% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=0.02; crop max t=0.85; diff -0.002%; noise CV=2.7%; detectable effect >~0.54% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=0.78; crop max t=1.10; diff -0.470%; noise CV=13.5%; detectable effect >~2.76% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (~0.89% ✅ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=0.06; crop max t=1.34; diff -0.014%; noise CV=5.6%; detectable effect >~1.14% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=0.43; crop max t=1.53; diff +0.097%; noise CV=5.0%; detectable effect >~1.02% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=0.35; crop max t=1.75; diff +0.099%; noise CV=6.3%; detectable effect >~1.28% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=0.45; crop max t=1.89; diff +0.278%; noise CV=13.7%; detectable effect >~2.82% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (~0.71% ⚠️ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=0.14; crop max t=4.81; diff -0.034%; noise CV=5.5%; detectable effect >~1.12% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=0.92; crop max t=1.54; diff -0.218%; noise CV=5.3%; detectable effect >~1.06% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=0.43; crop max t=1.34; diff -0.096%; noise CV=5.0%; detectable effect >~1.00% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=1.15; crop max t=1.42; diff -0.718%; noise CV=13.9%; detectable effect >~2.82% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (1 means vpr is faster vpr (FIPS rounds) vs OpenSSL bundled 1.94x (95% CI 1.65-2.28) -> vpr faster vpr (FIPS rounds) vs OpenSSL system 1.89x (95% CI 1.59-2.26) -> vpr faster vpr (gmpy2 BPSW+1MR) vs OpenSSL bundled 2.03x (95% CI 1.71-2.40) -> vpr faster vpr (gmpy2 BPSW+1MR) vs OpenSSL system 1.97x (95% CI 1.63-2.36) -> vpr faster Parallel throughput, 4 worker processes (keys/s, mean of blocks): vpr (FIPS rounds) 147.75 vpr (gmpy2 BPSW+1MR) 171.48 OpenSSL bundled 91.02 OpenSSL system 83.13 ratio vpr(FIPS)/OpenSSL bundled: 1.62x ratio vpr(FIPS)/OpenSSL system: 1.78x ratio vpr(BPSW+1MR)/OpenSSL bundled: 1.88x ratio vpr(BPSW+1MR)/OpenSSL system: 2.06x scaling vpr(FIPS): 147.7 keys/s with 4 workers vs 49.8 serial = 2.97x scaling vpr(BPSW+1MR): 171.5 keys/s with 4 workers vs 51.8 serial = 3.31x Keys saved (test keys, plain text): ./vpr_method_fips_2048_single.txt ./vpr_method_bpsw_2048_single.txt ./openssl_2048_single.txt ./openssl_2048_parallel.txt ./vpr_method_fips_2048_parallel.txt ./vpr_method_bpsw_2048_parallel.txt Verification of 100 keys from [vpr (FIPS rounds)] (structure + OpenSSL RSA_check_key + sign/verify + OAEP + `openssl rsa -check`): 100 OK, 0 failed Verification of 100 keys from [vpr (gmpy2 BPSW+1MR)] (structure + OpenSSL RSA_check_key + sign/verify + OAEP + `openssl rsa -check`): 100 OK, 0 failed ------------------------------------------------------------------------------ RSA-4096 (vpr FIPS profile uses 4 MR rounds) ------------------------------------------------------------------------------ SERIAL: one process per contender (this table does NOT depend on --workers) contender n mean ms median ms p10-p90 ms keys/s vpr (FIPS rounds) 100 212.3 169.1 78.0-389.8 4.71 vpr (gmpy2 BPSW+1MR) 100 207.7 188.3 84.0-343.9 4.82 OpenSSL bundled 100 388.3 314.7 122.2-795.8 2.58 OpenSSL system 100 384.6 302.7 110.9-771.8 2.60 Speed ratio = (contender time) / (vpr time); >1 means vpr is faster vpr (FIPS rounds) vs OpenSSL bundled 1.83x (95% CI 1.51-2.22) -> vpr faster vpr (FIPS rounds) vs OpenSSL system 1.81x (95% CI 1.49-2.19) -> vpr faster vpr (gmpy2 BPSW+1MR) vs OpenSSL bundled 1.87x (95% CI 1.57-2.24) -> vpr faster vpr (gmpy2 BPSW+1MR) vs OpenSSL system 1.85x (95% CI 1.53-2.24) -> vpr faster Parallel throughput, 4 worker processes (keys/s, mean of blocks): vpr (FIPS rounds) 15.34 vpr (gmpy2 BPSW+1MR) 13.40 OpenSSL bundled 8.48 OpenSSL system 8.25 ratio vpr(FIPS)/OpenSSL bundled: 1.81x ratio vpr(FIPS)/OpenSSL system: 1.86x ratio vpr(BPSW+1MR)/OpenSSL bundled: 1.58x ratio vpr(BPSW+1MR)/OpenSSL system: 1.62x scaling vpr(FIPS): 15.3 keys/s with 4 workers vs 4.7 serial = 3.26x scaling vpr(BPSW+1MR): 13.4 keys/s with 4 workers vs 4.8 serial = 2.78x Keys saved (test keys, plain text): ./vpr_method_fips_4096_single.txt ./vpr_method_bpsw_4096_single.txt ./openssl_4096_single.txt ./vpr_method_fips_4096_parallel.txt ./vpr_method_bpsw_4096_parallel.txt ./openssl_4096_parallel.txt Verification of 100 keys from [vpr (FIPS rounds)] (structure + OpenSSL RSA_check_key + sign/verify + OAEP + `openssl rsa -check`): 100 OK, 0 failed Verification of 100 keys from [vpr (gmpy2 BPSW+1MR)] (structure + OpenSSL RSA_check_key + sign/verify + OAEP + `openssl rsa -check`): 100 OK, 0 failed Notes: timings are single-process wall-clock on this machine only; re-run on the target hardware. Key generation time is random, so compare means with their confidence intervals, not single runs. (base) lilyana.g@Stefkas-MacBook-Pro new % (base) lilyana.g@Stefkas-MacBook-Pro new % (base) lilyana.g@Stefkas-MacBook-Pro new % caffeinate python3 rsa_key_audit.py \ --vpr-fips-files vpr_method_fips_4096_single.txt vpr_method_fips_4096_parallel.txt \ --vpr-bpsw-files vpr_method_bpsw_4096_single.txt vpr_method_bpsw_4096_parallel.txt \ --openssl-files openssl_4096_single.txt openssl_4096_parallel.txt \ --sidechannel ==================================================================================================== 0. Environment and scope ==================================================================================================== python=3.13.2; platform=macOS-14.7.6-x86_64-i386-64bit-Mach-O; cpus=8; gmpy2=2.2.1; cryptography=45.0.3; openssl=OpenSSL 3.6.5 29 Sep 2026 The audit is a smoke test: not detecting a defect is NOT proof of security. ==================================================================================================== 0a. Loading already generated keys from files ( ) ==================================================================================================== ✅ [L01] [VPR-FIPS] vpr_method_fips_4096_single.txt: loaded 100 keys - all blocks intact ✅ [L01] [VPR-FIPS] vpr_method_fips_4096_parallel.txt: loaded 100 keys - all blocks intact ✅ [L01] [VPR-BPSW] vpr_method_bpsw_4096_single.txt: loaded 100 keys - all blocks intact ✅ [L01] [VPR-BPSW] vpr_method_bpsw_4096_parallel.txt: loaded 100 keys - all blocks intact ✅ [L01] [OpenSSL] openssl_4096_single.txt: loaded 100 keys - all blocks intact ✅ [L01] [OpenSSL] openssl_4096_parallel.txt: loaded 100 keys - all blocks intact RSA-4096; keys: VPR-FIPS=200, VPR-BPSW=200, OpenSSL=200; alpha=0.001; MR rounds=4 (FIPS 186-5 Table B.1) ==================================================================================================== 3. Source of randomness - static AST audit of the module (not proof) ==================================================================================================== ⏭️ [R01] static audit - no --vpr-module supplied ==================================================================================================== 1. [VPR-FIPS] Structural checks on 200 keys (RSA-4096) ==================================================================================================== ✅ [K01] p != q - 200/200 ✅ [K02] n = p*q - 200/200 ✅ [K03] exact length: p,q = b bits; n = 2b bits - 200/200 ✅ [K04] p,q >= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 200/200 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 200/200 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 200/200 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 200/200 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 200/200 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 200/200 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 200/200 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 200/200 ✅ [K12] Pollard p-1 does not factor n - 200/200 ✅ [K13] no ROCA-like fingerprint - 200/200 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 200/200 ==================================================================================================== 2. [VPR-FIPS] Population tests on 200 keys (400 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 19900 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.149 (r=17), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.149 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=0.44; chi2 p=0.699; single position detectable at deviation from 0.5 above ~0.126 (N=400) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=1 ✅ [T03] [VPR-FIPS:vpr_method_fips_4096_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(q) rho=+0.260 p=0.00906; threshold 2.0e-04; detectable |rho| >~0.37 at N=100 ✅ [T03] [VPR-FIPS:vpr_method_fips_4096_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: q mod 2^16 rho=-0.191 p=0.0572; threshold 2.0e-04; detectable |rho| >~0.37 at N=100 ✅ [R03] [VPR-FIPS] vpr_method_fips_4096_single.txt: 100 keys - no duplicates and no shared factors - none ✅ [R03] [VPR-FIPS] vpr_method_fips_4096_parallel.txt: 100 keys - no duplicates and no shared factors - none ==================================================================================================== 1. [VPR-BPSW] Structural checks on 200 keys (RSA-4096) ==================================================================================================== ✅ [K01] p != q - 200/200 ✅ [K02] n = p*q - 200/200 ✅ [K03] exact length: p,q = b bits; n = 2b bits - 200/200 ✅ [K04] p,q >= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 200/200 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 200/200 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 200/200 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 200/200 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 200/200 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 200/200 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 200/200 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 200/200 ✅ [K12] Pollard p-1 does not factor n - 200/200 ✅ [K13] no ROCA-like fingerprint - 200/200 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 200/200 ==================================================================================================== 2. [VPR-BPSW] Population tests on 200 keys (400 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 19900 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.0185 (r=7), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.405 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=0.649; chi2 p=0.886; single position detectable at deviation from 0.5 above ~0.126 (N=400) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=1 ✅ [T03] [VPR-BPSW:vpr_method_bpsw_4096_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(p) rho=-0.176 p=0.0791; threshold 2.0e-04; detectable |rho| >~0.37 at N=100 ✅ [T03] [VPR-BPSW:vpr_method_bpsw_4096_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: q mod 2^16 rho=+0.107 p=0.291; threshold 2.0e-04; detectable |rho| >~0.37 at N=100 ✅ [R03] [VPR-BPSW] vpr_method_bpsw_4096_single.txt: 100 keys - no duplicates and no shared factors - none ✅ [R03] [VPR-BPSW] vpr_method_bpsw_4096_parallel.txt: 100 keys - no duplicates and no shared factors - none ==================================================================================================== 1. [OpenSSL] Structural checks on 200 keys (RSA-4096) ==================================================================================================== ✅ [K01] p != q - 200/200 ✅ [K02] n = p*q - 200/200 ✅ [K03] exact length: p,q = b bits; n = 2b bits - 200/200 ✅ [K04] p,q >= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 200/200 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 200/200 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 200/200 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 200/200 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 200/200 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 200/200 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 200/200 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 200/200 ✅ [K12] Pollard p-1 does not factor n - 200/200 ✅ [K13] no ROCA-like fingerprint - 200/200 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 200/200 ==================================================================================================== 2. [OpenSSL] Population tests on 200 keys (400 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 19900 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.0357 (r=3), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.0958 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=0.129; chi2 p=0.56; single position detectable at deviation from 0.5 above ~0.126 (N=400) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=0.0801 ✅ [T03] [OpenSSL:openssl_4096_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: p mod 2^16 rho=-0.079 p=0.435; threshold 2.0e-04; detectable |rho| >~0.37 at N=100 ✅ [T03] [OpenSSL:openssl_4096_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(n) rho=+0.157 p=0.119; threshold 2.0e-04; detectable |rho| >~0.37 at N=100 ✅ [R03] [OpenSSL] openssl_4096_single.txt: 100 keys - no duplicates and no shared factors - none ✅ [R03] [OpenSSL] openssl_4096_parallel.txt: 100 keys - no duplicates and no shared factors - none ==================================================================================================== 2a. Cross-check between generators ==================================================================================================== ✅ [P06] shared primes between keys of DIFFERENT generators - none ==================================================================================================== 2b. Bias in prime selection (distribution of the gap before the prime) ==================================================================================================== generating 400 reference uniform primes (2048 bits, MR rounds=4)... ✅ [B00] calibration: two independent halves of the reference (expected no difference) - KS p=0.0878 ✅ [B01] [VPR-FIPS] mean gap before prime / reference - ratio 1.153 (95% CI 1.010-1.316); KS p=0.281; MWU p=0.643; N=400; minimum detectable effect ~±14% ✅ [B01] [VPR-BPSW] mean gap before prime / reference - ratio 1.073 (95% CI 0.945-1.216); KS p=0.641; MWU p=0.893; N=400; minimum detectable effect ~±14% ✅ [B01] [OpenSSL] mean gap before prime / reference - ratio 0.915 (95% CI 0.803-1.043); KS p=0.111; MWU p=0.0391; N=400; minimum detectable effect ~±14% ==================================================================================================== 4. Measurement calibration (mandatory before trusting 4a) ==================================================================================================== ✅ [T00a] null control: identical distributions (should NOT be flagged) - max|t|=1.06; noise CV=2.2% ✅ [T00b] positive control: synthetic leak ~100% (should be flagged) - max|t|=957.13; diff +65.425% ==================================================================================================== 4a. [VPR-FIPS] Timing leak on private-key operations (dudect-style, 2000 measurements/pair) ==================================================================================================== ✅ [T01] gmpy2.powmod (plain, no CRT) | fixed vs random - raw t=1.14; crop max t=1.80; diff -0.103%; noise CV=2.0%; detectable effect >~0.41% ✅ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=0.38; crop max t=1.92; diff -0.077%; noise CV=4.5%; detectable effect >~0.91% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=0.75; crop max t=2.58; diff +0.093%; noise CV=2.8%; detectable effect >~0.56% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=0.21; crop max t=1.33; diff +0.016%; noise CV=1.7%; detectable effect >~0.34% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=0.34; crop max t=0.95; diff -0.096%; noise CV=6.3%; detectable effect >~1.27% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (~0.45% ✅ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=0.78; crop max t=3.15; diff -0.074%; noise CV=2.1%; detectable effect >~0.43% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=0.58; crop max t=1.62; diff -0.084%; noise CV=3.2%; detectable effect >~0.66% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=1.51; crop max t=0.77; diff +0.136%; noise CV=2.0%; detectable effect >~0.40% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=0.63; crop max t=1.37; diff +0.182%; noise CV=6.5%; detectable effect >~1.33% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (~0.46% ✅ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=1.69; crop max t=4.00; diff -0.208%; noise CV=2.7%; detectable effect >~0.56% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=0.85; crop max t=0.67; diff +0.137%; noise CV=3.6%; detectable effect >~0.74% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=1.34; crop max t=1.31; diff -0.115%; noise CV=1.9%; detectable effect >~0.39% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=0.31; crop max t=0.95; diff -0.089%; noise CV=6.3%; detectable effect >~1.28% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 200/200 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 200/200 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 200/200 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 200/200 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 200/200 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 200/200 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 200/200 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 200/200 ✅ [K12] Pollard p-1 does not factor n - 200/200 ✅ [K13] no ROCA-like fingerprint - 200/200 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 200/200 ==================================================================================================== 2. [VPR-FIPS] Population tests on 200 keys (400 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 19900 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.0648 (r=13), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.18 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=0.219; chi2 p=0.666; single position detectable at deviation from 0.5 above ~0.122 (N=400) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=0.265 ✅ [T03] [VPR-FIPS:vpr_method_fips_2048_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: p mod 2^16 rho=+0.063 p=0.536; threshold 2.0e-04; detectable |rho| >~0.37 at N=100 ✅ [T03] [VPR-FIPS:vpr_method_fips_2048_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: p mod 2^16 rho=+0.105 p=0.296; threshold 2.0e-04; detectable |rho| >~0.37 at N=100 ✅ [R03] [VPR-FIPS] vpr_method_fips_2048_single.txt: 100 keys - no duplicates and no shared factors - none ✅ [R03] [VPR-FIPS] vpr_method_fips_2048_parallel.txt: 100 keys - no duplicates and no shared factors - none ==================================================================================================== 1. [VPR-BPSW] Structural checks on 200 keys (RSA-2048) ==================================================================================================== ✅ [K01] p != q - 200/200 ✅ [K02] n = p*q - 200/200 ✅ [K03] exact length: p,q = b bits; n = 2b bits - 200/200 ✅ [K04] p,q >= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 200/200 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 200/200 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 200/200 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 200/200 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 200/200 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 200/200 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 200/200 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 200/200 ✅ [K12] Pollard p-1 does not factor n - 200/200 ✅ [K13] no ROCA-like fingerprint - 200/200 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 200/200 ==================================================================================================== 2. [VPR-BPSW] Population tests on 200 keys (400 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 19900 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.0836 (r=5), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.253 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=0.0643; chi2 p=0.0792; single position detectable at deviation from 0.5 above ~0.122 (N=400) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=1 ✅ [T03] [VPR-BPSW:vpr_method_bpsw_2048_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(p) rho=-0.164 p=0.104; threshold 2.0e-04; detectable |rho| >~0.37 at N=100 ✅ [T03] [VPR-BPSW:vpr_method_bpsw_2048_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(p) rho=-0.138 p=0.171; threshold 2.0e-04; detectable |rho| >~0.37 at N=100 ✅ [R03] [VPR-BPSW] vpr_method_bpsw_2048_single.txt: 100 keys - no duplicates and no shared factors - none ✅ [R03] [VPR-BPSW] vpr_method_bpsw_2048_parallel.txt: 100 keys - no duplicates and no shared factors - none ==================================================================================================== 1. [OpenSSL] Structural checks on 200 keys (RSA-2048) ==================================================================================================== ✅ [K01] p != q - 200/200 ✅ [K02] n = p*q - 200/200 ✅ [K03] exact length: p,q = b bits; n = 2b bits - 200/200 ✅ [K04] p,q >= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 200/200 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 200/200 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 200/200 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 200/200 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 200/200 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 200/200 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 200/200 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 200/200 ✅ [K12] Pollard p-1 does not factor n - 200/200 ✅ [K13] no ROCA-like fingerprint - 200/200 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 200/200 ==================================================================================================== 2. [OpenSSL] Population tests on 200 keys (400 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 19900 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.204 (r=5), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.737 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=0.0419; chi2 p=0.318; single position detectable at deviation from 0.5 above ~0.122 (N=400) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=0.392 ✅ [T03] [OpenSSL:openssl_2048_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(n) rho=+0.058 p=0.564; threshold 2.0e-04; detectable |rho| >~0.37 at N=100 ✅ [T03] [OpenSSL:openssl_2048_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(q) rho=+0.101 p=0.319; threshold 2.0e-04; detectable |rho| >~0.37 at N=100 ✅ [R03] [OpenSSL] openssl_2048_single.txt: 100 keys - no duplicates and no shared factors - none ✅ [R03] [OpenSSL] openssl_2048_parallel.txt: 100 keys - no duplicates and no shared factors - none ==================================================================================================== 2a. Cross-check between generators ==================================================================================================== ✅ [P06] shared primes between keys of DIFFERENT generators - none ==================================================================================================== 2b. Bias in prime selection (distribution of the gap before the prime) ==================================================================================================== generating 400 reference uniform primes (1024 bits, MR rounds=5)... ✅ [B00] calibration: two independent halves of the reference (expected no difference) - KS p=0.713 ✅ [B01] [VPR-FIPS] mean gap before prime / reference - ratio 1.109 (95% CI 0.957-1.278); KS p=0.758; MWU p=0.538; N=400; minimum detectable effect ~±14% ✅ [B01] [VPR-BPSW] mean gap before prime / reference - ratio 0.962 (95% CI 0.836-1.098); KS p=0.907; MWU p=0.641; N=400; minimum detectable effect ~±14% ✅ [B01] [OpenSSL] mean gap before prime / reference - ratio 0.827 (95% CI 0.724-0.945); KS p=0.111; MWU p=0.0385; N=400; minimum detectable effect ~±14% ==================================================================================================== 4. Measurement calibration (mandatory before trusting 4a) ==================================================================================================== ✅ [T00a] null control: identical distributions (should NOT be flagged) - max|t|=1.30; noise CV=4.2% ✅ [T00b] positive control: synthetic leak ~100% (should be flagged) - max|t|=840.24; diff +66.695% ==================================================================================================== 4a. [VPR-FIPS] Timing leak on private-key operations (dudect-style, 2000 measurements/pair) ==================================================================================================== ✅ [T01] gmpy2.powmod (plain, no CRT) | fixed vs random - raw t=0.60; crop max t=1.53; diff -0.109%; noise CV=4.0%; detectable effect >~0.84% ✅ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=3.04; crop max t=3.27; diff -0.740%; noise CV=5.5%; detectable effect >~1.10% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=0.02; crop max t=1.42; diff +0.005%; noise CV=4.9%; detectable effect >~1.00% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=0.59; crop max t=1.23; diff +0.077%; noise CV=2.9%; detectable effect >~0.59% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=0.43; crop max t=2.83; diff +0.326%; noise CV=17.0%; detectable effect >~3.48% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (~0.55% ✅ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=1.36; crop max t=1.43; diff -0.331%; noise CV=5.5%; detectable effect >~1.11% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=0.50; crop max t=1.51; diff -0.131%; noise CV=5.9%; detectable effect >~1.20% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=1.23; crop max t=2.70; diff +0.289%; noise CV=5.3%; detectable effect >~1.08% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=1.36; crop max t=1.91; diff +0.825%; noise CV=13.5%; detectable effect >~2.77% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (~0.66% ✅ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=0.30; crop max t=3.97; diff -0.072%; noise CV=5.4%; detectable effect >~1.09% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=1.42; crop max t=1.11; diff -0.338%; noise CV=5.3%; detectable effect >~1.08% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=1.09; crop max t=0.57; diff -0.135%; noise CV=2.8%; detectable effect >~0.56% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=0.93; crop max t=1.14; diff -0.526%; noise CV=12.7%; detectable effect >~2.58% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (1 means vpr is faster vpr (FIPS rounds) vs OpenSSL bundled 1.95x (95% CI 1.74-2.18) -> vpr faster vpr (FIPS rounds) vs OpenSSL system 1.88x (95% CI 1.68-2.12) -> vpr faster vpr (gmpy2 BPSW+1MR) vs OpenSSL bundled 1.85x (95% CI 1.65-2.08) -> vpr faster vpr (gmpy2 BPSW+1MR) vs OpenSSL system 1.79x (95% CI 1.58-2.03) -> vpr faster Parallel throughput, 4 worker processes (keys/s, mean of blocks): vpr (FIPS rounds) 166.93 vpr (gmpy2 BPSW+1MR) 155.51 OpenSSL bundled 82.05 OpenSSL system 85.37 ratio vpr(FIPS)/OpenSSL bundled: 2.03x ratio vpr(FIPS)/OpenSSL system: 1.96x ratio vpr(BPSW+1MR)/OpenSSL bundled: 1.90x ratio vpr(BPSW+1MR)/OpenSSL system: 1.82x scaling vpr(FIPS): 166.9 keys/s with 4 workers vs 51.8 serial = 3.22x scaling vpr(BPSW+1MR): 155.5 keys/s with 4 workers vs 49.3 serial = 3.15x Keys saved (test keys, plain text): ./vpr_method_bpsw_2048_single.txt ./vpr_method_fips_2048_single.txt ./openssl_2048_single.txt ./vpr_method_fips_2048_parallel.txt ./openssl_2048_parallel.txt ./vpr_method_bpsw_2048_parallel.txt Verification of 100 keys from [vpr (gmpy2 BPSW+1MR)] (structure + OpenSSL RSA_check_key + sign/verify + OAEP + `openssl rsa -check`): 100 OK, 0 failed Verification of 100 keys from [vpr (FIPS rounds)] (structure + OpenSSL RSA_check_key + sign/verify + OAEP + `openssl rsa -check`): 100 OK, 0 failed ------------------------------------------------------------------------------ RSA-4096 (vpr FIPS profile uses 4 MR rounds) ------------------------------------------------------------------------------ SERIAL: one process per contender (this table does NOT depend on --workers) contender n mean ms median ms p10-p90 ms keys/s vpr (FIPS rounds) 200 212.1 187.5 78.0-367.2 4.72 vpr (gmpy2 BPSW+1MR) 200 202.5 166.6 79.3-376.8 4.94 OpenSSL bundled 200 402.3 337.4 122.7-762.3 2.49 OpenSSL system 200 359.4 311.4 107.3-654.0 2.78 Speed ratio = (contender time) / (vpr time); >1 means vpr is faster vpr (FIPS rounds) vs OpenSSL bundled 1.90x (95% CI 1.67-2.15) -> vpr faster vpr (FIPS rounds) vs OpenSSL system 1.70x (95% CI 1.51-1.91) -> vpr faster vpr (gmpy2 BPSW+1MR) vs OpenSSL bundled 1.99x (95% CI 1.73-2.25) -> vpr faster vpr (gmpy2 BPSW+1MR) vs OpenSSL system 1.78x (95% CI 1.57-2.00) -> vpr faster Parallel throughput, 4 worker processes (keys/s, mean of blocks): vpr (FIPS rounds) 15.51 vpr (gmpy2 BPSW+1MR) 13.04 OpenSSL bundled 8.23 OpenSSL system 7.74 ratio vpr(FIPS)/OpenSSL bundled: 1.88x ratio vpr(FIPS)/OpenSSL system: 2.00x ratio vpr(BPSW+1MR)/OpenSSL bundled: 1.58x ratio vpr(BPSW+1MR)/OpenSSL system: 1.69x scaling vpr(FIPS): 15.5 keys/s with 4 workers vs 4.7 serial = 3.29x scaling vpr(BPSW+1MR): 13.0 keys/s with 4 workers vs 4.9 serial = 2.64x Keys saved (test keys, plain text): ./vpr_method_bpsw_4096_single.txt ./openssl_4096_single.txt ./vpr_method_fips_4096_single.txt ./openssl_4096_parallel.txt ./vpr_method_bpsw_4096_parallel.txt ./vpr_method_fips_4096_parallel.txt Verification of 100 keys from [vpr (gmpy2 BPSW+1MR)] (structure + OpenSSL RSA_check_key + sign/verify + OAEP + `openssl rsa -check`): 100 OK, 0 failed Verification of 100 keys from [vpr (FIPS rounds)] (structure + OpenSSL RSA_check_key + sign/verify + OAEP + `openssl rsa -check`): 100 OK, 0 failed Notes: timings are single-process wall-clock on this machine only; re-run on the target hardware. Key generation time is random, so compare means with their confidence intervals, not single runs. (base) lilyana.g@Stefkas-MacBook-Pro new % caffeinate python3 rsa_key_audit.py \ --vpr-fips-files vpr_method_fips_4096_single.txt vpr_method_fips_4096_parallel.txt \ --vpr-bpsw-files vpr_method_bpsw_4096_single.txt vpr_method_bpsw_4096_parallel.txt \ --openssl-files openssl_4096_single.txt openssl_4096_parallel.txt \ --sidechannel ==================================================================================================== 0. Environment and scope ==================================================================================================== python=3.13.2; platform=macOS-14.7.6-x86_64-i386-64bit-Mach-O; cpus=8; gmpy2=2.2.1; cryptography=45.0.3; openssl=OpenSSL 3.6.5 29 Sep 2026 The audit is a smoke test: not detecting a defect is NOT proof of security. ==================================================================================================== 0a. Loading already generated keys from files ( ) ==================================================================================================== ✅ [L01] [VPR-FIPS] vpr_method_fips_4096_single.txt: loaded 200 keys - all blocks intact ✅ [L01] [VPR-FIPS] vpr_method_fips_4096_parallel.txt: loaded 200 keys - all blocks intact ✅ [L01] [VPR-BPSW] vpr_method_bpsw_4096_single.txt: loaded 200 keys - all blocks intact ✅ [L01] [VPR-BPSW] vpr_method_bpsw_4096_parallel.txt: loaded 200 keys - all blocks intact ✅ [L01] [OpenSSL] openssl_4096_single.txt: loaded 200 keys - all blocks intact ✅ [L01] [OpenSSL] openssl_4096_parallel.txt: loaded 200 keys - all blocks intact RSA-4096; keys: VPR-FIPS=400, VPR-BPSW=400, OpenSSL=400; alpha=0.001; MR rounds=4 (FIPS 186-5 Table B.1) ==================================================================================================== 3. Source of randomness - static AST audit of the module (not proof) ==================================================================================================== ⏭️ [R01] static audit - no --vpr-module supplied ==================================================================================================== 1. [VPR-FIPS] Structural checks on 400 keys (RSA-4096) ==================================================================================================== ✅ [K01] p != q - 400/400 ✅ [K02] n = p*q - 400/400 ✅ [K03] exact length: p,q = b bits; n = 2b bits - 400/400 ✅ [K04] p,q >= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 400/400 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 400/400 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 400/400 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 400/400 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 400/400 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 400/400 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 400/400 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 400/400 ✅ [K12] Pollard p-1 does not factor n - 400/400 ✅ [K13] no ROCA-like fingerprint - 400/400 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 400/400 ==================================================================================================== 2. [VPR-FIPS] Population tests on 400 keys (800 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 79800 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.0939 (r=13), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.275 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=1; chi2 p=0.404; single position detectable at deviation from 0.5 above ~0.089 (N=800) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=0.235 ✅ [T03] [VPR-FIPS:vpr_method_fips_4096_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: p mod 2^16 rho=+0.163 p=0.0209; threshold 2.0e-04; detectable |rho| >~0.26 at N=200 ✅ [T03] [VPR-FIPS:vpr_method_fips_4096_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(q) rho=+0.049 p=0.49; threshold 2.0e-04; detectable |rho| >~0.26 at N=200 ✅ [R03] [VPR-FIPS] vpr_method_fips_4096_single.txt: 200 keys - no duplicates and no shared factors - none ✅ [R03] [VPR-FIPS] vpr_method_fips_4096_parallel.txt: 200 keys - no duplicates and no shared factors - none ==================================================================================================== 1. [VPR-BPSW] Structural checks on 400 keys (RSA-4096) ==================================================================================================== ✅ [K01] p != q - 400/400 ✅ [K02] n = p*q - 400/400 ✅ [K03] exact length: p,q = b bits; n = 2b bits - 400/400 ✅ [K04] p,q >= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 400/400 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 400/400 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 400/400 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 400/400 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 400/400 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 400/400 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 400/400 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 400/400 ✅ [K12] Pollard p-1 does not factor n - 400/400 ✅ [K13] no ROCA-like fingerprint - 400/400 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 400/400 ==================================================================================================== 2. [VPR-BPSW] Population tests on 400 keys (800 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 79800 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.0464 (r=11), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.187 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=0.83; chi2 p=0.258; single position detectable at deviation from 0.5 above ~0.089 (N=800) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=0.535 ✅ [T03] [VPR-BPSW:vpr_method_bpsw_4096_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(n) rho=+0.088 p=0.214; threshold 2.0e-04; detectable |rho| >~0.26 at N=200 ✅ [T03] [VPR-BPSW:vpr_method_bpsw_4096_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(n) rho=-0.042 p=0.551; threshold 2.0e-04; detectable |rho| >~0.26 at N=200 ✅ [R03] [VPR-BPSW] vpr_method_bpsw_4096_single.txt: 200 keys - no duplicates and no shared factors - none ✅ [R03] [VPR-BPSW] vpr_method_bpsw_4096_parallel.txt: 200 keys - no duplicates and no shared factors - none ==================================================================================================== 1. [OpenSSL] Structural checks on 400 keys (RSA-4096) ==================================================================================================== ✅ [K01] p != q - 400/400 ✅ [K02] n = p*q - 400/400 ✅ [K03] exact length: p,q = b bits; n = 2b bits - 400/400 ✅ [K04] p,q >= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 400/400 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 400/400 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 400/400 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 400/400 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 400/400 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 400/400 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 400/400 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 400/400 ✅ [K12] Pollard p-1 does not factor n - 400/400 ✅ [K13] no ROCA-like fingerprint - 400/400 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 400/400 ==================================================================================================== 2. [OpenSSL] Population tests on 400 keys (800 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 79800 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.104 (r=3), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.684 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=0.633; chi2 p=0.128; single position detectable at deviation from 0.5 above ~0.089 (N=800) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=0.337 ✅ [T03] [OpenSSL:openssl_4096_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(q) rho=-0.112 p=0.113; threshold 2.0e-04; detectable |rho| >~0.26 at N=200 ✅ [T03] [OpenSSL:openssl_4096_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(q) rho=-0.104 p=0.143; threshold 2.0e-04; detectable |rho| >~0.26 at N=200 ✅ [R03] [OpenSSL] openssl_4096_single.txt: 200 keys - no duplicates and no shared factors - none ✅ [R03] [OpenSSL] openssl_4096_parallel.txt: 200 keys - no duplicates and no shared factors - none ==================================================================================================== 2a. Cross-check between generators ==================================================================================================== ✅ [P06] shared primes between keys of DIFFERENT generators - none ==================================================================================================== 2b. Bias in prime selection (distribution of the gap before the prime) ==================================================================================================== generating 400 reference uniform primes (2048 bits, MR rounds=4)... ✅ [B00] calibration: two independent halves of the reference (expected no difference) - KS p=0.328 ✅ [B01] [VPR-FIPS] mean gap before prime / reference - ratio 1.085 (95% CI 0.948-1.243); KS p=0.367; MWU p=0.394; N=400; minimum detectable effect ~±14% ✅ [B01] [VPR-BPSW] mean gap before prime / reference - ratio 1.219 (95% CI 1.066-1.408); KS p=0.0242; MWU p=0.00787; N=400; minimum detectable effect ~±14% ✅ [B01] [OpenSSL] mean gap before prime / reference - ratio 1.067 (95% CI 0.931-1.224); KS p=0.244; MWU p=0.32; N=400; minimum detectable effect ~±14% ==================================================================================================== 4. Measurement calibration (mandatory before trusting 4a) ==================================================================================================== ✅ [T00a] null control: identical distributions (should NOT be flagged) - max|t|=1.24; noise CV=6.9% ✅ [T00b] positive control: synthetic leak ~100% (should be flagged) - max|t|=257.50; diff +66.919% ==================================================================================================== 4a. [VPR-FIPS] Timing leak on private-key operations (dudect-style, 2000 measurements/pair) ==================================================================================================== ✅ [T01] gmpy2.powmod (plain, no CRT) | fixed vs random - raw t=1.14; crop max t=1.47; diff -0.332%; noise CV=6.5%; detectable effect >~1.34% ✅ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=0.88; crop max t=1.74; diff +0.169%; noise CV=4.3%; detectable effect >~0.88% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=1.02; crop max t=0.73; diff -0.209%; noise CV=4.6%; detectable effect >~0.93% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=0.93; crop max t=1.28; diff +0.159%; noise CV=3.8%; detectable effect >~0.78% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=0.59; crop max t=1.30; diff -0.206%; noise CV=7.8%; detectable effect >~1.58% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (~8.74% ✅ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=0.62; crop max t=1.14; diff +0.129%; noise CV=4.7%; detectable effect >~0.95% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=1.54; crop max t=1.47; diff -2.120%; noise CV=31.3%; detectable effect >~6.37% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=0.61; crop max t=1.72; diff -0.139%; noise CV=5.1%; detectable effect >~1.03% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=1.96; crop max t=1.37; diff -0.972%; noise CV=11.1%; detectable effect >~2.26% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (~2.63% ✅ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=1.58; crop max t=1.34; diff -3.211%; noise CV=46.3%; detectable effect >~9.48% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=0.04; crop max t=1.28; diff +0.006%; noise CV=3.6%; detectable effect >~0.73% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=0.11; crop max t=2.44; diff -0.012%; noise CV=2.5%; detectable effect >~0.51% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=0.58; crop max t=1.20; diff -0.169%; noise CV=6.5%; detectable effect >~1.34% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 400/400 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 400/400 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 400/400 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 400/400 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 400/400 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 400/400 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 400/400 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 400/400 ✅ [K12] Pollard p-1 does not factor n - 400/400 ✅ [K13] no ROCA-like fingerprint - 400/400 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 400/400 ==================================================================================================== 2. [VPR-FIPS] Population tests on 400 keys (800 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 79800 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.143 (r=23), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.664 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=0.699; chi2 p=0.503; single position detectable at deviation from 0.5 above ~0.087 (N=800) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=0.289 ✅ [T03] [VPR-FIPS:vpr_method_fips_2048_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: q mod 2^16 rho=-0.094 p=0.188; threshold 2.0e-04; detectable |rho| >~0.26 at N=200 ✅ [T03] [VPR-FIPS:vpr_method_fips_2048_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: q mod 2^16 rho=-0.053 p=0.46; threshold 2.0e-04; detectable |rho| >~0.26 at N=200 ✅ [R03] [VPR-FIPS] vpr_method_fips_2048_single.txt: 200 keys - no duplicates and no shared factors - none ✅ [R03] [VPR-FIPS] vpr_method_fips_2048_parallel.txt: 200 keys - no duplicates and no shared factors - none ==================================================================================================== 1. [VPR-BPSW] Structural checks on 400 keys (RSA-2048) ==================================================================================================== ✅ [K01] p != q - 400/400 ✅ [K02] n = p*q - 400/400 ✅ [K03] exact length: p,q = b bits; n = 2b bits - 400/400 ✅ [K04] p,q >= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 400/400 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 400/400 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 400/400 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 400/400 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 400/400 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 400/400 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 400/400 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 400/400 ✅ [K12] Pollard p-1 does not factor n - 400/400 ✅ [K13] no ROCA-like fingerprint - 400/400 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 400/400 ==================================================================================================== 2. [VPR-BPSW] Population tests on 400 keys (800 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 79800 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.171 (r=11), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.573 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=0.903; chi2 p=0.78; single position detectable at deviation from 0.5 above ~0.087 (N=800) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=1 ✅ [T03] [VPR-BPSW:vpr_method_bpsw_2048_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(n) rho=-0.047 p=0.51; threshold 2.0e-04; detectable |rho| >~0.26 at N=200 ✅ [T03] [VPR-BPSW:vpr_method_bpsw_2048_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(n) rho=+0.117 p=0.0984; threshold 2.0e-04; detectable |rho| >~0.26 at N=200 ✅ [R03] [VPR-BPSW] vpr_method_bpsw_2048_single.txt: 200 keys - no duplicates and no shared factors - none ✅ [R03] [VPR-BPSW] vpr_method_bpsw_2048_parallel.txt: 200 keys - no duplicates and no shared factors - none ==================================================================================================== 1. [OpenSSL] Structural checks on 400 keys (RSA-2048) ==================================================================================================== ✅ [K01] p != q - 400/400 ✅ [K02] n = p*q - 400/400 ✅ [K03] exact length: p,q = b bits; n = 2b bits - 400/400 ✅ [K04] p,q >= ceil(sqrt(2)*2^(b-1)) (FIPS 186 bound) - 400/400 ✅ [K05] |p-q| > 2^(nlen/2-100) and Fermat factorization fails - 400/400 ✅ [K06] p,q prime: GMP(50) + strong-BPSW + independent Miller-Rabin (FIPS rounds) - 400/400 ✅ [K07] e odd, 2^16 < e < 2^256, gcd(e,p-1)=gcd(e,q-1)=1 - 400/400 ✅ [K08] e*d = 1 (mod lambda(n)) and d > 2^(nlen/2) - 400/400 ✅ [K09] CRT parameters consistent (dP, dQ, qInv) - 400/400 ✅ [K10] round-trip (plain and CRT) incl. boundary values - 400/400 ✅ [K11] gcd(p-1,q-1) small (<= 64 bits) - 400/400 ✅ [K12] Pollard p-1 does not factor n - 400/400 ✅ [K13] no ROCA-like fingerprint - 400/400 ✅ [K14] independent validator (OpenSSL/cryptography) accepts the key - 400/400 ==================================================================================================== 2. [OpenSSL] Population tests on 400 keys (800 primes) ==================================================================================================== ✅ [P01] shared primes / duplicate moduli (pairwise gcd) - 79800 pairs, none ✅ [P02] uniformity of p mod r (10 moduli, chi-square, Bonferroni) - min p=0.0233 (r=8), threshold 1.0e-04 ✅ [P03] monobit over middle bits (excluding first 8 and last) - p=0.476 ✅ [P04] per-position frequency: most extreme position (Bonferroni) and overall chi-square - min-p(Bonf)=0.903; chi2 p=0.532; single position detectable at deviation from 0.5 above ~0.087 (N=800) ✅ [P05] runs test (NIST SP 800-22), aggregated calibrated (sum and dispersion of Z) - p=0.994 ✅ [T03] [OpenSSL:openssl_2048_single.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: HW(q) rho=-0.041 p=0.561; threshold 2.0e-04; detectable |rho| >~0.26 at N=200 ✅ [T03] [OpenSSL:openssl_2048_parallel.txt] correlation of generation time vs key characteristics (Spearman, Bonferroni) - strongest: p mod 2^16 rho=-0.145 p=0.0403; threshold 2.0e-04; detectable |rho| >~0.26 at N=200 ✅ [R03] [OpenSSL] openssl_2048_single.txt: 200 keys - no duplicates and no shared factors - none ✅ [R03] [OpenSSL] openssl_2048_parallel.txt: 200 keys - no duplicates and no shared factors - none ==================================================================================================== 2a. Cross-check between generators ==================================================================================================== ✅ [P06] shared primes between keys of DIFFERENT generators - none ==================================================================================================== 2b. Bias in prime selection (distribution of the gap before the prime) ==================================================================================================== generating 400 reference uniform primes (1024 bits, MR rounds=5)... ✅ [B00] calibration: two independent halves of the reference (expected no difference) - KS p=0.112 ✅ [B01] [VPR-FIPS] mean gap before prime / reference - ratio 1.033 (95% CI 0.898-1.192); KS p=0.758; MWU p=0.564; N=400; minimum detectable effect ~±14% ✅ [B01] [VPR-BPSW] mean gap before prime / reference - ratio 1.037 (95% CI 0.903-1.194); KS p=0.7; MWU p=0.348; N=400; minimum detectable effect ~±14% ✅ [B01] [OpenSSL] mean gap before prime / reference - ratio 1.067 (95% CI 0.929-1.223); KS p=0.416; MWU p=0.149; N=400; minimum detectable effect ~±14% ==================================================================================================== 4. Measurement calibration (mandatory before trusting 4a) ==================================================================================================== ✅ [T00a] null control: identical distributions (should NOT be flagged) - max|t|=1.40; noise CV=5.9% ✅ [T00b] positive control: synthetic leak ~100% (should be flagged) - max|t|=395.75; diff +66.125% ==================================================================================================== 4a. [VPR-FIPS] Timing leak on private-key operations (dudect-style, 2000 measurements/pair) ==================================================================================================== ✅ [T01] gmpy2.powmod (plain, no CRT) | fixed vs random - raw t=0.60; crop max t=1.79; diff +0.160%; noise CV=6.0%; detectable effect >~1.22% ✅ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=0.66; crop max t=1.79; diff -0.208%; noise CV=7.0%; detectable effect >~1.43% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=0.41; crop max t=1.84; diff -0.121%; noise CV=6.6%; detectable effect >~1.35% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=2.46; crop max t=1.92; diff -0.628%; noise CV=5.7%; detectable effect >~1.15% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=0.82; crop max t=1.96; diff -0.507%; noise CV=13.8%; detectable effect >~2.80% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (~1.03% ✅ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=2.27; crop max t=1.24; diff +0.741%; noise CV=7.3%; detectable effect >~1.47% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=1.28; crop max t=3.05; diff -0.389%; noise CV=6.8%; detectable effect >~1.38% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=1.70; crop max t=2.20; diff -0.542%; noise CV=7.1%; detectable effect >~1.45% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=0.47; crop max t=1.34; diff +0.321%; noise CV=15.3%; detectable effect >~3.09% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small (~1.31% ⚠️ [T01] CRT without blinding (gmpy2) | fixed vs random - raw t=0.16; crop max t=5.96; diff +0.069%; noise CV=9.5%; detectable effect >~1.93% ✅ [T01] CRT with blinding (gmpy2) | fixed vs random - raw t=0.30; crop max t=1.00; diff -0.103%; noise CV=7.8%; detectable effect >~1.58% ✅ [T01] gmpy2.powmod_sec (constant-time) | fixed vs random - raw t=0.48; crop max t=2.35; diff -0.119%; noise CV=5.5%; detectable effect >~1.12% ✅ [T01] OpenSSL sign PKCS1v15 (via cryptography) | fixed vs random - raw t=0.19; crop max t=0.87; diff -0.118%; noise CV=14.1%; detectable effect >~2.83% ℹ️ [T02] INTERPRETATION - PASS only means 'no leak detected at this sample size and noise'. Python/GMP are not constant-time by construction; small ( K13 flags ✅ healthy key is NOT flagged by K13 (no false alarm) ✅ close p,q -> K05 flags (Fermat) ✅ smooth p-1 -> K12 flags (Pollard p-1) ✅ composite 'prime' -> K06 flags ✅ shared prime between two keys -> P01 flags ✅ corrupted CRT parameter -> K09 flags ✅ small d (Wiener-vulnerable) -> K07/K08 flag ✅ primes only ==1 (mod 4) -> P02 flags ✅ bit tests: random sample is NOT flagged (no false alarm) - monobit p=0.227; pos min-p(Bonf)=0.486; pos chi2 p=0.646; runs p=0.946 ✅ bit tests: bit 200 = 1 with probability 0.75 -> P04 flags - pos min-p(Bonf)=3.54e-22; pos chi2 p=0.15 (bias test: reference vs reference, and reference vs incremental search...) ✅ uniform vs uniform -> NOT flagged (no false alarm) - ratio 0.971, KS p=0.585 ✅ incremental search -> flagged as biased - ratio 1.847 (theory ~2.0), KS p=5.4e-18 ✅ fault attack: unverified CRT leaks a factor (tool works) - 10/10 ✅ fault attack: verified CRT leaks nothing ==================================================================================================== 4. Measurement calibration (mandatory before trusting 4a) ==================================================================================================== ✅ [T00a] null control: identical distributions (should NOT be flagged) - max|t|=0.98; noise CV=15.9% ✅ [T00b] positive control: synthetic leak ~100% (should be flagged) - max|t|=137.87; diff +66.985% (null control is clean) ==================================================================================================== SELF-TEST: 16/16 controls caught/clean Note: the positive timing control depends on machine noise - see T00b above.