123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160(***********************************************************************)(* *)(* The Cryptokit library *)(* *)(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)(* *)(* Copyright 2002 Institut National de Recherche en Informatique et *)(* en Automatique. All rights reserved. This file is distributed *)(* under the terms of the GNU Library General Public License, with *)(* the special exception on linking described in file LICENSE. *)(* *)(***********************************************************************)(* Arithmetic on big integers, based on the ZArith library. *)typet=Z.texternalwipe:t->unit="caml_wipe_z"(* This is no longer used in Cryptokit. Kept for backward compatibility. *)letzero=Z.zeroletone=Z.oneletof_int=Z.of_intletcompare=Z.compareletadd=Z.addletsub=Z.subletmult=Z.mulletdiv=Z.divletmod_=Z.remletlcm=Z.lcmletmod_power=Z.powm_secletsub_modabp=letd=Z.subabinifZ.signd<0thenZ.adddpelsedletmod_inv=Z.invert(* This is still used. *)letrelative_primeab=Z.equal(Z.gcdab)Z.one(* Modular arithmetic *)letaddmabq=Z.(erem(a+b)q)letsubmabq=Z.(erem(a-b)q)letmulmabq=Z.(erem(a*b)q)letsqrmaq=Z.(erem(a*a)q)letinvm=Z.invertletdivmabq=mulma(Z.invertbq)qletpowm=Z.powm(* Modular exponentiation via the Chinese Remainder Theorem.
Compute a ^ d mod pq, where d is defined by
dp = d mod (p-1) and dq = d mod (q-1).
qinv is q^-1 mod p.
Formula:
mp = (a mod p)^dp mod p
mq = (a mod q)^dq mod q
m = ((((mp - mq) mod p) * qInv) mod p) * q + mq
*)letmod_power_CRTapqdpdqqinv=letamodp=Z.remapandamodq=Z.remaqinletmp=mod_poweramodpdppandmq=mod_poweramodqdqqinletdiff=sub_modmpmqpinletdiff_qinv=Z.muldiffqinvinletdiff_qinv_mod_p=Z.remdiff_qinvpinletres=Z.(add(mulqdiff_qinv_mod_p)mq)inwipeamodp;wipeamodq;(* It is possible that res == mq, so we cannot wipe mq.
For consistency we don't wipe any of the intermediate results
besides amodp and amodq. *)res(* Modular square root. Tonnelli-Shanks algorithm. *)letsqrtmnp=letrecfind_nonquadratic_residuez=ifZ.legendrezp=-1thenzelsefind_nonquadratic_residue(Z.succz)inletrecrepsquareit=ift=Z.onethenielserepsquare(i+1)(mulmttp)inletrecloopmctr=ift=Z.onethenSomerelsebeginleti=repsquare1(sqrmtp)inletb=powmc(Z.shift_leftZ.one(m-i-1))pinletbb=sqrmbpinloopibb(mulmtbbp)(mulmrbp)endinmatchZ.legendrenpwith|0->SomeZ.zero|-1->None|_(*1*)->lets=Z.trailing_zeros(Z.predp)inletq=Z.shift_right(Z.predp)sinletz=find_nonquadratic_residue(Z.of_int2)inloops(powmzqp)(powmnqp)(powmnZ.(succqasr1)p)(* Conversions to big-endian byte strings *)letwipe_bytess=Bytes.fills0(Bytes.lengths)'\000'letof_bytess=letl=String.lengthsinlett=Bytes.createlinfori=0tol-1doBytes.settis.[l-1-i]done;letn=Z.of_bits(Bytes.unsafe_to_stringt)inwipe_bytest;nletto_bytes?numbitsn=lets=Z.to_bitsninletl=matchnumbitswith|None->String.lengths|Somenb->assert(Z.numbitsn<=nb);(nb+7)/8inlett=String.initl(funi->letj=l-1-iinifj<String.lengthsthens.[j]else'\000')inwipe_bytes(Bytes.unsafe_of_strings);t(* Random number generation *)letchange_bytesif=Bytes.setsi(Char.chr(f(Char.code(Bytes.getsi))))letrandom~rng?(odd=false)numbits=letnumbytes=(numbits+7)/8inletbuf=Bytes.createnumbytesinrngbuf0numbytes;(* adjust low byte if requested *)ifoddthenchange_bytebuf0(funb->blor1);(* adjust high byte so that the number is exactly numbits long *)letmask=1lsl((numbits-1)land7)inchange_bytebuf(numbytes-1)(funb->(bland(mask-1))lormask);(* convert to a number *)letn=Z.of_bits(Bytes.unsafe_to_stringbuf)inwipe_bytesbuf;assert(Z.numbitsn=numbits);ifoddthenassert(Z.is_oddn);nletrecrandom_prime~rngnumbits=(* Generate random odd number *)letn=random~rng~odd:truenumbitsin(* Find next prime above n *)letp=Z.nextprimenin(* Make sure it has the right number of bits *)ifZ.numbitsp=numbitsthenpelserandom_prime~rngnumbitsletrecrandom_upto~rngbound=letn=Z.random_int_gen~fill:rngboundinifn=Z.zerothenrandom_upto~rngboundelsen