123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109(*****************************************************************************)(* *)(* Copyright (c) 2020-2021 Danny Willems <be.danny.willems@gmail.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. *)(* *)(*****************************************************************************)moduletypeT=sigincludeFf_sig.PRIME(** Check if a point, represented as a byte array, is in the field **)valcheck_bytes:Bytes.t->boolendmoduleMakeFr(Stubs:S.RAW_BASE):T=structincludeS.Make(Stubs)lettwo_z=Z.succZ.oneletfactor_power_of_two=letrecauxin=let(q,r)=Z.ediv_remntwo_zinifZ.equalrZ.zerothenauxInt.(succi)qelse(i,n)inaux0(Z.predorder)letempty()=Bytes.makesize_in_bytes'\000'letto_stringa=Z.to_string(Z.of_bits(Bytes.to_string(to_bytesa)))letto_za=Z.of_bits(Bytes.to_string(to_bytesa))letof_strings=letg=empty()inlets=Bytes.of_string(Z.to_bits(Z.erem(Z.of_strings)order))inBytes.blits0g0(min(Bytes.lengths)size_in_bytes);of_bytes_exngletof_zz=letz=Bytes.of_string(Z.to_bits(Z.eremzorder))inletx=empty()inBytes.blitz0x0(min(Bytes.lengthz)size_in_bytes);of_bytes_exnxletlegendre_symbolx=ifis_zeroxthenZ.zeroelseifis_one(powx(Z.divexact(Z.predorder)(Z.of_int2)))thenZ.oneelseZ.negZ.oneletis_quadratic_residuex=ifis_zeroxthentrueelseZ.equal(legendre_symbolx)Z.oneletrecpick_non_square()=letz=random()inifZ.equal(legendre_symbolz)(Z.of_int(-1))thenzelsepick_non_square()letsqrt_optx=ifnot(is_quadratic_residuex)thenNoneelse(* https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm *)let(s,q)=factor_power_of_twoin(* implies p = 3 mod 4 *)ifInt.equals1then(* r = x^((p + 1) / 4) *)letr=powx(Z.divexact(Z.succorder)(Z.of_string"4"))inSomerelseletreccompute_lowest_n_2th_root_of_unity(i:int)xupper:int=letx=squarexinifis_onexthenielseifInt.(equaliupper)thenfailwith"Upperbound should be higher"(* should never happen in this case, just being explicit *)elsecompute_lowest_n_2th_root_of_unity(Int.succi)xupperinletz=pick_non_square()inletc=powzqinletrecauxmctr=ifeqtzerothenzero(* case x is zero *)elseifeqtonethenr(* base case *)elseleti=compute_lowest_n_2th_root_of_unity1tminletb=powc(Z.powtwo_zInt.(pred(submi)))inletm=iinletc=mulbbinlett=multcinletr=mulrbinauxmctrinSome(auxsc(powxq)(powx(Z.divexact(Z.succq)two_z)))end