123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440(*****************************************************************************)(* *)(* MIT License *)(* Copyright (c) 2023 Nomadic Labs <contact@nomadic-labs.com> *)(* *)(* Permission is hereby granted, free of charge, to any person obtaining a *)(* copy of this software and associated documentation files (the "Software"),*)(* to deal in the Software without restriction, including without limitation *)(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *)(* and/or sell copies of the Software, and to permit persons to whom the *)(* Software is furnished to do so, subject to the following conditions: *)(* *)(* The above copyright notice and this permission notice shall be included *)(* in all copies or substantial portions of the Software. *)(* *)(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *)(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *)(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *)(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *)(* DEALINGS IN THE SOFTWARE. *)(* *)(*****************************************************************************)openLang_stdlibopenSha2_variants(** Gadget implementing SHA2.
Specification can be found at
https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
Precious test vectors can be found at
https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values
under "Secure hashing".
*)moduletypeOp=sigmoduleL:LIBopenLtypetlvalband:tlrepr->tlrepr->tlreprtvalbnot:tlrepr->tlreprtvalxor_list:tlreprlist->tlreprtvalxor:tlrepr->tlrepr->tlreprtvalrotate_right:tlrepr->int->tlreprtvalshift_right:tlrepr->int->tlreprtvalof_bytes:Bytes.blrepr->tlreprtvalto_bytes:tlrepr->Bytes.blreprtvalto_scalar:tlrepr->scalarreprtvalof_scalar:total_nb_bits:int->scalarrepr->tlreprtvalof_constant:le:bool->bytes->tlreprtendmoduleOp_limbs_impl(L:LIB)=structmoduleL=LopenLmoduleLimbs=Limbs(structletnb_bits=4end)typetl=Limbs.slletxor_listlb=foldMLimbs.xor_lookup(List.hdlb)(List.tllb)letxorab=xor_list[a;b]letband=Limbs.band_lookupletbnot=Limbs.bnot_lookupletrotate_right=Limbs.rotate_right_lookupletshift_right=Limbs.shift_right_lookupletof_bytes=Limbs.of_bytesletto_bytes=Limbs.to_bytesletto_scalar=Limbs.to_scalarletof_scalar~total_nb_bitsx=with_label~label:"Sha2.of_scalar"@@Limbs.of_scalar~total_nb_bitsxletof_constant~lex=with_label~label:"Sha2.of_constant"@@Limbs.constant~lexendmoduleOp_bits_impl(L:LIB):OpwithmoduleL=Landtypetl=L.Bytes.bl=structmoduleL=LopenLtypetl=Bytes.blletxor_listlb=foldMBytes.xor(List.hdlb)(List.tllb)letxorab=xor_list[a;b]letband=Bytes.bandletbnot=Bytes.notletrotate_right=Bytes.rotate_rightletshift_right=Bytes.shift_rightletof_bytesx=retxletto_bytesx=retxletto_scalar=Num.scalar_of_bytesletof_scalar~total_nb_bitsx=bits_of_scalar~nb_bits:total_nb_bitsxletof_constant~lex=Bytes.constant~lexendmoduletypeOperations=functor(L:LIB)->OpwithmoduleL=LmoduleOp_limbs:Operations=Op_limbs_implmoduleOp_bits:Operations=Op_bits_implmoduleMAKE(L:LIB)(V:VARIANT)(Op:OpwithmoduleL=L)=structopenLmoduleM64=Gadget_mod_arith.ArithMod64(L)(* Utils *)letsplit_exactlyarraysize_chunknb_chunks=assert(Bytes.lengtharray=size_chunk*nb_chunks);letres=List.initnb_chunks(funi->letarray=Array.of_list(of_listarray)inletarray=Array.subarray(i*size_chunk)size_chunkinto_list(Array.to_listarray))inArray.of_list(List.revres)letdebug_toggle=falseletdebugmsgv=ifdebug_togglethendebugmsgvelseretunitletdebug_arraysa=ifdebug_togglethendebugsunit>*iterM(debug"")(Array.to_lista)elseretunit(* For now, we mainly focus on optimising SHA-512, so we have a special
case for a 64-bit word, where a modular addition gadget is used.
In the future, the same optimisation can be done for a 32-bit word. *)letadd_list(lb:Op.tlreprlist):Op.tlreprt=with_label~label:"Sha2.add_list"@@ifV.word_size=64thenletmod_int_of_bytes(x:Op.tlrepr):M64.mod_intreprt=let*sx=Op.to_scalarxinM64.mod_int_of_scalars(to_list[sx])inlet*lm=mapMmod_int_of_byteslbinlet*mres=foldMM64.add(List.hdlm)(List.tllm)inlet*sres=M64.scalars_of_mod_intmresinOp.of_scalar~total_nb_bits:V.word_size(List.hd(of_listsres))elselet*(lb:Bytes.blreprlist)=mapMOp.to_byteslbinlet*res=foldM(Bytes.add~ignore_carry:true)(List.hdlb)(List.tllb)inOp.of_bytesresletaddab=add_list[a;b](* Section 4.1.2
use six logical functions, where each function operates on 32-bit words,
which are represented as x, y, and z. The result of each function is a
new 32-bit word. For SHA-512, 64-bit operations are used. *)(* Ch(x, y, z) = (x && y) XOR ( !x && z) *)letchxyz=with_label~label:"Sha2.Ch"@@let*x_and_y=Op.bandxyinlet*not_x=Op.bnotxinlet*not_x_and_z=Op.bandnot_xzinOp.xorx_and_ynot_x_and_z(* Maj(x, y, z) = (x && y) XOR (x && z) XOR (y && z) *)letmajxyz=with_label~label:"Sha2.Maj"@@let*x_and_y=Op.bandxyinlet*x_and_z=Op.bandxzinlet*y_and_z=Op.bandyzinOp.xor_list[x_and_y;x_and_z;y_and_z](* Sum_0(x) = ROTR^{c0}(x) XOR ROTR^{c1}(x) XOR ROTR^{c2}(x) *)letsum_0x=with_label~label:"Sha2.Sum0"@@let*x0=Op.rotate_rightxV.sum_constants.(0)inlet*x1=Op.rotate_rightxV.sum_constants.(1)inlet*x2=Op.rotate_rightxV.sum_constants.(2)inOp.xor_list[x0;x1;x2](* Sum_1(x) = ROTR^{c3}(x) XOR ROTR^{c4}(x) XOR ROTR^{c5}(x) *)letsum_1x=with_label~label:"Sha2.Sum1"@@let*x0=Op.rotate_rightxV.sum_constants.(3)inlet*x1=Op.rotate_rightxV.sum_constants.(4)inlet*x2=Op.rotate_rightxV.sum_constants.(5)inOp.xor_list[x0;x1;x2](* Sigma_0(x) = ROTR^{d0}(x) XOR ROTR^{d1}(x) XOR SHR^{d2}(x) *)letsigma_0x=with_label~label:"Sha2.Sigma0"@@let*x0=Op.rotate_rightxV.sigma_constants.(0)inlet*x1=Op.rotate_rightxV.sigma_constants.(1)inlet*x2=Op.shift_rightxV.sigma_constants.(2)inOp.xor_list[x0;x1;x2](* Sigma_1(x) = ROTR^{d3}(x) XOR ROTR^{d4}(x) XOR SHR^{d5}(x) *)letsigma_1x=with_label~label:"Sha2.Sigma1"@@let*x0=Op.rotate_rightxV.sigma_constants.(3)inlet*x1=Op.rotate_rightxV.sigma_constants.(4)inlet*x2=Op.shift_rightxV.sigma_constants.(5)inOp.xor_list[x0;x1;x2](* Section 4.2.2 constants *)letks:Op.tlreprarrayt=with_label~label:"Sha2.ks"@@let*a=mapM(funs->Op.of_constant~le:false@@Utils.bytes_of_hexs)(Array.to_listV.round_constants)inret@@Array.of_lista(* Section 5.3 *)letinitial_hash:Op.tlreprarrayt=let*a=mapM(funs->Op.of_constant~le:false@@Utils.bytes_of_hexs)(Array.to_listV.init_hash)inret@@Array.of_lista(* Section 5.1.1 *)letpadding:Bytes.blrepr->Bytes.blreprt=funmsg->with_label~label:"Sha2.padding"@@letl=Bytes.lengthmsginletk=letk=(V.block_size-(2*V.word_size)-(l+1))modV.block_sizeinifk>0thenkelsek+V.block_sizeinlet*padding=letbitlist=List.(initk(Fun.constfalse)@[true])inBytes.constant~le:false@@Utils.of_bitlist~le:falsebitlistinlet*binary_l=letocaml_bytes=Z.of_intl|>Z.to_bits|>Stdlib.Bytes.of_stringinletocaml_bytes=letlen=Stdlib.Bytes.lengthocaml_bytesinletlen_padded=V.word_size/4iniflen=len_paddedthenocaml_byteselseletbytes_padded=Stdlib.Bytes.makelen_padded'\000'inStdlib.Bytes.blitocaml_bytes0bytes_padded0len;bytes_paddedinBytes.constant~le:trueocaml_bytesinret@@Bytes.concat[|msg;padding;binary_l|](* Section 5.2 *)letparsing:Bytes.blrepr->Bytes.blreprarrayarray=funmsg->letnb_blocks=Bytes.lengthmsg/V.block_sizein(* Split in blocks of V.block_size bits *)letblocks=split_exactlymsgV.block_sizenb_blocksin(* Split each block into 16 words of V.word_size bits *)Array.map(funblock->split_exactlyblockV.word_size16)blocks(* Section 6.2.2 step 1 *)letschedule:Op.tlreprarray->Op.tlreprarrayt=funmessage_block->assert(Array.lengthmessage_block=16);with_label~label:"Sha2.schedule"@@let*rest=let*res=mapM(fun_->Op.of_constant~le:falseStdlib.Bytes.empty)(List.init(V.loop_bound-16)Fun.id)inret@@Array.of_listresinletws=Array.appendmessage_blockrestinletrecauxt=ift=V.loop_boundthenret()else(* res = sigma_1 ws.(t - 2) + ws.(t - 7) + sigma_0 ws.(t - 15) + ws.(t - 16) *)let*res=let*tmp1=sigma_1ws.(t-2)inlet*tmp2=sigma_0ws.(t-15)inadd_list[tmp1;ws.(t-7);tmp2;ws.(t-16)]inws.(t)<-res;aux(succt)inlet*()=aux16inretwstypevars=Op.tlrepr*Op.tlrepr*Op.tlrepr*Op.tlrepr*Op.tlrepr*Op.tlrepr*Op.tlrepr*Op.tlreprletassign_variables:Op.tlreprarray->vars=funhs->leta=hs.(0)inletb=hs.(1)inletc=hs.(2)inletd=hs.(3)inlete=hs.(4)inletf=hs.(5)inletg=hs.(6)inleth=hs.(7)in(a,b,c,d,e,f,g,h)(* Section 6.2.2 step 3. *)letstep3_one_iterationt:vars->Op.tlreprarray->varst=fun(a,b,c,d,e,f,g,h)ws->let(+)=addinwith_label~label:"Sha2.step3_one_iteration"@@let*ksin(* t1 <- h + tmp_sum + tmp_ch + ks.(t) + ws.(t) *)let*t1=let*tmp_sum=sum_1einlet*tmp_ch=chefginadd_list[h;tmp_sum;tmp_ch;ks.(t);ws.(t)]inlet*t1_plus_t2=let*tmp_sum=sum_0ainlet*tmp_maj=majabcinadd_list[t1;tmp_sum;tmp_maj]inleth=ginletg=finletf=einlet*e=d+t1inletd=cinletc=binletb=ainleta=t1_plus_t2inret(a,b,c,d,e,f,g,h)letstep3:vars->Op.tlreprarray->varst=funvarsws->with_label~label:"Sha2.step3"@@letrecaux(acc:vars)t=ift=V.loop_boundthenretaccelselet*acc=step3_one_iterationtaccwsinauxacc(t+1)inauxvars0(* Section 6.2.2 step 4 *)letcompute_intermediate_hash:vars->Op.tlreprarray->Op.tlreprarrayt=fun(a,b,c,d,e,f,g,h)hs->with_label~label:"Sha2.compute_intermediate_hash"@@letvars=[a;b;c;d;e;f;g;h]inleths=Array.to_listhsinlet*res=map2Maddvarshsinret@@Array.of_listresletprocess_one_block:Op.tlreprarray->Op.tlreprarray->Op.tlreprarrayt=funblockhs->with_label~label:"Sha2.process_one_block"@@let*ws=scheduleblockinletvars=assign_variableshsinlet*vars=step3varswsincompute_intermediate_hashvarshsletdigest:Bytes.blrepr->L.Bytes.blreprt=funblocks->assert(Bytes.lengthblocksmod8=0);with_label~label:"Sha2.digest"@@let*blocks=paddingblocksinlet*_=debug"padding"blocksinletblocks=parsingblocksinlet*_=debug_array"parsing"blocks.(0)inlet*initial_hashinletrecprocess_blocks(acc:Op.tlreprarray)i=ifi=Array.lengthblocksthenretaccelselet*blocks_i=mapMOp.of_bytes(Array.to_listblocks.(i))inlet*acc=process_one_block(Array.of_listblocks_i)accinprocess_blocksacc(i+1)inlet*res=process_blocksinitial_hash0inlet*res=mapMOp.to_bytes(Array.to_listres)inletres=Array.sub(Array.of_listres)0V.digest_blocksinret@@Bytes.concatresendmoduletypeSHA2=functor(L:LIB)->sigopenLvaldigest:Bytes.blrepr->Bytes.blreprtendmoduleSHA224:SHA2=functor(L:LIB)->MAKE(L)(Sha224)(Op_bits(L))moduleSHA256:SHA2=functor(L:LIB)->MAKE(L)(Sha256)(Op_bits(L))moduleSHA384:SHA2=functor(L:LIB)->MAKE(L)(Sha384)(Op_limbs(L))moduleSHA512:SHA2=functor(L:LIB)->MAKE(L)(Sha512)(Op_limbs(L))