123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137(* Pfortuna is a re-implementation of Fortuna with a mutex. The goal of this
module is to provide a global and domain-safe RNG. The implementation use
[Miou.Mutex] instead of [Mutex] - [Pfortuna] is only available as part of
the [mirage-crypto-rng-mkernel] package. Thus, in the context of Miou,
[Pfortuna] can be used and recommended in place of [Fortuna], so that the
user can generate random numbers in parallel in several domains.
{[
let () = Miou_unix.run @@ fun () ->
let rng = Mirage_crypto_rng_mkernel.(initialize (module Pfortuna)) in
...
Mirage_crypto_rng_mkernel.kill rng
]}
NOTE: when modifying this file, please also check whether rng/fortuna.ml
needs to be updated. *)openMirage_cryptoopenMirage_crypto.UncommonmoduleSHAd256=structopenDigestiftypectx=SHA256.ctxletempty=SHA256.emptyletgett=SHA256.(gett|>to_raw_string|>digest_string|>to_raw_string)letdigestii=SHA256.(digesti_stringi|>to_raw_string|>digest_string|>to_raw_string)letfeedi=SHA256.feedi_stringendletblock=16(* the minimal amount of bytes in a pool to trigger a reseed *)letmin_pool_size=64(* the minimal duration between two reseeds *)letmin_time_duration=1_000_000_000L(* number of pools *)letpools=32typet={ctr:AES.CTR.ctr;secret:string;key:AES.CTR.key;pools:SHAd256.ctxarray;pool0_size:int;reseed_count:int;last_reseed:int64;time:(unit->int64)option}typeg=Miou.Mutex.t*trefletupdate(m,g)fn=Miou.Mutex.protectm@@fun()->g:=fn!gletget(m,g)fn=Miou.Mutex.protectm@@fun()->fn!gletcreate?time()=letsecret=String.make32'\000'inletm=Miou.Mutex.create()inlett={ctr=(0L,0L);secret;key=AES.CTR.of_secretsecret;pools=Array.makepoolsSHAd256.empty;pool0_size=0;reseed_count=0;last_reseed=0L;time}in(m,{contents=t})letseeded~t=letlo,hi=t.ctrinnot(Int64.equallo0L&&Int64.equalhi0L)letset_key~tsecret={twithsecret;key=AES.CTR.of_secretsecret}letreseedi~titer=lett=set_key~t(SHAd256.digesti(funfn->fnt.secret;iterfn))in{twithctr=AES.CTR.add_ctrt.ctr1L}letiter1af=faletreseed~tcs=reseedi~t(iter1cs)letgenerate_rekey~tbuf~offlen=letb=len//block*2inletn=b*blockinletr=AES.CTR.stream~key:t.key~ctr:t.ctrninBytes.unsafe_blit_stringr0bufofflen;letr2=String.subr(n-32)32inlett=set_key~tr2in{twithctr=AES.CTR.add_ctrt.ctr(Int64.of_intb)}letadd_pool_entropyt=ift.pool0_size>min_pool_sizethenletshould_reseed,now=matcht.timewith|None->true,0L|Somefn->letnow=fn()inInt64.(subnowt.last_reseed>min_time_duration),nowinifshould_reseedthenbeginlett={twithreseed_count=t.reseed_count+1;last_reseed=now;pool0_size=0}inreseedi~t@@funadd->fori=0topools-1doift.reseed_countland((1lsli)-1)=0then(SHAd256.gett.pools.(i)|>add;t.pools.(i)<-SHAd256.empty)doneendelsetelsetletgenerate_into~tbuf~offlen=lett=add_pool_entropytinifnot(seeded~t)thenraiseMirage_crypto_rng.Unseeded_generator;letrecchunktoff=function|iwheni<=0->t|n->letn'=iminn0x10000inlett=generate_rekey~tbuf~offn'inchunkt(off+n')(n-n')inchunktofflenletadd~tsource~pooldata=letbuf=Bytes.create2andpool=poolland(pools-1)andsource=Mirage_crypto_rng.Entropy.idsourceland0xffinBytes.set_uint8buf0source;Bytes.set_uint8buf1(String.lengthdata);t.pools.(pool)<-SHAd256.feedit.pools.(pool)(iter2(Bytes.unsafe_to_stringbuf)data);ifpool=0then{twithpool0_size=t.pool0_size+String.lengthdata}elsetletaccumulate~gsource=letpool=ref0in`Acc(funbuf->updateg@@funt->lett=add~tsource~pool:!poolbufinincrpool;t)letreseed~gcs=updateg@@funt->reseed~tcsletgenerate_into~gbuf~offlen=updateg@@funt->generate_into~tbuf~offlenletseeded~g=getg@@funt->seeded~t