123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535(***********************************************************************)(* *)(* 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. *)(* *)(***********************************************************************)(* Utilities *)letseq_equal(len:'a->int)(get:'a->int->char)(s1:'a)(s2:'a)=letl=lens1inletrecequaliaccu=ifi>=lthenaccu=0elseequal(i+1)(acculor((Char.code(gets1i))lxor(Char.code(gets2i))))inl=lens2&&equal00letstring_equal=seq_equalString.lengthString.getletbytes_equal=seq_equalBytes.lengthBytes.getletwipe_bytess=Bytes.fills0(Bytes.lengths)'\000'letwipe_strings=wipe_bytes(Bytes.unsafe_of_strings)letshl1_bytessrcsoffdstdofflen=letrecshl1carryi=ifi>=0thenbeginletn=Char.code(Bytes.getsrc(soff+i))inBytes.setdst(doff+i)(Char.unsafe_chr((nlsl1)lorcarry));shl1(nlsr7)(i-1)endinshl10(len-1)(* Error reporting *)typeerror=|Wrong_key_size|Wrong_IV_size|Wrong_data_length|Bad_padding|Output_buffer_overflow|Incompatible_block_size|Number_too_long|Seed_too_short|Message_too_long|Bad_encoding|Compression_errorofstring*string|No_entropy_source|Entropy_source_closed|Compression_not_supportedexceptionErroroferrorlet_=Callback.register_exception"Cryptokit.Error"(ErrorWrong_key_size)(* Interface with C *)typedir=Encrypt|Decryptexternalxor_bytes:bytes->int->bytes->int->int->unit="caml_xor_string"externalxor_string:string->int->bytes->int->int->unit="caml_xor_string"externalaes_cook_encrypt_key:string->bytes="caml_aes_cook_encrypt_key"externalaes_cook_decrypt_key:string->bytes="caml_aes_cook_decrypt_key"externalaes_encrypt:bytes->bytes->int->bytes->int->unit="caml_aes_encrypt"externalaes_decrypt:bytes->bytes->int->bytes->int->unit="caml_aes_decrypt"externalblowfish_cook_key:string->bytes="caml_blowfish_cook_key"externalblowfish_encrypt:bytes->bytes->int->bytes->int->unit="caml_blowfish_encrypt"externalblowfish_decrypt:bytes->bytes->int->bytes->int->unit="caml_blowfish_decrypt"externaldes_cook_key:string->int->dir->bytes="caml_des_cook_key"externaldes_transform:bytes->bytes->int->bytes->int->unit="caml_des_transform"externalarcfour_cook_key:string->bytes="caml_arcfour_cook_key"externalarcfour_transform:bytes->bytes->int->bytes->int->int->unit="caml_arcfour_transform_bytecode""caml_arcfour_transform"externalchacha20_cook_key:string->bytes->int64->bytes="caml_chacha20_cook_key"externalchacha20_transform:bytes->bytes->int->bytes->int->int->unit="caml_chacha20_transform_bytecode""caml_chacha20_transform"externalchacha20_extract:bytes->bytes->int->int->unit="caml_chacha20_extract"externalsha1_init:unit->bytes="caml_sha1_init"externalsha1_update:bytes->bytes->int->int->unit="caml_sha1_update"externalsha1_final:bytes->string="caml_sha1_final"externalsha256_init:unit->bytes="caml_sha256_init"externalsha224_init:unit->bytes="caml_sha224_init"externalsha256_update:bytes->bytes->int->int->unit="caml_sha256_update"externalsha256_final:bytes->string="caml_sha256_final"externalsha224_final:bytes->string="caml_sha224_final"externalsha512_init:unit->bytes="caml_sha512_init"externalsha384_init:unit->bytes="caml_sha384_init"externalsha512_update:bytes->bytes->int->int->unit="caml_sha512_update"externalsha512_final:bytes->string="caml_sha512_final"externalsha384_final:bytes->string="caml_sha384_final"typesha3_contextexternalsha3_init:int->sha3_context="caml_sha3_init"externalsha3_absorb:sha3_context->bytes->int->int->unit="caml_sha3_absorb"externalsha3_extract:bool->sha3_context->string="caml_sha3_extract"externalsha3_wipe:sha3_context->unit="caml_sha3_wipe"externalripemd160_init:unit->bytes="caml_ripemd160_init"externalripemd160_update:bytes->bytes->int->int->unit="caml_ripemd160_update"externalripemd160_final:bytes->string="caml_ripemd160_final"externalmd5_init:unit->bytes="caml_md5_init"externalmd5_update:bytes->bytes->int->int->unit="caml_md5_update"externalmd5_final:bytes->string="caml_md5_final"externalblake2b_init:int->string->bytes="caml_blake2b_init"externalblake2b_update:bytes->bytes->int->int->unit="caml_blake2b_update"externalblake2b_final:bytes->int->string="caml_blake2b_final"externalblake2s_init:int->string->bytes="caml_blake2s_init"externalblake2s_update:bytes->bytes->int->int->unit="caml_blake2s_update"externalblake2s_final:bytes->int->string="caml_blake2s_final"typeghash_contextexternalghash_init:bytes->ghash_context="caml_ghash_init"externalghash_mult:ghash_context->bytes->unit="caml_ghash_mult"externalpoly1305_init:bytes->bytes="caml_poly1305_init"externalpoly1305_update:bytes->bytes->int->int->unit="caml_poly1305_update"externalpoly1305_final:bytes->string="caml_poly1305_final"externalsiphash_init:string->int->bytes="caml_siphash_init"externalsiphash_update:bytes->bytes->int->int->unit="caml_siphash_update"externalsiphash_final:bytes->int->string="caml_siphash_final"typeblake3_contextexternalblake3_init:string->blake3_context="caml_blake3_init"externalblake3_update:blake3_context->bytes->int->int->unit="caml_blake3_update"externalblake3_final:blake3_context->int->string="caml_blake3_extract"externalblake3_wipe:blake3_context->unit="caml_blake3_wipe"(* Abstract transform type *)classtypetransform=objectmethodinput_block_size:intmethodoutput_block_size:intmethodput_substring:bytes->int->int->unitmethodput_string:string->unitmethodput_char:char->unitmethodput_byte:int->unitmethodfinish:unitmethodflush:unitmethodavailable_output:intmethodget_string:stringmethodget_substring:bytes*int*intmethodget_char:charmethodget_byte:intmethodwipe:unitendlettransform_stringtrs=tr#put_strings;tr#finish;letr=tr#get_stringintr#wipe;rlettransform_channeltr?lenicoc=letibuf=Bytes.create256inletrectransf_to_eof()=letr=inputicibuf0256inifr>0thenbegintr#put_substringibuf0r;let(obuf,opos,olen)=tr#get_substringinoutputocobufoposolen;transf_to_eof()endandtransf_boundednumleft=ifnumleft>0thenbeginletr=inputicibuf0(min256numleft)inifr=0thenraiseEnd_of_file;tr#put_substringibuf0r;let(obuf,opos,olen)=tr#get_substringinoutputocobufoposolen;transf_bounded(numleft-r)endinbeginmatchlenwithNone->transf_to_eof()|Somel->transf_boundedlend;wipe_bytesibuf;tr#finish;let(obuf,opos,olen)=tr#get_substringinoutputocobufoposolen;tr#wipeclasscompose(tr1:transform)(tr2:transform)=object(self)methodinput_block_size=tr1#input_block_sizemethodoutput_block_size=tr2#output_block_sizemethodput_substringbufofslen=tr1#put_substringbufofslen;self#transfermethodput_strings=tr1#put_strings;self#transfermethodput_charc=tr1#put_charc;self#transfermethodput_byteb=tr1#put_byteb;self#transfermethodprivatetransfer=let(buf,ofs,len)=tr1#get_substringintr2#put_substringbufofslenmethodavailable_output=tr2#available_outputmethodget_string=tr2#get_stringmethodget_substring=tr2#get_substringmethodget_char=tr2#get_charmethodget_byte=tr2#get_bytemethodflush=tr1#flush;self#transfer;tr2#flushmethodfinish=tr1#finish;self#transfer;tr2#finishmethodwipe=tr1#wipe;tr2#wipeendletcomposetr1tr2=newcomposetr1tr2classtypehash=objectmethodhash_size:intmethodadd_substring:bytes->int->int->unitmethodadd_string:string->unitmethodadd_char:char->unitmethodadd_byte:int->unitmethodresult:stringmethodwipe:unitendlethash_stringhashs=hash#add_strings;letr=hash#resultinhash#wipe;rlethash_channelhash?lenic=letibuf=Bytes.create256inletrechash_to_eof()=letr=inputicibuf0256inifr>0thenbeginhash#add_substringibuf0r;hash_to_eof()endandhash_boundednumleft=ifnumleft>0thenbeginletr=inputicibuf0(min256numleft)inifr=0thenraiseEnd_of_file;hash#add_substringibuf0r;hash_bounded(numleft-r)endinbeginmatchlenwithNone->hash_to_eof()|Somel->hash_boundedlend;wipe_bytesibuf;letres=hash#resultinhash#wipe;resclasstypeauthenticated_transform=objectmethodinput_block_size:intmethodoutput_block_size:intmethodtag_size:intmethodput_substring:bytes->int->int->unitmethodput_string:string->unitmethodput_char:char->unitmethodput_byte:int->unitmethodfinish_and_get_tag:stringmethodavailable_output:intmethodget_string:stringmethodget_substring:bytes*int*intmethodget_char:charmethodget_byte:intmethodwipe:unitendletauth_transform_string_detachedtrs=tr#put_strings;lettag=tr#finish_and_get_taginlettxt=tr#get_stringintr#wipe;(txt,tag)letauth_transform_stringtrs=let(txt,tag)=auth_transform_string_detachedtrsintxt^tagletauth_check_transform_stringtrs=letls=String.lengthsinletlt=tr#tag_sizeinifls<ltthenraise(ErrorWrong_data_length);tr#put_string(String.subs0(ls-lt));lettag=tr#finish_and_get_taginletres=ifstring_equaltag(String.subs(ls-lt)lt)thenSome(tr#get_string)elseNoneintr#wipe;res(* Generic handling of output buffering *)classbuffered_outputinitial_buffer_size=object(self)valmutableobuf=Bytes.createinitial_buffer_sizevalmutableobeg=0valmutableoend=0methodprivateensure_capacityn=letlen=Bytes.lengthobufinifoend+n>lenthenbeginifoend-obeg+n<lenthenbeginBytes.blitobufobegobuf0(oend-obeg);oend<-oend-obeg;obeg<-0endelsebeginletnewlen=ref(2*len)inwhileoend-obeg+n>(!newlen)donewlen:=(!newlen)*2done;if(!newlen)>Sys.max_string_lengththenbeginif(oend-obeg+n)<=Sys.max_string_lengththennewlen:=Sys.max_string_lengthelseraise(ErrorOutput_buffer_overflow)end;letnewbuf=Bytes.create(!newlen)inBytes.blitobufobegnewbuf0(oend-obeg);obuf<-newbuf;oend<-oend-obeg;obeg<-0endendmethodavailable_output=oend-obegmethodget_substring=letres=(obuf,obeg,oend-obeg)inobeg<-0;oend<-0;resmethodget_string=letres=Bytes.sub_stringobufobeg(oend-obeg)inobeg<-0;oend<-0;resmethodget_char=ifobeg>=oendthenraiseEnd_of_file;letr=Bytes.getobufobeginobeg<-obeg+1;rmethodget_byte=Char.codeself#get_charmethodwipe=wipe_bytesobufend(* Combining a transform and a hash to get an authenticated transform *)classtransform_then_hash(tr:transform)(h:hash)=object(self)inheritbuffered_output256asoutput_buffermethodprivatetransfer=let(buf,ofs,len)=tr#get_substringinh#add_substringbufofslen;self#ensure_capacitylen;Bytes.blitbufofsobufoendlen;oend<-oend+lenmethodinput_block_size=tr#input_block_sizemethodoutput_block_size=tr#output_block_sizemethodtag_size=h#hash_sizemethodput_substringbufofslen=tr#put_substringbufofslen;self#transfermethodput_strings=tr#put_strings;self#transfermethodput_charc=tr#put_charc;self#transfermethodput_byteb=tr#put_byteb;self#transfermethodfinish_and_get_tag=tr#finish;self#transfer;h#resultmethodwipe=output_buffer#wipe;tr#wipe;h#wipeendlettransform_then_hashtrh=newtransform_then_hashtrhclasstransform_and_hash(tr:transform)(h:hash)=object(self)methodinput_block_size=tr#input_block_sizemethodoutput_block_size=tr#output_block_sizemethodtag_size=h#hash_sizemethodput_substringbufofslen=tr#put_substringbufofslen;h#add_substringbufofslenmethodput_strings=tr#put_strings;h#add_stringsmethodput_charc=tr#put_charc;h#add_charcmethodput_byteb=tr#put_byteb;h#add_bytebmethodfinish_and_get_tag=tr#finish;h#resultmethodwipe=tr#wipe;h#wipemethodavailable_output=tr#available_outputmethodget_substring=tr#get_substringmethodget_string=tr#get_stringmethodget_char=tr#get_charmethodget_byte=tr#get_byteendlettransform_and_hashtrh=newtransform_and_hashtrh(* Padding schemes *)modulePadding=structclasstypescheme=objectmethodpad:bytes->int->unitmethodstrip:bytes->intendclasslength=objectmethodpadbufferused=letn=Bytes.lengthbuffer-usedinassert(n>0&&n<256);Bytes.fillbufferusedn(Char.chrn)methodstripbuffer=letblocksize=Bytes.lengthbufferinletn=Char.code(Bytes.getbuffer(blocksize-1))inifn=0||n>blocksizethenraise(ErrorBad_padding);(* Characters blocksize - n to blocksize - 1 must be equal to n *)fori=blocksize-ntoblocksize-2doifChar.code(Bytes.getbufferi)<>nthenraise(ErrorBad_padding)done;blocksize-nendletlength=newlengthclass_8000=objectmethodpadbufferused=Bytes.setbufferused'\128';fori=used+1toBytes.lengthbuffer-1doBytes.setbufferi'\000'donemethodstripbuffer=letrecstrippos=ifpos<0thenraise(ErrorBad_padding)elsematchBytes.getbufferposwith'\128'->pos|'\000'->strip(pos-1)|_->raise(ErrorBad_padding)instrip(Bytes.lengthbuffer-1)endlet_8000=new_8000end(* Block ciphers *)moduleBlock=structclasstypeblock_cipher=objectmethodblocksize:intmethodtransform:bytes->int->bytes->int->unitmethodwipe:unitendclassaes_encryptkey=objectvalckey=letkl=String.lengthkeyinifkl=16||kl=24||kl=32thenaes_cook_encrypt_keykeyelseraise(ErrorWrong_key_size)methodblocksize=16methodtransformsrcsrc_ofsdstdst_ofs=ifsrc_ofs<0||src_ofs>Bytes.lengthsrc-16||dst_ofs<0||dst_ofs>Bytes.lengthdst-16theninvalid_arg"aes#transform";aes_encryptckeysrcsrc_ofsdstdst_ofsmethodwipe=wipe_bytesckey;Bytes.setckey(Bytes.lengthckey-1)'\016'endclassaes_decryptkey=objectvalckey=letkl=String.lengthkeyinifkl=16||kl=24||kl=32thenaes_cook_decrypt_keykeyelseraise(ErrorWrong_key_size)methodblocksize=16methodtransformsrcsrc_ofsdstdst_ofs=ifsrc_ofs<0||src_ofs>Bytes.lengthsrc-16||dst_ofs<0||dst_ofs>Bytes.lengthdst-16theninvalid_arg"aes#transform";aes_decryptckeysrcsrc_ofsdstdst_ofsmethodwipe=wipe_bytesckey;Bytes.setckey(Bytes.lengthckey-1)'\016'endclassblowfish_encryptkey=objectvalckey=letkl=String.lengthkeyinifkl>=4&&kl<=56thenblowfish_cook_keykeyelseraise(ErrorWrong_key_size)methodblocksize=8methodtransformsrcsrc_ofsdstdst_ofs=ifsrc_ofs<0||src_ofs>Bytes.lengthsrc-8||dst_ofs<0||dst_ofs>Bytes.lengthdst-8theninvalid_arg"blowfish#transform";blowfish_encryptckeysrcsrc_ofsdstdst_ofsmethodwipe=wipe_bytesckeyendclassblowfish_decryptkey=objectvalckey=letkl=String.lengthkeyinifkl>=4&&kl<=56thenblowfish_cook_keykeyelseraise(ErrorWrong_key_size)methodblocksize=8methodtransformsrcsrc_ofsdstdst_ofs=ifsrc_ofs<0||src_ofs>Bytes.lengthsrc-8||dst_ofs<0||dst_ofs>Bytes.lengthdst-8theninvalid_arg"blowfish#transform";blowfish_decryptckeysrcsrc_ofsdstdst_ofsmethodwipe=wipe_bytesckeyendclassdesdirectionkey=objectvalckey=ifString.lengthkey=8thendes_cook_keykey0directionelseraise(ErrorWrong_key_size)methodblocksize=8methodtransformsrcsrc_ofsdstdst_ofs=ifsrc_ofs<0||src_ofs>Bytes.lengthsrc-8||dst_ofs<0||dst_ofs>Bytes.lengthdst-8theninvalid_arg"des#transform";des_transformckeysrcsrc_ofsdstdst_ofsmethodwipe=wipe_bytesckeyendclassdes_encrypt=desEncryptclassdes_decrypt=desDecryptclasstriple_des_encryptkey=let_=letkl=String.lengthkeyinifkl<>16&&kl<>24thenraise(ErrorWrong_key_size)inletckey1=des_cook_keykey0Encryptinletckey2=des_cook_keykey8Decryptinletckey3=ifString.lengthkey=24thendes_cook_keykey16Encryptelseckey1inobjectmethodblocksize=8methodtransformsrcsrc_ofsdstdst_ofs=ifsrc_ofs<0||src_ofs>Bytes.lengthsrc-8||dst_ofs<0||dst_ofs>Bytes.lengthdst-8theninvalid_arg"triple_des#transform";des_transformckey1srcsrc_ofsdstdst_ofs;des_transformckey2dstdst_ofsdstdst_ofs;des_transformckey3dstdst_ofsdstdst_ofsmethodwipe=wipe_bytesckey1;wipe_bytesckey2;wipe_bytesckey3endclasstriple_des_decryptkey=let_=letkl=String.lengthkeyinifkl<>16&&kl<>24thenraise(ErrorWrong_key_size)inletckey3=des_cook_keykey0Decryptinletckey2=des_cook_keykey8Encryptinletckey1=ifString.lengthkey=24thendes_cook_keykey16Decryptelseckey3inobjectmethodblocksize=8methodtransformsrcsrc_ofsdstdst_ofs=ifsrc_ofs<0||src_ofs>Bytes.lengthsrc-8||dst_ofs<0||dst_ofs>Bytes.lengthdst-8theninvalid_arg"triple_des#transform";des_transformckey1srcsrc_ofsdstdst_ofs;des_transformckey2dstdst_ofsdstdst_ofs;des_transformckey3dstdst_ofsdstdst_ofsmethodwipe=wipe_bytesckey1;wipe_bytesckey2;wipe_bytesckey3end(* Chaining modes *)letmake_initial_ivblocksize=function|None->Bytes.makeblocksize'\000'|Somes->ifString.lengths<>blocksizethenraise(ErrorWrong_IV_size);Bytes.of_stringsclasscbc_encrypt?iv:iv_init(cipher:block_cipher)=letblocksize=cipher#blocksizeinobject(self)valiv=make_initial_ivblocksizeiv_initmethodblocksize=blocksizemethodtransformsrcsrc_offdstdst_off=xor_bytessrcsrc_offiv0blocksize;cipher#transformiv0dstdst_off;Bytes.blitdstdst_offiv0blocksizemethodwipe=cipher#wipe;wipe_bytesivendclasscbc_decrypt?iv:iv_init(cipher:block_cipher)=letblocksize=cipher#blocksizeinobject(self)valiv=make_initial_ivblocksizeiv_initvalnext_iv=Bytes.createblocksizemethodblocksize=blocksizemethodtransformsrcsrc_offdstdst_off=Bytes.blitsrcsrc_offnext_iv0blocksize;cipher#transformsrcsrc_offdstdst_off;xor_bytesiv0dstdst_offblocksize;Bytes.blitnext_iv0iv0blocksizemethodwipe=cipher#wipe;wipe_bytesiv;wipe_bytesnext_ivendclasscfb_encrypt?iv:iv_initchunksize(cipher:block_cipher)=letblocksize=cipher#blocksizeinlet_=assert(chunksize>0&&chunksize<=blocksize)inobject(self)valiv=make_initial_ivblocksizeiv_initvalout=Bytes.createblocksizemethodblocksize=chunksizemethodtransformsrcsrc_offdstdst_off=cipher#transformiv0out0;Bytes.blitsrcsrc_offdstdst_offchunksize;xor_bytesout0dstdst_offchunksize;Bytes.blitivchunksizeiv0(blocksize-chunksize);Bytes.blitdstdst_offiv(blocksize-chunksize)chunksizemethodwipe=cipher#wipe;wipe_bytesiv;wipe_bytesoutendclasscfb_decrypt?iv:iv_initchunksize(cipher:block_cipher)=letblocksize=cipher#blocksizeinlet_=assert(chunksize>0&&chunksize<=blocksize)inobject(self)valiv=make_initial_ivblocksizeiv_initvalout=Bytes.createblocksizemethodblocksize=chunksizemethodtransformsrcsrc_offdstdst_off=cipher#transformiv0out0;Bytes.blitivchunksizeiv0(blocksize-chunksize);Bytes.blitsrcsrc_offiv(blocksize-chunksize)chunksize;Bytes.blitsrcsrc_offdstdst_offchunksize;xor_bytesout0dstdst_offchunksizemethodwipe=cipher#wipe;wipe_bytesiv;wipe_bytesoutendclassofb?iv:iv_initchunksize(cipher:block_cipher)=letblocksize=cipher#blocksizeinlet_=assert(chunksize>0&&chunksize<=blocksize)inobject(self)valiv=make_initial_ivblocksizeiv_initmethodblocksize=chunksizemethodtransformsrcsrc_offdstdst_off=cipher#transformiv0iv0;Bytes.blitsrcsrc_offdstdst_offchunksize;xor_bytesiv0dstdst_offchunksizemethodwipe=cipher#wipe;wipe_bytesivendletrecincrement_counterclimpos=ifpos>=limthenbeginleti=1+Char.code(Bytes.getcpos)inBytes.setcpos(Char.unsafe_chri);ifi=0x100thenincrement_counterclim(pos-1)endclassctr?iv:iv_init?inc(cipher:block_cipher)=letblocksize=cipher#blocksizeinletnincr=matchincwith|None->blocksize|Somen->assert(n>0&&n<=blocksize);ninobject(self)valiv=make_initial_ivblocksizeiv_initvalout=Bytes.createblocksizevalmutablemax_transf=ifnincr<8thenInt64.(shift_left1L(nincr*8))else0Lmethodblocksize=blocksizemethodtransformsrcsrc_offdstdst_off=cipher#transformiv0out0;Bytes.blitsrcsrc_offdstdst_offblocksize;xor_bytesout0dstdst_offblocksize;increment_counteriv(blocksize-nincr)(blocksize-1);letm=Int64.predmax_transfinifm=0Lthenraise(ErrorMessage_too_long);max_transf<-mmethodwipe=cipher#wipe;wipe_bytesiv;wipe_bytesoutend(* Wrapping of a block cipher as a transform *)classcipher(cipher:block_cipher)=letblocksize=cipher#blocksizeinobject(self)valibuf=Bytes.createblocksizevalmutableused=0inheritbuffered_output(max256(2*blocksize))asoutput_buffermethodinput_block_size=blocksizemethodoutput_block_size=blocksizemethodput_substringsrcofslen=iflen<=0then()elseifused+len<=blocksizethenbegin(* Just accumulate len characters in ibuf *)Bytes.blitsrcofsibufusedlen;used<-used+lenendelsebegin(* Fill buffer and run it through cipher *)letn=blocksize-usedinBytes.blitsrcofsibufusedn;self#ensure_capacityblocksize;cipher#transformibuf0obufoend;oend<-oend+blocksize;used<-0;(* Recurse on remainder of string *)self#put_substringsrc(ofs+n)(len-n)endmethodput_strings=self#put_substring(Bytes.unsafe_of_strings)0(String.lengths)methodput_charc=ifused<blocksizethenbeginBytes.setibufusedc;used<-used+1endelsebeginself#ensure_capacityblocksize;cipher#transformibuf0obufoend;oend<-oend+blocksize;Bytes.setibuf0c;used<-1endmethodput_byteb=self#put_char(Char.unsafe_chrb)methodwipe=cipher#wipe;output_buffer#wipe;wipe_bytesibufmethodflush=ifused=0then()elseifused=blocksizethenbeginself#ensure_capacityblocksize;cipher#transformibuf0obufoend;used<-0;oend<-oend+blocksizeendelseraise(ErrorWrong_data_length)methodfinish=self#flushend(* Block cipher with padding *)classcipher_padded_encrypt(padding:Padding.scheme)(cipher:block_cipher)=letblocksize=cipher#blocksizeinobject(self)inheritcipherciphermethodinput_block_size=1methodfinish=ifused>=blocksizethenbeginself#ensure_capacityblocksize;cipher#transformibuf0obufoend;oend<-oend+blocksize;used<-0end;padding#padibufused;self#ensure_capacityblocksize;cipher#transformibuf0obufoend;oend<-oend+blocksizeendclasscipher_padded_decrypt(padding:Padding.scheme)(cipher:block_cipher)=letblocksize=cipher#blocksizeinobject(self)inheritcipherciphermethodoutput_block_size=1methodfinish=ifused<>blocksizethenraise(ErrorWrong_data_length);cipher#transformibuf0ibuf0;letvalid=padding#stripibufinself#ensure_capacityvalid;Bytes.blitibuf0obufoendvalid;oend<-oend+validend(* Wrapping of a block cipher as a MAC, using CBC mode *)classmac?iv:iv_init?(pad:Padding.schemeoption)(cipher:block_cipher)=letblocksize=cipher#blocksizeinobject(self)valiv=make_initial_ivblocksizeiv_initvalbuffer=Bytes.createblocksizevalmutableused=0methodhash_size=blocksizemethodadd_substringsrcsrc_ofslen=letrecaddsrc_ofslen=iflen<=0then()elseifused+len<=blocksizethenbegin(* Just accumulate len characters in buffer *)Bytes.blitsrcsrc_ofsbufferusedlen;used<-used+lenendelsebegin(* Fill buffer and run it through cipher *)letn=blocksize-usedinBytes.blitsrcsrc_ofsbufferusedn;xor_bytesiv0buffer0blocksize;cipher#transformbuffer0iv0;used<-0;(* Recurse on remainder of string *)add(src_ofs+n)(len-n)endinaddsrc_ofslenmethodadd_strings=self#add_substring(Bytes.unsafe_of_strings)0(String.lengths)methodadd_charc=ifused<blocksizethenbeginBytes.setbufferusedc;used<-used+1endelsebeginxor_bytesiv0buffer0blocksize;cipher#transformbuffer0iv0;Bytes.setbuffer0c;used<-1endmethodadd_byteb=self#add_char(Char.unsafe_chrb)methodwipe=cipher#wipe;wipe_bytesbuffer;wipe_bytesivmethodresult=ifused=blocksizethenbeginxor_bytesiv0buffer0blocksize;cipher#transformbuffer0iv0;used<-0end;beginmatchpadwithNone->ifused<>0thenraise(ErrorWrong_data_length)|Somep->p#padbufferused;xor_bytesiv0buffer0blocksize;cipher#transformbuffer0iv0;used<-0end;Bytes.to_stringivendclassmac_final_triple?iv?pad(cipher1:block_cipher)(cipher2:block_cipher)(cipher3:block_cipher)=let_=ifcipher1#blocksize<>cipher2#blocksize||cipher2#blocksize<>cipher3#blocksizethenraise(ErrorIncompatible_block_size)inobjectinheritmac?iv?padcipher1assupermethodresult=letr=Bytes.of_stringsuper#resultincipher2#transformr0r0;cipher3#transformr0r0;Bytes.unsafe_to_stringrmethodwipe=super#wipe;cipher2#wipe;cipher3#wipeend(* Wrapping of a block ciper as a MAC, in CMAC mode (a.k.a. OMAC1) *)classcmac?iv:iv_init(cipher:block_cipher)k1k2=object(self)inheritmac?iv:iv_initcipherassupermethodresult=letblocksize=cipher#blocksizeinletk'=ifused=blocksizethenk1else(Padding._8000#padbufferused;k2)inxor_bytesiv0buffer0blocksize;xor_bytesk'0buffer0blocksize;cipher#transformbuffer0iv0;used<-0;(* really useful? *)Bytes.to_stringivmethodwipe=super#wipe;wipe_bytesk1;wipe_bytesk2endend(* Stream ciphers *)moduleStream=structclasstypestream_cipher=objectmethodtransform:bytes->int->bytes->int->int->unitmethodwipe:unitendclassarcfourkey=objectvalckey=ifString.lengthkey>0&&String.lengthkey<=256thenarcfour_cook_keykeyelseraise(ErrorWrong_key_size)methodtransformsrcsrc_ofsdstdst_ofslen=iflen<0||src_ofs<0||src_ofs>Bytes.lengthsrc-len||dst_ofs<0||dst_ofs>Bytes.lengthdst-lentheninvalid_arg"arcfour#transform";arcfour_transformckeysrcsrc_ofsdstdst_ofslenmethodwipe=wipe_bytesckeyendclasschacha20?iv?(ctr=0L)key=objectvalckey=ifnot(String.lengthkey=16||String.lengthkey=32)thenraise(ErrorWrong_key_size);letiv=matchivwith|None->Bytes.make8'\000'|Somes->ifString.lengths=8||String.lengths=12&&ctr<0x1_000_000LthenBytes.of_stringselseraise(ErrorWrong_IV_size)inchacha20_cook_keykeyivctrmethodtransformsrcsrc_ofsdstdst_ofslen=iflen<0||src_ofs<0||src_ofs>Bytes.lengthsrc-len||dst_ofs<0||dst_ofs>Bytes.lengthdst-lentheninvalid_arg"chacha20#transform";chacha20_transformckeysrcsrc_ofsdstdst_ofslenmethodwipe=wipe_bytesckeyend(* Wrapping of a stream cipher as a cipher *)classcipher(cipher:stream_cipher)=object(self)valcharbuf=Bytes.create1inheritbuffered_output256asoutput_buffermethodinput_block_size=1methodoutput_block_size=1methodput_substringsrcofslen=self#ensure_capacitylen;cipher#transformsrcofsobufoendlen;oend<-oend+lenmethodput_strings=self#put_substring(Bytes.unsafe_of_strings)0(String.lengths)methodput_charc=Bytes.setcharbuf0c;self#ensure_capacity1;cipher#transformcharbuf0obufoend1;oend<-oend+1methodput_byteb=self#put_char(Char.unsafe_chrb)methodflush=()methodfinish=()methodwipe=cipher#wipe;output_buffer#wipe;wipe_bytescharbufendend(* Hash functions *)moduleHash=structclasssha1=object(self)valcontext=sha1_init()methodhash_size=20methodadd_substringsrcofslen=ifofs<0||len<0||ofs>Bytes.lengthsrc-lentheninvalid_arg"sha1#add_substring";sha1_updatecontextsrcofslenmethodadd_stringsrc=sha1_updatecontext(Bytes.unsafe_of_stringsrc)0(String.lengthsrc)methodadd_charc=self#add_string(String.make1c)methodadd_byteb=self#add_char(Char.unsafe_chrb)methodresult=sha1_finalcontextmethodwipe=wipe_bytescontextendletsha1()=newsha1classsha224=object(self)valcontext=sha224_init()methodhash_size=24methodadd_substringsrcofslen=ifofs<0||len<0||ofs>Bytes.lengthsrc-lentheninvalid_arg"sha224#add_substring";sha256_updatecontextsrcofslenmethodadd_stringsrc=sha256_updatecontext(Bytes.unsafe_of_stringsrc)0(String.lengthsrc)methodadd_charc=self#add_string(String.make1c)methodadd_byteb=self#add_char(Char.unsafe_chrb)methodresult=sha224_finalcontextmethodwipe=wipe_bytescontextendletsha224()=newsha224classsha256=object(self)valcontext=sha256_init()methodhash_size=32methodadd_substringsrcofslen=ifofs<0||len<0||ofs>Bytes.lengthsrc-lentheninvalid_arg"sha256#add_substring";sha256_updatecontextsrcofslenmethodadd_stringsrc=sha256_updatecontext(Bytes.unsafe_of_stringsrc)0(String.lengthsrc)methodadd_charc=self#add_string(String.make1c)methodadd_byteb=self#add_char(Char.unsafe_chrb)methodresult=sha256_finalcontextmethodwipe=wipe_bytescontextendletsha256()=newsha256classsha384=object(self)valcontext=sha384_init()methodhash_size=48methodadd_substringsrcofslen=ifofs<0||len<0||ofs>Bytes.lengthsrc-lentheninvalid_arg"sha384#add_substring";sha512_updatecontextsrcofslenmethodadd_stringsrc=sha512_updatecontext(Bytes.unsafe_of_stringsrc)0(String.lengthsrc)methodadd_charc=self#add_string(String.make1c)methodadd_byteb=self#add_char(Char.unsafe_chrb)methodresult=sha384_finalcontextmethodwipe=wipe_bytescontextendletsha384()=newsha384classsha512=object(self)valcontext=sha512_init()methodhash_size=64methodadd_substringsrcofslen=ifofs<0||len<0||ofs>Bytes.lengthsrc-lentheninvalid_arg"sha512#add_substring";sha512_updatecontextsrcofslenmethodadd_stringsrc=sha512_updatecontext(Bytes.unsafe_of_stringsrc)0(String.lengthsrc)methodadd_charc=self#add_string(String.make1c)methodadd_byteb=self#add_char(Char.unsafe_chrb)methodresult=sha512_finalcontextmethodwipe=wipe_bytescontextendletsha512()=newsha512letsha2sz=matchszwith|224->newsha224|256->newsha256|384->newsha384|512->newsha512|_->raise(ErrorWrong_key_size)classsha3szofficial=object(self)valcontext=ifsz=224||sz=256||sz=384||sz=512thensha3_initszelseraise(ErrorWrong_key_size)methodhash_size=sz/8methodadd_substringsrcofslen=ifofs<0||len<0||ofs>Bytes.lengthsrc-lentheninvalid_arg((ifofficialthen"sha3"else"keccak")^"#add_substring");sha3_absorbcontextsrcofslenmethodadd_stringsrc=sha3_absorbcontext(Bytes.unsafe_of_stringsrc)0(String.lengthsrc)methodadd_charc=self#add_string(String.make1c)methodadd_byteb=self#add_char(Char.unsafe_chrb)methodresult=sha3_extractofficialcontextmethodwipe=sha3_wipecontextendletsha3sz=newsha3sztrueletkeccaksz=newsha3szfalseclassripemd160=object(self)valcontext=ripemd160_init()methodhash_size=32methodadd_substringsrcofslen=ifofs<0||len<0||ofs>Bytes.lengthsrc-lentheninvalid_arg"ripemd160#add_substring";ripemd160_updatecontextsrcofslenmethodadd_stringsrc=ripemd160_updatecontext(Bytes.unsafe_of_stringsrc)0(String.lengthsrc)methodadd_charc=self#add_string(String.make1c)methodadd_byteb=self#add_char(Char.unsafe_chrb)methodresult=ripemd160_finalcontextmethodwipe=wipe_bytescontextendletripemd160()=newripemd160classmd5=object(self)valcontext=md5_init()methodhash_size=16methodadd_substringsrcofslen=ifofs<0||len<0||ofs>Bytes.lengthsrc-lentheninvalid_arg"md5#add_substring";md5_updatecontextsrcofslenmethodadd_stringsrc=md5_updatecontext(Bytes.unsafe_of_stringsrc)0(String.lengthsrc)methodadd_charc=self#add_string(String.make1c)methodadd_byteb=self#add_char(Char.unsafe_chrb)methodresult=md5_finalcontextmethodwipe=wipe_bytescontextendletmd5()=newmd5classblake2bszkey=object(self)valcontext=ifsz>=8&&sz<=512&&szmod8=0&&String.lengthkey<=64thenblake2b_init(sz/8)keyelseraise(ErrorWrong_key_size)methodhash_size=sz/8methodadd_substringsrcofslen=ifofs<0||len<0||ofs>Bytes.lengthsrc-lentheninvalid_arg"blake2b#add_substring";blake2b_updatecontextsrcofslenmethodadd_stringsrc=blake2b_updatecontext(Bytes.unsafe_of_stringsrc)0(String.lengthsrc)methodadd_charc=self#add_string(String.make1c)methodadd_byteb=self#add_char(Char.unsafe_chrb)methodresult=blake2b_finalcontext(sz/8)methodwipe=wipe_bytescontextendletblake2bsz=newblake2bsz""letblake2b512()=newblake2b512""classblake2sszkey=object(self)valcontext=ifsz>=8&&sz<=256&&szmod8=0&&String.lengthkey<=32thenblake2s_init(sz/8)keyelseraise(ErrorWrong_key_size)methodhash_size=sz/8methodadd_substringsrcofslen=ifofs<0||len<0||ofs>Bytes.lengthsrc-lentheninvalid_arg"blake2s#add_substring";blake2s_updatecontextsrcofslenmethodadd_stringsrc=blake2s_updatecontext(Bytes.unsafe_of_stringsrc)0(String.lengthsrc)methodadd_charc=self#add_string(String.make1c)methodadd_byteb=self#add_char(Char.unsafe_chrb)methodresult=blake2s_finalcontext(sz/8)methodwipe=wipe_bytescontextendletblake2ssz=newblake2ssz""letblake2s256()=newblake2s256""classblake3keysz=object(self)valcontext=ifsz>0&&szmod8=0&&(String.lengthkey=0||String.lengthkey=32)thenblake3_initkeyelseraise(ErrorWrong_key_size)methodhash_size=sz/8methodadd_substringsrcofslen=ifofs<0||len<0||ofs>Bytes.lengthsrc-lentheninvalid_arg"blake3#add_substring";blake3_updatecontextsrcofslenmethodadd_stringsrc=blake3_updatecontext(Bytes.unsafe_of_stringsrc)0(String.lengthsrc)methodadd_charc=self#add_string(String.make1c)methodadd_byteb=self#add_char(Char.unsafe_chrb)methodresult=blake3_finalcontext(sz/8)methodwipe=blake3_wipecontextendletblake3sz=newblake3""szletblake3_256()=newblake3""256end(* High-level entry points for ciphers *)moduleCipher=structtypedirection=dir=Encrypt|Decrypttypechaining_mode=ECB|CBC|CFBofint|OFBofint|CTR|CTR_Nofintletmake_block_cipher?(mode=CBC)?pad?ivdirblock_cipher=letchained_cipher=match(mode,dir)with(ECB,_)->block_cipher|(CBC,Encrypt)->newBlock.cbc_encrypt?ivblock_cipher|(CBC,Decrypt)->newBlock.cbc_decrypt?ivblock_cipher|(CFBn,Encrypt)->newBlock.cfb_encrypt?ivnblock_cipher|(CFBn,Decrypt)->newBlock.cfb_decrypt?ivnblock_cipher|(OFBn,_)->newBlock.ofb?ivnblock_cipher|(CTR,_)->newBlock.ctr?ivblock_cipher|(CTR_Nn,_)->newBlock.ctr?iv~inc:nblock_cipherinmatchpadwithNone->newBlock.cipherchained_cipher|Somep->matchdirwithEncrypt->newBlock.cipher_padded_encryptpchained_cipher|Decrypt->newBlock.cipher_padded_decryptpchained_cipherletnormalize_dirmodedir=matchmodewith|Some(CFB_)|Some(OFB_)|Some(CTR)|Some(CTR_N_)->Encrypt|_->dirletaes?mode?pad?ivkeydir=make_block_cipher?mode?pad?ivdir(matchnormalize_dirmodedirwithEncrypt->newBlock.aes_encryptkey|Decrypt->newBlock.aes_decryptkey)letblowfish?mode?pad?ivkeydir=make_block_cipher?mode?pad?ivdir(matchnormalize_dirmodedirwithEncrypt->newBlock.blowfish_encryptkey|Decrypt->newBlock.blowfish_decryptkey)letdes?mode?pad?ivkeydir=make_block_cipher?mode?pad?ivdir(newBlock.des(normalize_dirmodedir)key)lettriple_des?mode?pad?ivkeydir=make_block_cipher?mode?pad?ivdir(matchnormalize_dirmodedirwithEncrypt->newBlock.triple_des_encryptkey|Decrypt->newBlock.triple_des_decryptkey)letarcfourkeydir=newStream.cipher(newStream.arcfourkey)letchacha20?iv?ctrkeydir=newStream.cipher(newStream.chacha20key?iv?ctr)end(* The hmac construction *)moduleHMAC(H:sigclassh:hashvalblocksize:intend)=structlethmac_padkeybyte=letkey=ifString.lengthkey>H.blocksizethenhash_string(newH.h)keyelsekeyinletr=Bytes.makeH.blocksize(Char.chrbyte)inxor_stringkey0r0(String.lengthkey);rclasshmackey=object(self)inheritH.hassuperinitializer(letb=hmac_padkey0x36inself#add_substringb0(Bytes.lengthb);wipe_bytesb)methodresult=leth'=newH.hinletb=hmac_padkey0x5Cinh'#add_substringb0(Bytes.lengthb);wipe_bytesb;h'#add_string(super#result);letr=h'#resultinh'#wipe;rendend(* High-level entry points for MACs *)moduleMAC=structmoduleHMAC_SHA1=HMAC(structclassh=Hash.sha1letblocksize=64end)moduleHMAC_SHA256=HMAC(structclassh=Hash.sha256letblocksize=64end)moduleHMAC_SHA384=HMAC(structclassh=Hash.sha384letblocksize=128end)moduleHMAC_SHA512=HMAC(structclassh=Hash.sha512letblocksize=128end)moduleHMAC_RIPEMD160=HMAC(structclassh=Hash.ripemd160letblocksize=64end)moduleHMAC_MD5=HMAC(structclassh=Hash.md5letblocksize=64end)lethmac_sha1key=newHMAC_SHA1.hmackeylethmac_sha256key=newHMAC_SHA256.hmackeylethmac_sha384key=newHMAC_SHA384.hmackeylethmac_sha512key=newHMAC_SHA512.hmackeylethmac_ripemd160key=newHMAC_RIPEMD160.hmackeylethmac_md5key=newHMAC_MD5.hmackeyletblake2bszkey=newHash.blake2bszkeyletblake2b512key=newHash.blake2b512keyletblake2sszkey=newHash.blake2sszkeyletblake2s256key=newHash.blake2s256keyletblake3szkey=newHash.blake3keyszletblake3_256key=newHash.blake3key256letaes?iv?padkey=newBlock.mac?iv?pad(newBlock.aes_encryptkey)letdes?iv?padkey=newBlock.mac?iv?pad(newBlock.des_encryptkey)lettriple_des?iv?padkey=newBlock.mac?iv?pad(newBlock.triple_des_encryptkey)letdes_final_triple_des?iv?padkey=letkl=String.lengthkeyinifkl<>16&&kl<>24thenraise(ErrorWrong_key_size);letk1=String.subkey08inletk2=String.subkey88inletk3=ifkl=24thenString.subkey168elsek1inletc1=newBlock.des_encryptk1andc2=newBlock.des_decryptk2andc3=newBlock.des_encryptk3inwipe_stringk1;wipe_stringk2;wipe_stringk3;newBlock.mac_final_triple?iv?padc1c2c3letaes_cmac?ivkey=letcipher=newBlock.aes_encryptkeyinletb=Bytes.make16'\000'inletl=Bytes.create16incipher#transformb0l0;(* l = AES-128(K, 000...000 *)Bytes.setb15'\x87';(* b = the Rb constant *)letk1=Bytes.create16inshl1_bytesl0k1016;ifChar.code(Bytes.getl0)land0x80>0thenxor_bytesb0k1016;letk2=Bytes.create16inshl1_bytesk10k2016;ifChar.code(Bytes.getk10)land0x80>0thenxor_bytesb0k2016;wipe_bytesl;newBlock.cmac?ivcipherk1k2classsiphashszkey=object(self)valcontext=ifString.lengthkey=16&&(sz=64||sz=128)thensiphash_initkey(sz/8)elseraise(ErrorWrong_key_size)methodhash_size=sz/8methodadd_substringsrcofslen=ifofs<0||len<0||ofs>Bytes.lengthsrc-lentheninvalid_arg"siphash#add_substring";siphash_updatecontextsrcofslenmethodadd_stringsrc=siphash_updatecontext(Bytes.unsafe_of_stringsrc)0(String.lengthsrc)methodadd_charc=self#add_string(String.make1c)methodadd_byteb=self#add_char(Char.unsafe_chrb)methodresult=siphash_finalcontext(sz/8)methodwipe=wipe_bytescontextendletsiphashkey=newsiphash64keyletsiphash128key=newsiphash128keyend(* Authenticated encryption with associated data *)moduleAEAD=structtypedirection=dir=Encrypt|Decrypt(* AES-GCM *)(* The H multiplier for GHASH is derived from the AES key by
encrypting the all-zero block. *)letghash_multiplier(aes:Block.block_cipher)=letb=Bytes.make16'\000'inaes#transformb0b0;ghash_initb(* Add a block to the rolling MAC. len must be between 0 and 16.
If less than 16, we logically pad with zeros at the end,
i.e. we "xor with zero" (= keep unchanged) the MAC bytes
between len and 16. *)letghash_blockhmacbufofslen=xor_bytesbufofsmac0len;ghash_multhmacletghash_block_shmacbufofslen=xor_stringbufofsmac0len;ghash_multhmac(* Hash the given string, with zero padding. Used for the non-encrypted
authenticated data and for counter generation. *)letghash_stringhmsg=letmac=Bytes.make16'\000'inletl=String.lengthmsginleti=ref0inwhile!i+16<=ldoghash_block_shmacmsg!i16;i:=!i+16done;if!i<lthenghash_block_shmacmsg!i(l-!i);mac(* Produce the final authentication tag *)letghash_finalhmacheaderlencipherlene0=letbuf=Bytes.create16in(* Hash the extra block containing the lengths *)Bytes.set_int64_bebuf0(Int64.mulheaderlen8L);(* in bits *)Bytes.set_int64_bebuf8(Int64.mulcipherlen8L);(* in bits *)ghash_blockhmacbuf016;(* Authentication tag = final MAC xor encryption of the IV *)Bytes.blitmac0buf016;xor_bytese00buf016;Bytes.to_stringbuf(* Initial value of the counter *)letcounter0hiv=ifString.lengthiv=12thenBytes.of_string(iv^"\000\000\000\001")elsebeginletmac=ghash_stringhivinletbuf=Bytes.make16'\000'inBytes.set_int64_bebuf8(Int64.mul(Int64.of_int(String.lengthiv))8L);ghash_blockhmacbuf016;macend(* Encryption of the initial counter *)letenc_initial_counter(aes:Block.block_cipher)counter0=letb=Bytes.create16inaes#transformcounter00b0;b(* CTR encryption / decryption *)letctr_enc_dec(aes:Block.block_cipher)ctrbufsrcsoffdstdofflen=Block.increment_counterctr1215;aes#transformctr0buf0;xor_bytessrcsoffbuf0len;Bytes.blitbuf0dstdofflenclassaes_gcm_encrypt?(header="")~ivkey=(* The AES block cipher *)letaes=newBlock.aes_encryptkeyin(* The multiplier for the GHASH MAC *)leth=ghash_multiplieraesin(* The counter for use in CTR mode. *)letctr=counter0hivin(* The encryption of the initial counter, to be used for the final MAC *)lete0=enc_initial_counteraesctrin(* The current MAC, initialized with the header
(the non-encrypted authenticated data) *)letmac=ghash_stringhheaderin(* Lengths of the authenticated data and the encrypted data *)letheaderlen=Int64.of_int(String.lengthheader)andcipherlen=ref0Lin(* A wrapper around the block cipher that
- performs encryption in CTR mode
- updates the MAC
- updates the length of encrypted data *)letenc_wrapped:Block.block_cipher=letbuf=Bytes.create16inobjectmethodblocksize=16methodwipe=aes#wipemethodtransformsrcsoffdstdoff=ctr_enc_decaesctrbufsrcsoffdstdoff16;ghash_blockhmacdstdoff16;cipherlen:=Int64.(add!cipherlen16L);if!cipherlen>0xfffffffe0Lthenraise(ErrorMessage_too_long)endinobject(self)inherit(Block.cipherenc_wrapped)methodinput_block_size=1methodoutput_block_size=1methodtag_size=16methodfinish_and_get_tag=ifused>0thenbeginletbuf=Bytes.create16in(* Encrypt final block *)self#ensure_capacityused;ctr_enc_decaesctrbufibuf0obufoendused;(* Hash final block padded with zeros *)ghash_blockhmacobufoendused;oend<-oend+used;cipherlen:=Int64.(add!cipherlen(of_intused));if!cipherlen>0xfffffffe0Lthenraise(ErrorMessage_too_long)end;(* Produce authentication tag *)ghash_finalhmacheaderlen!cipherlene0endclassaes_gcm_decrypt?(header="")~ivkey=(* The AES block cipher *)letaes=newBlock.aes_encryptkeyin(* The multiplier for the GHASH MAC *)leth=ghash_multiplieraesin(* The counter for use in CTR mode. *)letctr=counter0hivin(* The encryption of the initial counter, to be used for the final MAC *)lete0=enc_initial_counteraesctrin(* The current MAC, initialized with the header
(the non-encrypted authenticated data) *)letmac=ghash_stringhheaderin(* Lengths of the authenticated data and the encrypted data *)letheaderlen=Int64.of_int(String.lengthheader)andcipherlen=ref0Lin(* A wrapper around the block cipher that
- updates the MAC
- performs decryption in CTR mode
- updates the length of encrypted data *)letdec_wrapped:Block.block_cipher=letbuf=Bytes.create16inobjectmethodblocksize=16methodwipe=aes#wipemethodtransformsrcsoffdstdoff=ghash_blockhmacsrcsoff16;ctr_enc_decaesctrbufsrcsoffdstdoff16;cipherlen:=Int64.(add!cipherlen16L)endinobject(self)inherit(Block.cipherdec_wrapped)methodinput_block_size=1methodoutput_block_size=1methodtag_size=16methodfinish_and_get_tag=ifused>0thenbeginletbuf=Bytes.create16in(* Hash final block padded with zeros *)ghash_blockhmacibuf0used;(* Decrypt final block *)self#ensure_capacityused;ctr_enc_decaesctrbufibuf0obufoendused;oend<-oend+used;cipherlen:=Int64.(add!cipherlen(of_intused))end;(* Produce authentication tag *)ghash_finalhmacheaderlen!cipherlene0endletaes_gcm?header~ivkeydir=matchdirwith|Encrypt->(newaes_gcm_encrypt?header~ivkey:>authenticated_transform)|Decrypt->(newaes_gcm_decrypt?header~ivkey:>authenticated_transform)(* Chacha20-Poly1305 *)letpoly1305_update_padhn=letn=(0x10-n)land0xFinifn>0thenpoly1305_updateh(Bytes.maken'\000')0nletpoly1305_init_hashchaheader=letbuf=Bytes.make64'\000'incha#transformbuf0buf064;leth=poly1305_initbufin(* only the first 32 bytes are used *)wipe_bytesbuf;poly1305_updateh(Bytes.unsafe_of_stringheader)0(String.lengthheader);(* Pad header to a multiple of 16 bytes *)poly1305_update_padh(String.lengthheaderland0xF);hletpoly1305_finish_and_get_taghheaderlencipherlen=(* Pad ciphertext to a multiple of 16 bytes *)poly1305_update_padhInt64.(to_int(logandcipherlen0xFL));(* Add lengths as 64-bit little-endian numbers *)letbuf=Bytes.create16inBytes.set_int64_lebuf0headerlen;Bytes.set_int64_lebuf8cipherlen;poly1305_updatehbuf016;(* The final hash is the authentication tag *)poly1305_finalhclasschapoly_encrypt?(header="")~ivkey=(* The Chacha20 stream cipher *)letcha=newStream.chacha20~ivkeyin(* The Poly1305 hash *)leth=poly1305_init_hashchaheaderin(* Lengths of the authenticated data and the encrypted data *)letheaderlen=Int64.of_int(String.lengthheader)andcipherlen=ref0Lin(* Maximum length for encrypted data *)letmaxlen=ifString.lengthiv=12then0x4000000000LelseInt64.max_intin(* The stream cipher that wraps Chacha20 with hash updates *)letenc=objectmethodtransformsrcsoffdstdofflen=cha#transformsrcsoffdstdofflen;poly1305_updatehdstdofflen;cipherlen:=Int64.(add!cipherlen(of_intlen));if!cipherlen>maxlenthenraise(ErrorMessage_too_long)methodwipe=cha#wipe;wipe_byteshendinobject(self)inherit(Stream.cipherenc)methodinput_block_size=1methodoutput_block_size=1methodtag_size=16methodfinish_and_get_tag=poly1305_finish_and_get_taghheaderlen!cipherlenendclasschapoly_decrypt?(header="")~ivkey=(* The Chacha20 stream cipher *)letcha=newStream.chacha20~ivkeyin(* The Poly1305 hash *)leth=poly1305_init_hashchaheaderin(* Lengths of the authenticated data and the encrypted data *)letheaderlen=Int64.of_int(String.lengthheader)andcipherlen=ref0Lin(* The stream cipher that wraps Chacha20 with hash updates *)letenc=objectmethodtransformsrcsoffdstdofflen=poly1305_updatehsrcsofflen;cha#transformsrcsoffdstdofflen;cipherlen:=Int64.(add!cipherlen(of_intlen))methodwipe=cha#wipe;wipe_byteshendinobject(self)inherit(Stream.cipherenc)methodinput_block_size=1methodoutput_block_size=1methodtag_size=16methodfinish_and_get_tag=poly1305_finish_and_get_taghheaderlen!cipherlenendletchacha20_poly1305?header~ivkeydir=matchdirwith|Encrypt->(newchapoly_encrypt?header~ivkey:>authenticated_transform)|Decrypt->(newchapoly_decrypt?header~ivkey:>authenticated_transform)end(* Random number generation *)moduleRandom=structclasstyperng=objectmethodrandom_bytes:bytes->int->int->unitmethodwipe:unitendletstringrnglen=letres=Bytes.createleninrng#random_bytesres0len;Bytes.unsafe_to_stringrestypesystem_rng_handleexternalget_system_rng:unit->system_rng_handle="caml_get_system_rng"externalclose_system_rng:system_rng_handle->unit="caml_close_system_rng"externalsystem_rng_random_bytes:system_rng_handle->bytes->int->int->bool="caml_system_rng_random_bytes"classsystem_rng=object(self)valh=get_system_rng()methodrandom_bytesbufofslen=ifofs<0||len<0||ofs>Bytes.lengthbuf-lentheninvalid_arg"random_bytes";ifsystem_rng_random_byteshbufofslenthen()elseraise(ErrorEntropy_source_closed)methodwipe=close_system_rnghendletsystem_rng()=trynewsystem_rngwithNot_found->raise(ErrorNo_entropy_source)classdevice_rngfilename=object(self)valfd=Unix.openfilefilename[Unix.O_RDONLY;Unix.O_CLOEXEC]0methodrandom_bytesbufofslen=iflen>0thenbeginletn=Unix.readfdbufofsleninifn=0thenraise(ErrorEntropy_source_closed);ifn<lenthenself#random_bytesbuf(ofs+n)(len-n)endmethodwipe=Unix.closefdendletdevice_rngfilename=newdevice_rngfilenameexternalhardware_rng_available:unit->bool="caml_hardware_rng_available"externalhardware_rng_random_bytes:bytes->int->int->bool="caml_hardware_rng_random_bytes"classhardware_rng=objectmethodrandom_bytesbufofslen=ifofs<0||len<0||ofs>Bytes.lengthbuf-lentheninvalid_arg"hardware_rng#random_bytes";ifnot(hardware_rng_random_bytesbufofslen)thenraise(ErrorEntropy_source_closed)methodwipe=()endlethardware_rng()=ifhardware_rng_available()thennewhardware_rngelseraise(ErrorNo_entropy_source)classno_rng=objectmethodrandom_bytes(buf:bytes)(ofs:int)(len:int):unit=raise(ErrorNo_entropy_source)methodwipe=()endletsecure_rng=trynewsystem_rngwithNot_found->trynewdevice_rng"/dev/random"withUnix.Unix_error(_,_,_)->ifhardware_rng_available()thennewhardware_rngelsenewno_rngclasspseudo_rngseed=let_=ifString.lengthseed<16thenraise(ErrorSeed_too_short)inobject(self)valckey=letl=String.lengthseedinchacha20_cook_key(ifl>=32thenString.subseed032elseifl>16thenseed^String.make(32-l)'\000'elseseed)(Bytes.make8'\000')0Lmethodrandom_bytesbufofslen=iflen<0||ofs<0||ofs>Bytes.lengthbuf-lentheninvalid_arg"pseudo_rng#random_bytes"elsechacha20_extractckeybufofslenmethodwipe=wipe_bytesckey;wipe_stringseedendletpseudo_rngseed=newpseudo_rngseedclasspseudo_rng_aes_ctrseed=let_=ifString.lengthseed<16thenraise(ErrorSeed_too_short)inobject(self)valcipher=newBlock.aes_encrypt(String.subseed016)valctr=Bytes.make16'\000'valobuf=Bytes.create16valmutableopos=16methodrandom_bytesbufofslen=iflen>0thenbeginifopos>=16thenbegin(* Encrypt the counter *)cipher#transformctr0obuf0;(* Increment the counter *)Block.increment_counterctr015;(* We have 16 fresh bytes of pseudo-random data *)opos<-0end;letr=min(16-opos)leninBytes.blitobufoposbufofsr;opos<-opos+r;ifr<lenthenself#random_bytesbuf(ofs+r)(len-r)endmethodwipe=wipe_bytesobuf;wipe_stringseedendletpseudo_rng_aes_ctrseed=newpseudo_rng_aes_ctrseedend(* RSA operations *)moduleBn=CryptokitBignummoduleRSA=structtypekey={size:int;n:string;e:string;d:string;p:string;q:string;dp:string;dq:string;qinv:string}letwipe_keyk=wipe_stringk.n;wipe_stringk.e;wipe_stringk.d;wipe_stringk.p;wipe_stringk.q;wipe_stringk.dp;wipe_stringk.dq;wipe_stringk.qinvletencryptkeymsg=letmsg=Bn.of_bytesmsginletn=Bn.of_byteskey.ninlete=Bn.of_byteskey.einifBn.comparemsgn>=0thenraise(ErrorMessage_too_long);letr=Bn.mod_powermsgeninlets=Bn.to_bytes~numbits:key.sizerinBn.wipemsg;Bn.wipen;Bn.wipee;Bn.wiper;sletunwrap_signature=encryptletdecryptkeymsg=letmsg=Bn.of_bytesmsginletn=Bn.of_byteskey.ninletd=Bn.of_byteskey.dinifBn.comparemsgn>=0thenraise(ErrorMessage_too_long);letr=Bn.mod_powermsgdninlets=Bn.to_bytes~numbits:key.sizerinBn.wipemsg;Bn.wipen;Bn.wiped;Bn.wiper;sletsign=decryptletdecrypt_CRTkeymsg=letmsg=Bn.of_bytesmsginletn=Bn.of_byteskey.ninletp=Bn.of_byteskey.pinletq=Bn.of_byteskey.qinletdp=Bn.of_byteskey.dpinletdq=Bn.of_byteskey.dqinletqinv=Bn.of_byteskey.qinvinifBn.comparemsgn>=0thenraise(ErrorMessage_too_long);letr=Bn.mod_power_CRTmsgpqdpdqqinvinlets=Bn.to_bytes~numbits:key.sizerinBn.wipemsg;Bn.wipen;Bn.wipep;Bn.wipeq;Bn.wipedp;Bn.wipedq;Bn.wipeqinv;Bn.wiper;sletsign_CRT=decrypt_CRTletnew_key?(rng=Random.secure_rng)?enumbits=ifnumbits<32||numbitsland1>0thenraise(ErrorWrong_key_size);letnumbits2=numbits/2in(* Generate primes p, q with numbits / 2 digits.
If fixed exponent e, make sure gcd(p-1,e) = 1 and
gcd(q-1,e) = 1. *)letrecgen_factornbits=letn=Bn.random_prime~rng:(rng#random_bytes)nbitsinmatchewithNone->n|Somee->ifBn.relative_prime(Bn.subnBn.one)(Bn.of_inte)thennelsegen_factornbitsin(* Make sure p > q *)letrecgen_factorsnbits=letp=gen_factornbitsandq=gen_factornbitsinletcmp=Bn.comparepqinifcmp=0thengen_factorsnbitselseifcmp<0then(q,p)else(p,q)inlet(p,q)=gen_factorsnumbits2in(* p1 = p - 1 and q1 = q - 1 *)letp1=Bn.subpBn.oneandq1=Bn.subqBn.onein(* If no fixed exponent specified, generate random exponent e such that
gcd(p-1,e) = 1 and gcd(q-1,e) = 1 *)lete=matchewithSomee->Bn.of_inte|None->letrecgen_exponent()=letn=Bn.random~rng:(rng#random_bytes)numbitsinifBn.relative_primenp1&&Bn.relative_primenq1thennelsegen_exponent()ingen_exponent()in(* n = pq *)letn=Bn.multpqin(* d = e^-1 mod (p-1)(q-1) *)letd=Bn.mod_inve(Bn.multp1q1)in(* dp = d mod p-1 and dq = d mod q-1 *)letdp=Bn.mod_dp1anddq=Bn.mod_dq1in(* qinv = q^-1 mod p *)letqinv=Bn.mod_invqpin(* Build key *)letres={size=numbits;n=Bn.to_bytes~numbits:numbitsn;e=Bn.to_bytes~numbits:numbitse;d=Bn.to_bytes~numbits:numbitsd;p=Bn.to_bytes~numbits:numbits2p;q=Bn.to_bytes~numbits:numbits2q;dp=Bn.to_bytes~numbits:numbits2dp;dq=Bn.to_bytes~numbits:numbits2dq;qinv=Bn.to_bytes~numbits:numbits2qinv}inBn.wipen;Bn.wipee;Bn.wiped;Bn.wipep;Bn.wipeq;Bn.wipep1;Bn.wipeq1;Bn.wipedp;Bn.wipedq;Bn.wipeqinv;resend(* Diffie-Hellman key agreement *)moduleDH=structtypeparameters={p:string;g:string;privlen:int}letnew_parameters?(rng=Random.secure_rng)?(privlen=160)numbits=ifnumbits<32||numbits<=privlenthenraise(ErrorWrong_key_size);letnp=Bn.random_prime~rng:(rng#random_bytes)numbitsinletrecfind_generator()=letg=Bn.random~rng:(rng#random_bytes)(numbits-1)inifBn.comparegBn.one<=0thenfind_generator()elseginletng=find_generator()in{p=Bn.to_bytes~numbitsnp;g=Bn.to_bytes~numbitsng;privlen=privlen}typeprivate_secret=Bn.tletprivate_secret?(rng=Random.secure_rng)params=Bn.random~rng:(rng#random_bytes)params.privlenletmessageparamsprivsec=Bn.to_bytes~numbits:(String.lengthparams.p*8)(Bn.mod_power(Bn.of_bytesparams.g)privsec(Bn.of_bytesparams.p))letshared_secretparamsprivsecothermsg=letres=Bn.to_bytes~numbits:(String.lengthparams.p*8)(Bn.mod_power(Bn.of_bytesothermsg)privsec(Bn.of_bytesparams.p))inBn.wipeprivsec;resletderive_key?(diversification="")sharedsecnumbytes=letresult=Bytes.createnumbytesinletrecderiveposcounter=ifpos<numbytesthenbeginleth=hash_string(Hash.sha256())(diversification^sharedsec^string_of_intcounter)inString.blith0resultpos(min(String.lengthh)(numbytes-pos));wipe_stringh;derive(pos+String.lengthh)(counter+1)endinderive01;Bytes.unsafe_to_stringresultend(* Base64 encoding *)moduleBase64=structletbase64_conv_table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"classencodemultilinepadding=object(self)methodinput_block_size=1methodoutput_block_size=1inheritbuffered_output256asoutput_buffervalibuf=Bytes.create3valmutableipos=0valmutableocolumn=0methodput_charc=Bytes.setibufiposc;ipos<-ipos+1;ifipos=3thenbeginletb0=Char.code(Bytes.getibuf0)andb1=Char.code(Bytes.getibuf1)andb2=Char.code(Bytes.getibuf2)inself#ensure_capacity4;Bytes.setobufoendbase64_conv_table.[b0lsr2];Bytes.setobuf(oend+1)base64_conv_table.[(b0land3)lsl4+(b1lsr4)];Bytes.setobuf(oend+2)base64_conv_table.[(b1land15)lsl2+(b2lsr6)];Bytes.setobuf(oend+3)base64_conv_table.[b2land63];oend<-oend+4;ipos<-0;ocolumn<-ocolumn+4;ifmultiline&&ocolumn>=72thenbeginself#ensure_capacity1;Bytes.setobufoend'\n';oend<-oend+1;ocolumn<-0endendmethodput_substringsofslen=fori=ofstoofs+len-1doself#put_char(Bytes.getsi)donemethodput_strings=String.iterself#put_charsmethodput_byteb=self#put_char(Char.chrb)methodflush:unit=raise(ErrorWrong_data_length)methodfinish=beginmatchiposwith1->self#ensure_capacity2;letb0=Char.code(Bytes.getibuf0)inBytes.setobufoendbase64_conv_table.[b0lsr2];Bytes.setobuf(oend+1)base64_conv_table.[(b0land3)lsl4];oend<-oend+2|2->self#ensure_capacity3;letb0=Char.code(Bytes.getibuf0)andb1=Char.code(Bytes.getibuf1)inBytes.setobufoendbase64_conv_table.[b0lsr2];Bytes.setobuf(oend+1)base64_conv_table.[(b0land3)lsl4+(b1lsr4)];Bytes.setobuf(oend+2)(base64_conv_table.[(b1land15)lsl2]);oend<-oend+3|_->()end;ifmultiline||paddingthenbeginletnum_equals=matchiposwith1->2|2->1|_->0inself#ensure_capacitynum_equals;Bytes.fillobufoendnum_equals'=';oend<-oend+num_equalsend;ifmultiline&&ocolumn>0thenbeginself#ensure_capacity1;Bytes.setobufoend'\n';oend<-oend+1end;ocolumn<-0methodwipe=wipe_bytesibuf;output_buffer#wipeendletencode_multiline()=newencodetruetrueletencode_compact()=newencodefalsefalseletencode_compact_pad()=newencodefalsetrueletbase64_decode_charc=matchcwith'A'..'Z'->Char.codec-65|'a'..'z'->Char.codec-97+26|'0'..'9'->Char.codec-48+52|'+'->62|'/'->63|' '|'\t'|'\n'|'\r'->-1|_->raise(ErrorBad_encoding)classdecode=object(self)inheritbuffered_output256asoutput_buffermethodinput_block_size=1methodoutput_block_size=1valibuf=Array.make40valmutableipos=0valmutablefinished=falsemethodput_charc=ifc='='thenfinished<-trueelsebeginletn=base64_decode_charcinifn>=0thenbeginiffinishedthenraise(ErrorBad_encoding);ibuf.(ipos)<-n;ipos<-ipos+1;ifipos=4thenbeginself#ensure_capacity3;Bytes.setobufoend(Char.chr(ibuf.(0)lsl2+ibuf.(1)lsr4));Bytes.setobuf(oend+1)(Char.chr((ibuf.(1)land15)lsl4+ibuf.(2)lsr2));Bytes.setobuf(oend+2)(Char.chr((ibuf.(2)land3)lsl6+ibuf.(3)));oend<-oend+3;ipos<-0endendendmethodput_substringsofslen=fori=ofstoofs+len-1doself#put_char(Bytes.getsi)donemethodput_strings=String.iterself#put_charsmethodput_byteb=self#put_char(Char.chrb)methodflush:unit=raise(ErrorWrong_data_length)methodfinish=finished<-true;matchiposwith|1->raise(ErrorBad_encoding)|2->self#ensure_capacity1;Bytes.setobufoend(Char.chr(ibuf.(0)lsl2+ibuf.(1)lsr4));oend<-oend+1|3->self#ensure_capacity2;Bytes.setobufoend(Char.chr(ibuf.(0)lsl2+ibuf.(1)lsr4));Bytes.setobuf(oend+1)(Char.chr((ibuf.(1)land15)lsl4+ibuf.(2)lsr2));oend<-oend+2|_->()methodwipe=Array.fillibuf040;output_buffer#wipeendletdecode()=newdecodeend(* Hexadecimal encoding *)moduleHexa=structlethex_conv_table="0123456789abcdef"classencode=object(self)methodinput_block_size=1methodoutput_block_size=1inheritbuffered_output256asoutput_buffermethodput_byteb=self#ensure_capacity2;Bytes.setobufoend(hex_conv_table.[blsr4]);Bytes.setobuf(oend+1)(hex_conv_table.[bland0xF]);oend<-oend+2methodput_charc=self#put_byte(Char.codec)methodput_substringsofslen=fori=ofstoofs+len-1doself#put_char(Bytes.getsi)donemethodput_strings=String.iterself#put_charsmethodflush=()methodfinish=()methodwipe=output_buffer#wipeendletencode()=newencodelethex_decode_charc=matchcwith|'0'..'9'->Char.codec-48|'A'..'F'->Char.codec-65+10|'a'..'f'->Char.codec-97+10|' '|'\t'|'\n'|'\r'->-1|_->raise(ErrorBad_encoding)classdecode=object(self)inheritbuffered_output256asoutput_buffermethodinput_block_size=1methodoutput_block_size=1valibuf=Array.make20valmutableipos=0methodput_charc=letn=hex_decode_charcinifn>=0thenbeginibuf.(ipos)<-n;ipos<-ipos+1;ifipos=2thenbeginself#ensure_capacity1;Bytes.setobufoend(Char.chr(ibuf.(0)lsl4loribuf.(1)));oend<-oend+1;ipos<-0endendmethodput_substringsofslen=fori=ofstoofs+len-1doself#put_char(Bytes.getsi)donemethodput_strings=String.iterself#put_charsmethodput_byteb=self#put_char(Char.chrb)methodflush=ifipos<>0thenraise(ErrorWrong_data_length)methodfinish=ifipos<>0thenraise(ErrorBad_encoding)methodwipe=Array.fillibuf020;output_buffer#wipeendletdecode()=newdecodeend(* Compression *)moduleZlib=structtypestreamtypeflush_command=Z_NO_FLUSH|Z_SYNC_FLUSH|Z_FULL_FLUSH|Z_FINISHexternaldeflate_init:int->bool->stream="caml_zlib_deflateInit"externaldeflate:stream->bytes->int->int->bytes->int->int->flush_command->bool*int*int="caml_zlib_deflate_bytecode""caml_zlib_deflate"externaldeflate_end:stream->unit="caml_zlib_deflateEnd"externalinflate_init:bool->stream="caml_zlib_inflateInit"externalinflate:stream->bytes->int->int->bytes->int->int->flush_command->bool*int*int="caml_zlib_inflate_bytecode""caml_zlib_inflate"externalinflate_end:stream->unit="caml_zlib_inflateEnd"classcompresslevelwrite_zlib_header=object(self)valzs=deflate_initlevelwrite_zlib_headerinheritbuffered_output512asoutput_buffermethodinput_block_size=1methodoutput_block_size=1methodput_substringsrcofslen=iflen>0thenbeginself#ensure_capacity256;let(_,used_in,used_out)=deflatezssrcofslenobufoend(Bytes.lengthobuf-oend)Z_NO_FLUSHinoend<-oend+used_out;ifused_in<lenthenself#put_substringsrc(ofs+used_in)(len-used_in)endmethodput_strings=self#put_substring(Bytes.unsafe_of_strings)0(String.lengths)methodput_charc=self#put_string(String.make1c)methodput_byteb=self#put_char(Char.chrb)methodflush=self#ensure_capacity256;let(_,_,used_out)=deflatezs(Bytes.unsafe_of_string"")00obufoend(Bytes.lengthobuf-oend)Z_SYNC_FLUSHinoend<-oend+used_out;ifoend=Bytes.lengthobufthenself#flushmethodfinish=self#ensure_capacity256;let(finished,_,used_out)=deflatezs(Bytes.unsafe_of_string"")00obufoend(Bytes.lengthobuf-oend)Z_FINISHinoend<-oend+used_out;iffinishedthendeflate_endzselseself#finishmethodwipe=output_buffer#wipeendletcompress?(level=6)?(write_zlib_header=false)()=newcompresslevelwrite_zlib_headerclassuncompressexpect_zlib_header=object(self)valzs=inflate_initexpect_zlib_headerinheritbuffered_output512asoutput_buffermethodinput_block_size=1methodoutput_block_size=1methodput_substringsrcofslen=iflen>0thenbeginself#ensure_capacity256;let(finished,used_in,used_out)=inflatezssrcofslenobufoend(Bytes.lengthobuf-oend)Z_SYNC_FLUSHinoend<-oend+used_out;ifused_in<lenthenbeginiffinishedthenraise(Error(Compression_error("Zlib.uncompress","garbage at end of compressed data")));self#put_substringsrc(ofs+used_in)(len-used_in)endendmethodput_strings=self#put_substring(Bytes.unsafe_of_strings)0(String.lengths)methodput_charc=self#put_string(String.make1c)methodput_byteb=self#put_char(Char.chrb)methodflush=()methodfinish=letrecdo_finishfirst_finish=self#ensure_capacity256;let(finished,_,used_out)=inflatezs(Bytes.unsafe_of_string" ")0(iffirst_finishthen1else0)obufoend(Bytes.lengthobuf-oend)Z_SYNC_FLUSHinoend<-oend+used_out;ifnotfinishedthendo_finishfalseindo_finishtrue;inflate_endzsmethodwipe=output_buffer#wipeendletuncompress?(expect_zlib_header=false)()=newuncompressexpect_zlib_headerend(* Utilities *)letxor_bytessrcsrc_ofsdstdst_ofslen=iflen<0||src_ofs<0||src_ofs>Bytes.lengthsrc-len||dst_ofs<0||dst_ofs>Bytes.lengthdst-lentheninvalid_arg"xor_bytes";xor_bytessrcsrc_ofsdstdst_ofslenletxor_stringsrcsrc_ofsdstdst_ofslen=iflen<0||src_ofs<0||src_ofs>String.lengthsrc-len||dst_ofs<0||dst_ofs>Bytes.lengthdst-lentheninvalid_arg"xor_string";xor_stringsrcsrc_ofsdstdst_ofslenletmod_powerabc=Bn.to_bytes~numbits:(String.lengthc*8)(Bn.mod_power(Bn.of_bytesa)(Bn.of_bytesb)(Bn.of_bytesc))letmod_multabc=Bn.to_bytes~numbits:(String.lengthc*8)(Bn.mod_(Bn.mult(Bn.of_bytesa)(Bn.of_bytesb))(Bn.of_bytesc))