123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277(*****************************************************************************)(* *)(* 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_coreopenLang_stdlibmoduletypeAFFINE=functor(L:LIB)->sigopenLmoduleCurve=Mec.Curve.Curve25519.AffineEdwards(** Represents an element from a base field *)typenat_mod(** Represents a point on the curve in affine coordinates *)typepoint=nat_mod*nat_modvalpoint_encoding:(Curve.t,pointrepr,point)Encodings.encoding(** Returns a Plompiler representation of a point *)valinput_point:?kind:input_kind->Z.t*Z.t->pointreprt(** [is_on_curve p] checks whether a point [p] is on the curve *)valis_on_curve:pointrepr->boolreprt(** [from_coordinates x y] constructs a point [p = (x, y)] from coordinates
[x] and [y]. The function also checks whether the point is on the curve
(but not necessarily in the subgroup) *)valfrom_coordinates:nat_modrepr->nat_modrepr->pointreprt(** [unsafe_from_coordinates x y] is similar to {!from_coordinates} but
does not verify the point is on the curve. It can be used to build a
variable of type [point] without adding any constraint *)valunsafe_from_coordinates:nat_modrepr->nat_modrepr->pointreprt(** [get_x_coordinate p] returns a first coordinate [x] of a point [p] *)valget_x_coordinate:pointrepr->nat_modrepr(** [get_y_coordinate p] returns a second coordinate [y] of a point [p] *)valget_y_coordinate:pointrepr->nat_modrepr(** Returns the point at infinity of the curve (additive identity) *)valid:pointreprt(** Returns the base point of the curve (a fixed generator) *)valbase_point:pointreprt(** [add p q] computes a point addition [p + q] *)valadd:pointrepr->pointrepr->pointreprt(** [negate p] computes a point negation [-p] *)valnegate:pointrepr->pointreprt(** [cond_add p q b] returns [p + b * q], i.e., either a point addition [p] and [q]
or a point [p] based on the value [b] *)valcond_add:pointrepr->pointrepr->boolrepr->pointreprt(** [double p] computes a point doubling [p + p] *)valdouble:pointrepr->pointreprt(** [scalar_mul s p] computes a point multiplication [p] by a scalar [s].
The scalar [s] is encoded in little-endian order *)valscalar_mul:boollistrepr->pointrepr->pointreprt(** [multi_scalar_mul ls lp] computes the multi-scalar multiplication
[s₁·p₁ + s₂·p₂ + … + sₖ·pₖ] *)valmulti_scalar_mul:boollistlistrepr->pointlistrepr->pointreprt(** Returns the order of the prime-order subgroup of the elliptic curve group *)valscalar_order:Z.t(** Returns the prime number defining the underlying field *)valbase_order:Z.t(** [to_compressed_bytes p] returns the compressed representation of a point
[p = (x, y)] in little-endian bytes [pow2 255 * (x % 2) + y] *)valto_compressed_bytes:pointrepr->Bytes.blreprtendmoduleMakeEdwards25519:AFFINE=functor(L:LIB)->structopenLmoduleM=Gadget_mod_arith.ArithMod25519(L)moduleCurve=Mec.Curve.Curve25519.AffineEdwardstypenat_mod=M.mod_inttypepoint=nat_mod*nat_modletis_on_curvep:boolreprt=with_label~label:"Edwards25519.is_on_curve"@@letx,y=of_pairpin(* y^2 - x^2 = 1 + d * x^2 * y^2 *)let*x2=M.mulxxinlet*y2=M.mulyyinlet*lhs=M.suby2x2inlet*rhs=letqm=Curve.Base.to_zCurve.dinlet*x2y2=M.mulx2y2inlet*dx2y2=M.mul_constantx2y2qminM.add_constantdx2y2Z.oneinM.equallhsrhsletpoint_encoding:(Curve.t,pointrepr,point)Encodings.encoding=letopenEncodingsinwith_implicit_bool_checkis_on_curve@@convof_pair(fun(x,y)->pairxy)(func->letto_limbs(x:Curve.Base.t)=Utils.z_to_limbs~len:M.nb_limbs~base:M.base@@Curve.Base.to_zx|>List.mapS.of_zinletx=Curve.get_u_coordinatec|>to_limbsinlety=Curve.get_v_coordinatec|>to_limbsin(x,y))(fun(x,y)->letof_limbs(n:S.tlist)=Utils.z_of_limbs~base:M.base@@List.mapS.to_zn|>Curve.Base.of_zinCurve.from_coordinates_exn~u:(of_limbsx)~v:(of_limbsy))(Encodings.obj2_encodingM.mod_int_encodingM.mod_int_encoding)letinput_point?(kind=`Private)(x,y):pointreprt=let*x=M.input_mod_int~kindxinlet*y=M.input_mod_int~kindyinret(pairxy)letfrom_coordinatesuv=with_label~label:"Edwards25519.from_coordinates"@@letp=pairuvinwith_bool_check(is_on_curvep)>*retpletunsafe_from_coordinatesuv=with_label~label:"Edwards25519.unsafe_from_coordinates"(pairuv|>ret)letget_x_coordinatep=of_pairp|>fstletget_y_coordinatep=of_pairp|>snd(* nat_to_bytes_le 32 (pow2 255 * (x % 2) + y) *)letto_compressed_bytesp:Bytes.blreprt=letpx=get_x_coordinatepinletpy=get_y_coordinatepin(* px_bytes and py_bytes are in little-endian *)let*px_bytes=M.bytes_of_mod_intpxinlet*py_bytes=M.bytes_of_mod_intpyinletpx0=List.hd@@of_listpx_bytesinret@@to_list(of_listpy_bytes@[px0])letid=let*zero=M.zeroinlet*one=M.oneinunsafe_from_coordinateszerooneletbase_point=let*x=Curve.get_u_coordinateCurve.one|>Curve.Base.to_z|>M.constantinlet*y=Curve.get_v_coordinateCurve.one|>Curve.Base.to_z|>M.constantinunsafe_from_coordinatesxyletaddpq:pointreprt=letx1,y1=of_pairpinletx2,y2=of_pairqinlet*x1x2=M.mulx1x2inlet*y1y2=M.muly1y2inlet*dx1x2y1y2=let*x1x2y1y2=M.mulx1x2y1y2inM.mul_constantx1x2y1y2(Curve.Base.to_zCurve.d)in(* x3 = (x1 * y2 + y1 * x2) / (1 + dx1x2y1y2) *)let*x3=let*x1y2=M.mulx1y2inlet*y1x2=M.muly1x2inlet*x1y2_plus_y1x2=M.addx1y2y1x2inlet*one_plus_dx1x2y1y2=M.add_constantdx1x2y1y2Z.oneinM.divx1y2_plus_y1x2one_plus_dx1x2y1y2in(* y3 = (y1y2 + x1x2) / (1 - dx1x2y1y2) *)let*y3=let*y1y2_plus_x1x2=M.addy1y2x1x2inlet*minus_dx1x2y1y2=M.negdx1x2y1y2inlet*one_minus_dx1x2y1y2=M.add_constantminus_dx1x2y1y2Z.oneinM.divy1y2_plus_x1x2one_minus_dx1x2y1y2inunsafe_from_coordinatesx3y3letnegatep=letx,y=of_pairpinlet*x=M.negxinunsafe_from_coordinatesxyletpoint_or_zeropb=let*idinBool.ifthenelsebpid(* compute R = P + b * Q *)letcond_addpqb=let*bq=point_or_zeroqbinaddpbqletdoublep=addppletscalar_order:Z.t=Curve.Scalar.orderletbase_order:Z.t=Curve.Base.orderletscalar_mulsp=let*one=Bool.constanttrueinwith_label~label:"Edwards25519.scalar_mul"@@letrev_s=List.rev(of_lists)inlet*init=point_or_zerop(List.hdrev_s)infoldM(funaccb->let*acc=cond_addaccacconeincond_addaccpb)init(List.tlrev_s)letmulti_scalar_mullslp=let*one=Bool.constanttrueinwith_label~label:"Edwards.multi_scalar_mul"@@(* Check we apply Shamir's trick on at least 2 points *)let()=assert(List.(length(of_listls)>1))in(* Converting ls to ls' = [[s_11; ...; s_n1]; ...; [s_1m; ...; s_nm]] *)letls=List.mapof_list(of_listls)|>Utils.transpose|>List.revinletpoints=of_listlpin(* Check we perform scalar multiplications on lists of at least 1 bit *)assert(List.(lengthls>0));(* Initializing the accumulator with the first round of Shamir's trick *)letheads=List.hdlsinlet*init=point_or_zero(List.hdpoints)(List.hdheads)inlet*init=fold2Mcond_addinit(List.tlpoints)(List.tlheads)in(* Applying Shamir's trick on the rest of the rounds *)foldM(funacclb->let*acc=cond_addaccacconeinfold2Mcond_addaccpoints(of_listlb))initList.(mapto_list(tlls))end