chidori-pow/src/prime.rs
Rin Cat (鈴猫) 4b71ad4d75
init
Signed-off-by: Rin Cat (鈴猫) <rincat@rincat.dev>
2025-02-10 02:04:47 -05:00

162 lines
11 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use num_bigint::{BigUint, RandBigInt, ToBigUint};
use num_integer::Integer;
use num_traits::One;
use rand::thread_rng;
/// MillerRabin primality test
/// https://en.wikipedia.org/wiki/Miller-Rabin_primality_test
///
/// `k` is the number of iterations (more iterations → higher confidence).
/// This is a basic implementation for odd candidate `n` greater than 2.
pub fn is_probably_prime(n: &BigUint, k: u32) -> bool {
// Handle small numbers.
if n == &2u32.to_biguint().unwrap() {
return true;
}
if n < &2u32.to_biguint().unwrap() || n.is_even() {
return false;
}
// Write n 1 as d * 2^r.
let one: BigUint = One::one();
let two: BigUint = 2u32.to_biguint().unwrap();
let n_minus_one = n - &one;
let mut d = n_minus_one.clone();
let mut r = 0u32;
while d.is_even() {
d /= &two;
r += 1;
}
// Try k random witnesses.
let mut rng = thread_rng();
'witness_loop: for _ in 0..k {
// Choose a random a in [2, n-2]
let a = rng.gen_biguint_range(&two, &(n - &two));
// Compute x = a^d mod n.
let mut x = a.modpow(&d, n);
if x == one || x == n_minus_one {
continue 'witness_loop;
}
// Repeat squaring x up to r 1 times.
for _ in 0..(r - 1) {
x = x.modpow(&two, n);
if x == n_minus_one {
continue 'witness_loop;
}
}
// If we never hit n 1, then composite.
return false;
}
true
}
pub fn generate_prime_mod_3_4(bits: u64, rounds: u32) -> BigUint {
let mut rng = thread_rng();
let four: BigUint = 4u32.to_biguint().unwrap();
let three: BigUint = 3u32.to_biguint().unwrap();
loop {
// Generate a random number of the required bit-length.
let mut candidate = rng.gen_biguint(bits);
// Force the most significant bit to ensure it is exactly `bits` long.
candidate.set_bit(bits - 1, true);
// Force the number to be odd.
candidate.set_bit(0, true);
// Adjust candidate so that candidate mod 4 == 3.
// Compute candidate mod 4.
let rem = &candidate % &four;
if rem != three {
// Calculate the adjustment needed.
// We want candidate + adj ≡ 3 (mod 4); that is, adj ≡ (3 - rem) mod 4.
let adj = if three >= rem {
&three - &rem
} else {
&three + &four - &rem
};
candidate += &adj;
}
// Now candidate mod 4 should equal 3.
if &candidate % &four != three {
continue; // Should not happen, but be safe.
}
// Use MillerRabin to test candidate for primality.
if is_probably_prime(&candidate, rounds) {
return candidate;
}
// Otherwise, try another candidate.
}
}
#[cfg(test)]
mod tests {
use super::*;
/// This test has chance of false positives since it's probabilistic.
#[test]
fn test_is_probably_prime() {
let primes: Vec<BigUint> = vec![
BigUint::parse_bytes(b"723646214863847842402314246121044767400617866176733021174245260448070467161519753555151305391831172396032179266088879736498934532967238875067731186605319314486487094813782345277515046149035823394700558031365128080643117834402421935144013956523482034192169360458395261772557972018417296402072764848759", 10).unwrap(),
BigUint::parse_bytes(b"268263962333296278340388301081833650583348564229009436402694247537863120457689419730666587700766950987474095807568632415133217325374788947918148879191904084190506645642271822238922848768059332086841470078498514866531550241226838886983034780850983546266212727522552823301120544245546076442247019621281", 10).unwrap(),
BigUint::parse_bytes(b"780316979179005788838703471892068793371544768939561076378203820641657870400087138649016384860083841124919558376637164999621109684339044217326208775683279745886968384731662626620658025388083028243454834224644661932974516055783125538555017192574377520720286008648938568231979020364719643484647961902129", 10).unwrap(),
BigUint::parse_bytes(b"783671025198992682112959634964787905698619719557738373194519536366273453103042115144772610507228390203926825435595621395808116267173935555322692156198469029023864294375800526944184191931093378457858882431220722817775243587676744651835166948761514278051800658105050374249135721906202152414030775826309", 10).unwrap(),
BigUint::parse_bytes(b"114288406837499406547552732741383061052894863572677735626102568465969919170114402741547085384743856605779595326952640244199273564193341729111945397295523754661407891360178372372116312851760319552575377963248694758420962490016988707765874984334711407905393818994588867816690596598910287994599226773623", 10).unwrap(),
BigUint::parse_bytes(b"961901548037893583081258840295866034390773310176366253775445667810599851064250586050396339468859430600645234941487066774638210060293941746865942337333205982508422664447189855574952850359167644652778348230933876780699478432665987194022576502478048244264535150875567551113136844280086912348903874971289", 10).unwrap(),
BigUint::parse_bytes(b"810327961783413779923342902853291047828538610393840061747638982695517616363172610276265437838324082720309171557216227906072585908285037423898327771759304451581073313718250825722376959089099901152629294365285556214142316224671295775214712025501417340129162614998356447407556284614940830419343154596069", 10).unwrap(),
BigUint::parse_bytes(b"454714144122862528239309790149196885616593324828166685793258814905365447620370501511767118061263049961557700030750456903650018330037078841617668619030424125469578524711042124583419218861229437961029104448364686795365893249168551162671874789888134646816513322705015243576954880991642886515757494254667", 10).unwrap(),
BigUint::parse_bytes(b"299912922453745379330945382700853905095713797966780365239274055194104040679024227632579691012245229549449540626544013218573948479074373395273033811581117143614653378585775286148244890322917470335843727405772749436431799738228530579087880286816355000236580795379259714642327676881458540815945300666161", 10).unwrap(),
BigUint::parse_bytes(b"376003947134918788492667049006858112525261477602024892881243407277975729638079808355736777378039431492585839429794604176393826090578267002411236819553297345047777740428504027180799354733010246832276383653403857249078308266896127980484004668688670243017355407377733246174535348005452263544141849401979", 10).unwrap(),
];
for p in primes.iter() {
assert!(is_probably_prime(&p, 64));
}
}
#[test]
fn test_is_probably_prime_composites() {
let composites: Vec<BigUint> = vec![
BigUint::parse_bytes(b"376003947134918788492667049006858112525261477602024892881243407277975729638079808355736777378039431492585839429794604176393826090578267002411236819553297345047777740428504027180799354733010246832276383653403857249078308266896127980484004668688670243017355407377733246174535348005452263544141849401978", 10).unwrap(),
BigUint::parse_bytes(b"215231561558920190548208026347283855893349614579652886715109041246408707968244796209282350597895810733095104924634951652047270639587011501367107805226413278625593440341692931130648759932592989139786464960914139157114834353651728654113090571685005692273343539839816029944416477359630133856900278127196", 10).unwrap(),
BigUint::parse_bytes(b"749740332669263695852915686823796627478248903975029332069160620969447428491611565267586339403720492925006681992807328998947128762023955011291116754751950431300094251835880417511251321687004478207880237612242291350451356839420408200814283441229483559638270099621526259859618448556951589488935694922070", 10).unwrap(),
BigUint::parse_bytes(b"169634000834057905687114185586149242948139045677318813563962611831640519981755545740448510103217026033366710697221717847963076219297087916994382041061663714750161811176003440851268811309999453614154110754445690358783609141283608528146586702857821814537749897378979649737926839924019514134727091160596", 10).unwrap(),
BigUint::parse_bytes(b"391405423376058365603029493927540848246012397355482118997842261901662598117428494623814607612916906669639217808344280832395767938093281898200653708872133101732068607931282089810121182927184303240249371585726676226781755240078640338182784311458749444597839572385973551700960543208703821367720798778072", 10).unwrap(),
BigUint::parse_bytes(b"145039293606441299096899867755014610341218018203667264841882003871966393556065187657468909971071812669502147831045878456015615749981859567162502647235340765709384621383450245978156289943326007863506929089750917534169817946092363165262671439744821303778355013113872010222371955198429114197736184765704", 10).unwrap(),
BigUint::parse_bytes(b"338068116382585626410927100767745326752329150652235801367160858233547248828235927245043642851496668587705981602011646708907509708706737409743516100542296115834435744682149323258036975223060910642424686044835405231352594163800211063010578142263828931218684092895126045712869105445627601057623777552090", 10).unwrap(),
BigUint::parse_bytes(b"733953124218193165395301551993038545406364629775221343783594613066305176245028773006556151249847554247355736690261867825939967080139988834452933842378528382325603700037881199289040333697671258970839307337751547799788219379733886335171402446580393040976829094147103849993165402411253055490791297710398", 10).unwrap(),
BigUint::parse_bytes(b"913203980385282094763740857489413119990150673887971726908661546534253494037072205386715229605079865032590651982202933180796243044488005878787438030787026922448019409247593693512864665427511952738234654431289764435314410753974607679676812065736404760888000826732781882360969951775729150509866946286836", 10).unwrap(),
BigUint::parse_bytes(b"232672468411571864128377684111610045835673835338188777956578101157986193934981310713021354012943502884801943521144084387464428198189142123559463576210566925881741632803965311463508844160091019108613782710387285819179522210515150555890570342556044250014755588708967402313183475521240406597634471243814", 10).unwrap(),
];
for c in composites.iter() {
assert!(!is_probably_prime(&c, 64));
}
}
#[test]
fn test_generate_prime_mod_3_4() {
let bits = 2048;
let rounds = 64;
let prime = generate_prime_mod_3_4(bits, rounds);
assert!(is_probably_prime(&prime, rounds));
assert_eq!(prime.bits(), bits);
assert_eq!(
&prime % 4u32.to_biguint().unwrap(),
3u32.to_biguint().unwrap()
);
}
}