123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458(*
For each curve form and each coordinates, an external link MUST be given
pointing to the addition and doubling formulas. The addition MUST support
doubling. It is verified by the ECProperties functor.
When adding a new curve form/coordinates, it is REQUIRED to add
- the number of additions (A),
- the number of additions with a constant (AC),
- the number of multiplications (M),
- the number of multiplications with a constant (MC),
- the number of negations (N),
- the number of substractions (SU),
- the number of divisions (DI),
- the number of doublings (DO),
- the number of squarings (SQ).
If there is a requirement for the parameters, it MUST be verified at the
module top level. It is RECOMMENDED, but not required, to use a specific
exception and raise it with a meaningful message.
When implementing a new form or new coordinates, it MUST be accompanied by
the instantiation of a standard/popular curve and test vectors in addition
to the generic PBT on EC properties. The origin of the test vectors MUST be
documented.
If any constant are defined at the top level, it MUST be accompanied by an
explanation on where it is used, and why.
In the case of an optimisation not listed in the associated paper/link
describing the formulas, the author MUST add an explanation. If it reduces
the number of field operations, the "original" and the "optimised" versions
MUST be compared in comments.
IMPROVEME:
- For the moment, there is no ec.mli. When adding a new form or new
coordinates, the functor signature MUST be added in the interface. If you
read these lines and there is a file ec.mli, please open an issue.
- For the moment, it is only RECOMMENDED to use a particular exception when
verifying the parameters. It must be changed to a REQUIREMENT.
- For the moment, there is no distinction between `not being on the curve`
and `not being in the prime subgroup`. This MUST change.
*)openBls12_381moduleMakeJacobianWeierstrass(Fq:Ff_sig.PRIME)(Fp:Ff_sig.PRIME)(Params:sigvala:Fq.tvalb:Fq.tvalcofactor:Z.tvalbytes_generator:Bytes.tend):Ec_sig.JacobianWeierstrassTwithtypeScalar.t=Fp.tandtypeBase.t=Fq.t=structlet()=assert(not(Fq.is_zeroParams.b))exceptionNot_on_curveofBytes.tmoduleBase=FqmoduleScalar=Fpleta=Params.aletb=Params.b(* checking the curve is non-singular *)let()=ifBase.is_zeroBase.((a*squarea)+(of_string"27"*squareb))thenfailwith"a^3 + 27 * b^2 must be different than zero"letcofactor=Params.cofactortypet={x:Fq.t;y:Fq.t;z:Fq.t}letsize_in_bytes=Fq.size_in_bytes*3letzero={x=Fq.zero;y=Fq.one;z=Fq.zero}letis_zerot=Fq.(t.x=zero)&&Fq.(t.z=zero)leteqt1t2=ifFq.(is_zerot1.z)&&Fq.(is_zerot2.z)thentrueelseifFq.is_zerot1.z||Fq.is_zerot2.zthenfalseelselett1z2=Fq.(squaret1.z)inlett1z3=Fq.(t1z2*t1.z)inlett2z2=Fq.(squaret2.z)inlett2z3=Fq.(t2z2*t2.z)inletx1=Fq.(t1.x/t1z2)inletx2=Fq.(t2.x/t2z2)inlety1=Fq.(t1.y/t1z3)inlety2=Fq.(t2.y/t2z3)inFq.(x1=x2&&y1=y2)(* https://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl *)letdoublet=ifis_zerotthenzeroelselet{x;y;z}=tinletxx=Fq.(squarex)inletyy=Fq.(squarey)inletyyyy=Fq.(squareyy)inletzz=Fq.(squarez)inlets=Fq.(double(square(x+yy)+negatexx+negateyyyy))inletm=Fq.(xx+xx+xx+(a*squarezz))inlett=Fq.(squarem+negate(doubles))inletx3=tinlety3=Fq.((m*(s+negatet))+negate(double(double(doubleyyyy))))inletz3=Fq.(square(y+z)+negateyy+negatezz)in{x=x3;y=y3;z=z3}(* https://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-add-2007-bl *)letaddt1t2=ifis_zerot1thent2elseifis_zerot2thent1elseifeqt1t2thendoublet1elselet{x=x1;y=y1;z=z1}=t1inlet{x=x2;y=y2;z=z2}=t2inletz1z1=Fq.(z1*z1)inletz2z2=Fq.(z2*z2)inletu1=Fq.(x1*z2z2)inletu2=Fq.(x2*z1z1)inlets1=Fq.(y1*z2*z2z2)inlets2=Fq.(y2*z1*z1z1)inleth=Fq.(u2+negateu1)inleti=Fq.(square(doubleh))inletj=Fq.(h*i)inletr=Fq.(double(s2+negates1))inletv=Fq.(u1*i)inletx3=Fq.(squarer+negatej+negate(doublev))inlety3=Fq.((r*(v+negatex3))+negate(double(s1*j)))inletz3=Fq.((square(z1+z2)+negatez1z1+negatez2z2)*h)in{x=x3;y=y3;z=z3}letnegate{x;y;z}={x;y=Fq.negatey;z}letmulxn=letrecauxxn=lettwo_z=Z.succZ.oneinifZ.equalnZ.zerothenzeroelseifZ.equalnZ.onethenxelseleta,r=Z.ediv_remntwo_zinifZ.equalrZ.zerothenaux(doublex)aelseaddx(auxx(Z.predn))inauxx(Scalar.to_zn)letis_on_curve~x~y~z=ifFq.is_zerox&&Fq.is_zerozthentrueelseifFq.is_zerozthenfalseelseletz2=Fq.(squarez)inletz3=Fq.(z*z2)inletx'=Fq.(x/z2)inlety'=Fq.(y/z3)inFq.((x'*x'*x')+(a*x')+b=y'*y')letis_in_prime_subgroup~x~y~z=letp={x;y;z}inifis_zeropthentrueelsenot(is_zero(mulp(Scalar.of_zcofactor)))letof_bytes_optbytes=(* no need to copy the bytes [p] because [Bytes.sub] is used and [Bytes.sub]
creates a new buffer *)ifBytes.lengthbytes<>size_in_bytesthenNoneelseletx_bytes=Bytes.subbytes0Fq.size_in_bytesinlety_bytes=Bytes.subbytesFq.size_in_bytesFq.size_in_bytesinletz_bytes=Bytes.subbytes(2*Fq.size_in_bytes)Fq.size_in_bytesinletx=Fq.of_bytes_optx_bytesinlety=Fq.of_bytes_opty_bytesinletz=Fq.of_bytes_optz_bytesinmatch(x,y,z)with|None,_,_|_,None,_|_,_,None->None(* Verify it is on the curve *)|Somex,Somey,Somez->ifFq.is_zerox&&Fq.is_zerozthenSomezeroelseifFq.is_zerozthenNoneelseifis_on_curve~x~y~z&&is_in_prime_subgroup~x~y~zthenSome{x;y;z}elseNoneletcheck_bytesbytes=matchof_bytes_optbyteswithSome_->true|None->falseletof_bytes_exnb=(* no need to copy the bytes [p] because [Bytes.sub] is used and [Bytes.sub]
creates a new buffer *)matchof_bytes_optbwithSomeg->g|None->raise(Not_on_curveb)letto_bytesg=letbuffer=Bytes.makesize_in_bytes'\000'inBytes.blit(Fq.to_bytesg.x)0buffer0Fq.size_in_bytes;Bytes.blit(Fq.to_bytesg.y)0bufferFq.size_in_bytesFq.size_in_bytes;Bytes.blit(Fq.to_bytesg.z)0buffer(2*Fq.size_in_bytes)Fq.size_in_bytes;bufferletone=of_bytes_exnParams.bytes_generatorletrandom?state()=(matchstatewithNone->()|Somes->Random.set_states);letrecaux()=letx=Fq.random()inlety_square=Fq.((x*x*x)+(a*x)+b)inlety_opt=Fq.sqrt_opty_squareinmatchy_optwith|None->aux()|Somey->mul{x;y;z=Fq.one}(Scalar.of_zcofactor)inaux()letget_x_coordinatet=t.xletget_y_coordinatet=t.yletget_z_coordinatet=t.zletfrom_coordinates_exn~x~y~z=ifis_on_curve~x~y~zthen{x;y;z}elseraise(Not_on_curve(Bytes.concatBytes.empty[Fq.to_bytesx;Fq.to_bytesy;Fq.to_bytesz]))letfrom_coordinates_opt~x~y~z=ifis_on_curve~x~y~zthenSome{x;y;z}elseNoneletget_affine_x_coordinatet=ifis_zerotthenfailwith"Zero"elseletz2=Fq.(squaret.z)inFq.(t.x/z2)letget_affine_y_coordinatet=ifis_zerotthenfailwith"Zero"elseletz3=Fq.(squaret.z*t.z)inFq.(t.y/z3)letfrom_affine_coordinates_exn~x~y=from_coordinates_exn~x~y~z:Fq.oneletfrom_affine_coordinates_opt~x~y=from_coordinates_exn~x~y~z:Fq.oneendmoduleMakeAffineWeierstrass(Fq:Ff_sig.PRIME)(Fp:Ff_sig.PRIME)(Params:sigvala:Fq.tvalb:Fq.tvalcofactor:Z.tvalbytes_generator:Bytes.tend):Ec_sig.AffineWeierstrassTwithtypeScalar.t=Fp.tandtypeBase.t=Fq.t=structlet()=assert(not(Fq.is_zeroParams.b))exceptionNot_on_curveofBytes.tmoduleBase=FqmoduleScalar=Fpleta=Params.aletb=Params.b(* checking the curve is non-singular *)let()=ifBase.is_zeroBase.((a*squarea)+(of_string"27"*squareb))thenfailwith"a^3 + 27 * b^2 must be different than zero"letcofactor=Params.cofactortypet=Infinity|Pof(Fq.t*Fq.t)letsize_in_bytes=Fq.size_in_bytes*2letzero=Infinityletbuffer_zero=Bytes.makesize_in_bytes'\000'letis_zerot=matchtwithInfinity->true|_->falseletis_on_curve~x~y=Fq.((x*x*x)+(a*x)+b=y*y)letto_bytesg=letbuffer=Bytes.makesize_in_bytes'\000'inmatchgwith|Infinity->buffer|P(x,y)->Bytes.blit(Fq.to_bytesx)0buffer0Fq.size_in_bytes;Bytes.blit(Fq.to_bytesy)0bufferFq.size_in_bytesFq.size_in_bytes;bufferleteqt1t2=match(t1,t2)with|Infinity,Infinity->true|Infinity,_|_,Infinity->false|P(x1,y1),P(x2,y2)->Fq.(x1=x2&&y1=y2)letdoublet=matchtwith|Infinity->Infinity|P(x,y)->letxx=Fq.(squarex)inletxx_3_plus_a=Fq.(doublexx+xx+a)inletdouble_x=Fq.(doublex)inletdouble_y=Fq.(doubley)inletsquare_double_y=Fq.(squaredouble_y)inletx3=Fq.((squarexx_3_plus_a/square_double_y)+negatedouble_x)inlettriple_x=Fq.(x+double_x)inlety3=Fq.((triple_x*xx_3_plus_a/double_y)+(negate(squarexx_3_plus_a*xx_3_plus_a)/(square_double_y*double_y)+negatey))inP(x3,y3)(* https://hyperelliptic.org/EFD/g1p/auto-shortw.html *)letaddt1t2=match(t1,t2)with|Infinity,t2->t2|t1,Infinity->t1|t1,t2wheneqt1t2->doublet1|P(x1,y1),P(x2,y2)->ifFq.(x1=x2&&y1=negatey2)thenInfinityelselety2_min_y1=Fq.(y2+negatey1)inletx2_min_x1=Fq.(x2+negatex1)inletslope=Fq.(y2_min_y1/x2_min_x1)inletsquare_slope=Fq.(squareslope)inletx3=Fq.(square_slope+negatex1+negatex2)inletdouble_x1=Fq.(doublex1)inletdouble_x1_plus_x2=Fq.(double_x1+x2)inlety3=Fq.((double_x1_plus_x2*slope)+negate(square_slope*slope)+negatey1)inP(x3,y3)letnegatep=matchpwithInfinity->Infinity|P(x,y)->P(x,Fq.negatey)letmulxn=letrecauxxn=lettwo_z=Z.succZ.oneinifZ.equalnZ.zerothenzeroelseifZ.equalnZ.onethenxelseleta,r=Z.ediv_remntwo_zinifZ.equalrZ.zerothenaux(doublex)aelseaddx(auxx(Z.predn))inauxx(Scalar.to_zn)letis_in_prime_subgroup~x~y=letp=P(x,y)inifis_zeropthentrueelsenot(is_zero(mulp(Scalar.of_zcofactor)))letof_bytes_optbytes=(* no need to copy the bytes [p] because [Bytes.sub] is used and [Bytes.sub]
creates a new buffer *)ifBytes.lengthbytes<>size_in_bytesthenNoneelseletx_bytes=Bytes.subbytes0Fq.size_in_bytesinlety_bytes=Bytes.subbytesFq.size_in_bytesFq.size_in_bytesinifBytes.equalbuffer_zerobytesthenSomeInfinityelseletx=Fq.of_bytes_optx_bytesinlety=Fq.of_bytes_opty_bytesinmatch(x,y)with|None,_|_,None->None(* Verify it is on the curve *)|Somex,Somey->ifis_on_curve~x~y&&is_in_prime_subgroup~x~ythenSome(P(x,y))elseNoneletcheck_bytesbytes=matchof_bytes_optbyteswithSome_->true|None->falseletof_bytes_exnb=(* no need to copy the bytes [p] because [Bytes.sub] is used and [Bytes.sub]
creates a new buffer *)matchof_bytes_optbwithSomeg->g|None->raise(Not_on_curveb)letone=of_bytes_exnParams.bytes_generatorletrandom?state()=(matchstatewithNone->()|Somes->Random.set_states);letrecaux()=letx=Fq.random()inlety_square=Fq.((x*x*x)+(a*x)+b)inlety_opt=Fq.sqrt_opty_squareinmatchy_optwith|None->aux()|Somey->mul(P(x,y))(Scalar.of_zParams.cofactor)inaux()letget_x_coordinatet=matchtwithInfinity->raise(Invalid_argument"Zero")|P(x,_y)->xletget_y_coordinatet=matchtwithInfinity->raise(Invalid_argument"Zero")|P(_x,y)->y(* FIXME:
To have functions to Montgomery, we need to be able to compute alpha which
is a root of the equation: z^3 + a*z + b = 0 which requires the cubic root
operation in F_q.
There is an open issue in ocaml-ff:
https://gitlab.com/dannywillems/ocaml-ff/-/issues/21
let alpha =
let two = Fq.of_string "2" in
let four = Fq.of_string "4" in
let twenty_seven = Fq.of_string "27" in
let a_cube = Fq.(mul a (square a)) in
let b_square = Fq.square b in
let delta = Fq.( b_square + negate ( (4 * a_cube ) / twenty_seven) ) in
let sqrt_delta = Fq.sqrt_opt delta in
if Option.is_none sqr_delta then assert (1=0);
let sqrt_delta = Option.get sqr_delta in
let u = Fq.(negate ((sqrt_delta + b) / two) ) in
let v = Fq.( (sqrt_delta + negate b) / two) in
let u_cbrt = Fq.cbrt_opt u in
let v_cbrt = Fq.cbrt_opt v in
if Option.(is_none u_cbrt || is_none v_cbrt) then assert (1=0);
Fq.(u_cbrt + v_cbrt)
let s =
let three = Fq.of_string "3" in
let aux = Fq.(three * (square alpha) + a)
let sqrt_aux = Fq.sqrt_opt aux in
if Option.is_none sqrt_aux then assert (1=0);
Fq.( 1 / (Option.get sqrt_aux) )
(* https://en.wikipedia.org/wiki/Montgomery_curve *)
let to_montgomery t =
match t with
| Infinity -> raise (Invalid_argument "Zero")
| P (x, y) ->
Some (Fq.(s * (x - alpha)), Fq.(s*y))
let to_montgomery_curve_parameters () =
let three = Fr.of_string "3" in
let cond = Fq.(three * (square alpha) * a) in
if not (Fq.is_quadratic_residue cond) then None
else
let gen = Option.get (to_montgomery one) in
let a = Fq.( three * alpha * s) in
let b = s in
Some (a, b, Params.cofactor, gen)
*)letfrom_coordinates_exn~x~y=ifis_on_curve~x~ythenP(x,y)elseraise(Not_on_curve(Bytes.concatBytes.empty[Fq.to_bytesx;Fq.to_bytesy]))letfrom_coordinates_opt~x~y=ifis_on_curve~x~ythenSome(P(x,y))elseNoneletof_compressed_bytes_optbs=(* required to avoid side effect! *)letbs=Bytes.copybsinletlength=Bytes.lengthbsiniflength<>Base.size_in_bytesthenNoneelseifbs=Bytes.makeBase.size_in_bytes'\000'thenSomeInfinityelse(* We get the last bit of the input, representing the bit of u. We also
remove the last bit from the bytes we received
*)letlast_byte=int_of_char@@Bytes.getbs(length-1)inletsign=last_bytelsr7inletlast_byte_without_sign=last_byteland0b01111111inBytes.setbs(length-1)(char_of_intlast_byte_without_sign);(* We compute u *)letx=Base.of_bytes_optbsinmatchxwith|None->None|Somex->(letyy=Base.((x*x*x)+(a*x)+b)inlety_opt=Base.sqrt_optyyinlety=matchy_optwith|None->None|Somey->(* computed before for constant time *)letnegated_y=Base.negateyinlety_first_byte=Bytes.get(Base.to_bytesy)0inletis_sign_flipped=int_of_chary_first_bytelxorsignland1inSome(ifis_sign_flipped=0thenyelsenegated_y)inmatchywith|Somey->ifis_in_prime_subgroup~x~ythenSome(P(x,y))elseNone|None->None)letof_compressed_bytes_exnb=matchof_compressed_bytes_optbwith|None->raise(Not_on_curveb)|Somep->pletto_compressed_bytesp=matchpwith|Infinity->Bytes.makeBase.size_in_bytes'\000'|P(x,y)->letx_bytes=Base.to_bytesxinlety_bytes=Base.to_bytesyinlety_first_byte=int_of_char(Bytes.gety_bytes0)inletx_last_byte=int_of_char(Bytes.getx_bytes(Base.size_in_bytes-1))in(* Get the first bit of y, i.e. the sign of y *)letsign_of_y=y_first_byteland0b00000001in(* Set the last bit of the last byte of x to the sign of y *)letx_last_byte_with_y=x_last_bytelor(sign_of_ylsl7)inBytes.setx_bytes(Base.size_in_bytes-1)(char_of_intx_last_byte_with_y);x_bytesendmoduleMakeProjectiveWeierstrass(Fq:Ff_sig.PRIME)(Fp:Ff_sig.PRIME)(Params:sigvala:Fq.tvalb:Fq.tvalcofactor:Z.tvalbytes_generator:Bytes.tend):Ec_sig.ProjectiveWeierstrassTwithtypeScalar.t=Fp.tandtypeBase.t=Fq.t=structlet()=assert(not(Fq.is_zeroParams.b))exceptionNot_on_curveofBytes.tmoduleBase=FqmoduleScalar=Fpleta=Params.aletb=Params.b(* checking the curve is non-singular *)let()=ifBase.is_zeroBase.((a*squarea)+(of_string"27"*squareb))thenfailwith"a^3 + 27 * b^2 must be different than zero"letcofactor=Params.cofactortypet={x:Fq.t;y:Fq.t;z:Fq.t}letsize_in_bytes=Fq.size_in_bytes*3letzero={x=Fq.zero;y=Fq.one;z=Fq.zero}letis_zerot=Fq.(t.x=zero)&&Fq.(t.z=zero)letis_on_curve~x~y~z=ifFq.is_zerox&&Fq.is_zerozthentrueelseifFq.is_zerozthenfalseelseletx'=Fq.(x/z)inlety'=Fq.(y/z)inFq.((x'*x'*x')+(a*x')+b=y'*y')letaddt1t2=(* See https://github.com/o1-labs/snarky/blob/master/snarkette/elliptic_curve.ml *)ifis_zerot1thent2elseifis_zerot2thent1elseletopenFqinletx1z2=t1.x*t2.zinletx2z1=t1.z*t2.xinlety1z2=t1.y*t2.zinlety2z1=t1.z*t2.yinifx1z2=x2z1&&y1z2=y2z1then(* Double case *)letxx=squaret1.xinletzz=squaret1.zinletw=(a*zz)+(xx+xx+xx)inlety1z1=t1.y*t1.zinlets=y1z1+y1z1inletss=squaresinletsss=s*ssinletr=t1.y*sinletrr=squarerinletb=square(t1.x+r)+negatexx+negaterrinleth=squarew+negate(b+b)inletx3=h*sinlety3=(w*(b+negateh))+negate(rr+rr)inletz3=sssin{x=x3;y=y3;z=z3}else(* Generic case *)letz1z2=t1.z*t2.zinletu=y2z1+negatey1z2inletuu=squareuinletv=x2z1+negatex1z2inletvv=squarevinletvvv=v*vvinletr=vv*x1z2inleta=(uu*z1z2)+negate(vvv+r+r)inletx3=v*ainlety3=(u*(r+negatea))+negate(vvv*y1z2)inletz3=vvv*z1z2in{x=x3;y=y3;z=z3}letdoublet=addttletnegate{x;y;z}={x;y=Fq.negatey;z}leteqt1t2=ifFq.(is_zerot1.z)&&Fq.(is_zerot2.z)thentrueelseifFq.is_zerot1.z||Fq.is_zerot2.zthenfalseelseletx1=Fq.(t1.x/t1.z)inletx2=Fq.(t2.x/t2.z)inlety1=Fq.(t1.y/t1.z)inlety2=Fq.(t2.y/t2.z)inFq.(x1=x2&&y1=y2)letmulxn=letrecauxxn=lettwo_z=Z.succZ.oneinifZ.equalnZ.zerothenzeroelseifZ.equalnZ.onethenxelseleta,r=Z.ediv_remntwo_zinifZ.equalrZ.zerothenaux(doublex)aelseaddx(auxx(Z.predn))inauxx(Scalar.to_zn)letis_in_prime_subgroup~x~y~z=letp={x;y;z}inifis_zeropthentrueelsenot(is_zero(mulp(Scalar.of_zcofactor)))letof_bytes_optbytes=(* no need to copy the bytes [p] because [Bytes.sub] is used and [Bytes.sub]
creates a new buffer *)ifBytes.lengthbytes<>size_in_bytesthenNoneelseletx_bytes=Bytes.subbytes0Fq.size_in_bytesinlety_bytes=Bytes.subbytesFq.size_in_bytesFq.size_in_bytesinletz_bytes=Bytes.subbytes(2*Fq.size_in_bytes)Fq.size_in_bytesinletx=Fq.of_bytes_optx_bytesinlety=Fq.of_bytes_opty_bytesinletz=Fq.of_bytes_optz_bytesinmatch(x,y,z)with|None,_,_|_,None,_|_,_,None->None(* Verify it is on the curve *)|Somex,Somey,Somez->ifFq.is_zerox&&Fq.is_zerozthenSomezeroelseifFq.is_zerozthenNoneelseifis_on_curve~x~y~z&&is_in_prime_subgroup~x~y~zthenSome{x;y;z}elseNoneletcheck_bytesbytes=matchof_bytes_optbyteswithSome_->true|None->falseletof_bytes_exnb=(* no need to copy the bytes [p] because [Bytes.sub] is used and [Bytes.sub]
creates a new buffer *)matchof_bytes_optbwithSomeg->g|None->raise(Not_on_curveb)letto_bytesg=letbuffer=Bytes.makesize_in_bytes'\000'inBytes.blit(Fq.to_bytesg.x)0buffer0Fq.size_in_bytes;Bytes.blit(Fq.to_bytesg.y)0bufferFq.size_in_bytesFq.size_in_bytes;Bytes.blit(Fq.to_bytesg.z)0buffer(2*Fq.size_in_bytes)Fq.size_in_bytes;bufferletone=of_bytes_exnParams.bytes_generatorletrandom?state()=(matchstatewithNone->()|Somes->Random.set_states);letrecaux()=letx=Fq.random()inlety_square=Fq.((x*x*x)+(a*x)+b)inlety_opt=Fq.sqrt_opty_squareinmatchy_optwith|None->aux()|Somey->mul{x;y;z=Fq.one}(Scalar.of_zcofactor)inaux()letget_x_coordinatet=t.xletget_y_coordinatet=t.yletget_z_coordinatet=t.zletfrom_coordinates_exn~x~y~z=ifis_on_curve~x~y~z&&is_in_prime_subgroup~x~y~zthen{x;y;z}elseraise(Not_on_curve(Bytes.concatBytes.empty[Fq.to_bytesx;Fq.to_bytesy;Fq.to_bytesz]))letfrom_coordinates_opt~x~y~z=ifis_on_curve~x~y~z&&is_in_prime_subgroup~x~y~zthenSome{x;y;z}elseNoneletget_affine_x_coordinatet=ifis_zerotthenfailwith"Zero"elseFq.(t.x/t.z)letget_affine_y_coordinatet=ifis_zerotthenfailwith"Zero"elseFq.(t.y/t.z)letfrom_affine_coordinates_exn~x~y=from_coordinates_exn~x~y~z:Fq.oneletfrom_affine_coordinates_opt~x~y=from_coordinates_exn~x~y~z:Fq.oneendmoduleMakeAffineMontgomery(Fq:Ff_sig.PRIME)(Fp:Ff_sig.PRIME)(Params:sigvala:Fq.tvalb:Fq.tvalcofactor:Z.tvalbytes_generator:Bytes.tend):Ec_sig.AffineMontgomeryTwithtypeScalar.t=Fp.tandtypeBase.t=Fq.t=struct(* Costello, Craig, and Benjamin Smith. "Montgomery curves and their
arithmetic."
Journal of Cryptographic Engineering 8.3 (2018): 227-240.
(https://arxiv.org/pdf/1703.01863.pdf) *)(* Summary for the complexity:
Addition: 6A + 1AC + 1M + 1MC + 4N + 1DI
Doubling: 4A + 2AC + 1M + 4MC + 3N + 1DI + 1DO + 2SQ
*)(* Checking non-singularity:
- if b=0 then the Curve is a union of 3 lines;
if a^2=4 then the curve is a nodal cubic. *)let()=assert(notFq.(eq(squareParams.a)(Fq.of_string"4")))let()=assert(not(Fq.is_zeroParams.b))exceptionNot_on_curveofBytes.tmoduleBase=FqmoduleScalar=Fp(* used later in addition and doubling formulas *)lettwo=Fq.(one+one)letthree=Fq.(one+two)leta=Params.alettwo_a=Fq.multwoaletb=Params.blettwo_b=Fq.multwobletthree_b=Fq.multhreebletcofactor=Params.cofactortypet=Infinity|Pof(Fq.t*Fq.t)letsize_in_bytes=Fq.size_in_bytes*2letzero=Infinityletbuffer_zero=Bytes.makesize_in_bytes'\000'letis_zerot=matchtwithInfinity->true|_->falseleteqt1t2=match(t1,t2)with|Infinity,Infinity->true|Infinity,_|_,Infinity->false|P(x1,y1),P(x2,y2)->Fq.(x1=x2&&y1=y2)(* The operation for adding two points are the same whether we add the same or
distinct points
except for the calculus of the slope.
(https://arxiv.org/pdf/1703.01863.pdf)
Let P(x_p, y_p) + P(x_q, y_q) = P(x_r, y_r), then
x_r = Params.b * slope^2 - (x_p + x_q) - Params.a
y_r = (2 * x_p + x_ q + Params.a) * slope - Params.b * slope^3 - y_p
= slope * (x_p - x_q) - y_p
*)(* Complexity (update above summary if any change):
1SQ
+ 1MC + 1MC + 1A + 1AC + 1MC + 1DI -> 3MC + 1A + 1AC + 1DI (SLOPE COMPUTATION)
+ 1SQ + 1DO + 1MC + 1AC + 1N + 1A (X COMPUTATION)
+ 1N + 1N + 1A + 1M + 1A -> 2N + 2A + 1M (Y COMPUTATION USING X)
Total:
4A + 2AC + 1M + 4MC + 3N + 1DI + 1DO + 2SQ
*)letdoublet=matchtwith|Infinity->Infinity|P(x,y)->(* Slope computation *)letxx=Fq.(squarex)in(* slope = (3 * x^2 + 2 * Params.a * x + 1) / (2 * Params.b * y) *)letthree_xx=Fq.(three*xx)inlettwo_a_x=Fq.(two_a*x)inletl_num=Fq.(three_xx+two_a_x)inletl_num=Fq.(l_num+Fq.one)inletl_div=Fq.(two_b*y)inletl=Fq.(l_num/l_div)in(* x computation *)letll=Fq.squarelinlettwo_x=Fq.doublexinletb_ll=Fq.(b*ll)inleta_two_x=Fq.(a+two_x)inletneg_a_two_x=Fq.(negatea_two_x)inletx3=Fq.(b_ll+neg_a_two_x)in(* computing y3 by using x3, see 2.2. There is a typo when giving a
shorter formula for y. It must be x_plus instead of x_q
*)letneg_x3=Fq.negatex3inletneg_y=Fq.negateyinletx_plus_neg_x3=Fq.(x+neg_x3)inletl_x_plus_neg_x3=Fq.(l*x_plus_neg_x3)inlety3=Fq.(l_x_plus_neg_x3+neg_y)inP(x3,y3)(* Complexity (update above summary if any change):
(SLOPE COMPUTATION)
1N + 1N + 1A + 1A + 1DI
-> 2A + 2N + 1DI
(X COMPUTATION)
+ 1SQ + 1A + 1MC + 1AC + 1N + 1A
-> 2A + 1AC + 1MC + 1N + 1SQ
(Y COMPUTATION)
+ 1N + 1A + 1M + 1A
-> 2A + 1M + 1N
Total:
6A + 1AC + 1M + 1MC + 4N + 1DI
*)letaddt1t2=match(t1,t2)with|Infinity,t2->t2|t1,Infinity->t1|t1,t2wheneqt1t2->doublet1|P(x1,y1),P(x2,y2)->ifFq.(x1=x2&&y1=negatey2)thenInfinityelse(* slope = (y2 - y1) / (x2 - x1) *)letneg_y1=Fq.(negatey1)inletneg_x1=Fq.(negatex1)inlety2_min_y1=Fq.(y2+neg_y1)inletx2_min_x1=Fq.(x2+neg_x1)inletl=Fq.(y2_min_y1/x2_min_x1)in(* x computation *)letll=Fq.(squarel)inletx2_plus_x1=Fq.(x1+x2)inletb_ll=Fq.(b*ll)inleta_plus_x2_plus_x1=Fq.(a+x2_plus_x1)inletneg_a_plus_x2_plus_x1=Fq.(negatea_plus_x2_plus_x1)inletx3=Fq.(b_ll+neg_a_plus_x2_plus_x1)in(* y computation
Cost improvement by using x3 directly:
3A + 1AC + 2M + 1MC -> 2A + 1M + 1N
Saving 1A, 1AC, 1M, 1MC and costs 1 extra N
*)letneg_x3=Fq.(negatex3)inletx1_min_x3=Fq.(x1+neg_x3)inletl_x1_min_x3=Fq.(l*x1_min_x3)inlety3=Fq.(l_x1_min_x3+neg_y1)inP(x3,y3)letnegatep=matchpwithInfinity->Infinity|P(x,y)->P(x,Fq.negatey)letmulxn=letrecauxxn=lettwo_z=Z.succZ.oneinifZ.equalnZ.zerothenzeroelseifZ.equalnZ.onethenxelseleta,r=Z.ediv_remntwo_zinifZ.equalrZ.zerothenaux(doublex)aelseaddx(auxx(Z.predn))inauxx(Scalar.to_zn)letis_on_curve~x~y=letxx=Fq.squarexinletyy=Fq.squareyinFq.((x*xx)+(a*xx)+x=b*yy)letis_in_prime_subgroup~x~y=letp=P(x,y)inifis_zeropthentrueelsenot(is_zero(mulp(Scalar.of_zcofactor)))letof_bytes_optbytes=(* no need to copy the bytes [p] because [Bytes.sub] is used and [Bytes.sub]
creates a new buffer *)ifBytes.lengthbytes<>size_in_bytesthenNoneelseletx_bytes=Bytes.subbytes0Fq.size_in_bytesinlety_bytes=Bytes.subbytesFq.size_in_bytesFq.size_in_bytesinifBytes.equalbuffer_zerobytesthenSomeInfinityelseletx=Fq.of_bytes_optx_bytesinlety=Fq.of_bytes_opty_bytesinmatch(x,y)with|None,_|_,None->None(* Verify it is on the curve *)|Somex,Somey->ifis_on_curve~x~y&&is_in_prime_subgroup~x~ythenSome(P(x,y))elseNoneletcheck_bytesbytes=matchof_bytes_optbyteswithSome_->true|None->falseletof_bytes_exnb=(* no need to copy the bytes [p] because [Bytes.sub] is used and [Bytes.sub]
creates a new buffer *)matchof_bytes_optbwithSomeg->g|None->raise(Not_on_curveb)letto_bytesg=letbuffer=Bytes.makesize_in_bytes'\000'inmatchgwith|Infinity->buffer|P(x,y)->Bytes.blit(Fq.to_bytesx)0buffer0Fq.size_in_bytes;Bytes.blit(Fq.to_bytesy)0bufferFq.size_in_bytesFq.size_in_bytes;bufferletone=of_bytes_exnParams.bytes_generatorletis_in_prime_subgroup~x~y=letp=P(x,y)inifis_zeropthentrueelsenot(is_zero(mulp(Scalar.of_zcofactor)))letrandom?state()=(matchstatewithNone->()|Somes->Random.set_states);letrecaux()=letx=Fq.random()inletxx=Fq.mulxxinlety_square=Fq.(((x*xx)+(a*xx)+x)/b)inlety_opt=Fq.sqrt_opty_squareinmatchy_optwith|None->aux()|Somey->mul(P(x,y))(Scalar.of_zParams.cofactor)inaux()letget_x_coordinatet=matchtwithInfinity->raise(Invalid_argument"Zero")|P(x,_y)->xletget_y_coordinatet=matchtwithInfinity->raise(Invalid_argument"Zero")|P(_x,y)->y(* https://en.wikipedia.org/wiki/Montgomery_curve *)letto_twistedt=matchtwith|Infinity->raise(Invalid_argument"Zero")|P(x,y)->ifFq.is_zeroy||Fq.(is_zero(one+x))thenNoneelseSomeFq.(x/y,(x+negateone)/(x+one))letto_twisted_curve_parameters()=letgen=to_twistedoneinifOption.is_nonegenthenNoneelseleta=Fq.((Params.a+two)/Params.b)inletd=Fq.((Params.a+negatetwo)/Params.b)inSome(a,d,Params.cofactor,Option.getgen)(* https://en.wikipedia.org/wiki/Montgomery_curve *)letto_weierstrasst=matchtwith|Infinity->raise(Invalid_argument"Zero")|P(x,y)->letx=Fq.((x/b)+(a/three_b))inlety=Fq.(y/b)inSome(x,y)letto_weierstrass_curve_parameters()=letgen=to_weierstrassoneinifOption.is_nonegenthenNoneelseletnine=Fq.of_string"9"inlettwenty_seven=Fq.of_string"27"inleta_square=Fq.squareParams.ainleta_cube=Fq.mulaa_squareinletb_square=Fq.squareParams.binletb_cube=Fq.mulbb_squareinletd=Fq.(((two*a_cube)+(negatenine*a))/(twenty_seven*b_cube))inleta=Fq.((three+negatea_square)/(three*b_square))inSome(a,d,Params.cofactor,Option.getgen)letfrom_coordinates_exn~x~y=ifis_on_curve~x~y&&is_in_prime_subgroup~x~ythenP(x,y)elseraise(Not_on_curve(Bytes.concatBytes.empty[Fq.to_bytesx;Fq.to_bytesy]))letfrom_coordinates_opt~x~y=ifis_on_curve~x~y&&is_in_prime_subgroup~x~ythenSome(P(x,y))elseNoneletof_compressed_bytes_optbs=(* required to avoid side effect! *)letbs=Bytes.copybsinletlength=Bytes.lengthbsiniflength<>Base.size_in_bytesthenNoneelseifbs=Bytes.makeBase.size_in_bytes'\000'thenSomeInfinityelse(* We get the last bit of the input, representing the bit of u. We also
remove the last bit from the bytes we received
*)letlast_byte=int_of_char@@Bytes.getbs(length-1)inletsign=last_bytelsr7inletlast_byte_without_sign=last_byteland0b01111111inBytes.setbs(length-1)(char_of_intlast_byte_without_sign);(* We compute u *)letx=Base.of_bytes_optbsinmatchxwith|None->None|Somex->(letxx=Base.mulxxinletyy=Base.(((x*xx)+(a*xx)+x)/b)inlety_opt=Base.sqrt_optyyinlety=matchy_optwith|None->None|Somey->(* computed before for constant time *)letnegated_y=Base.negateyinlety_first_byte=Bytes.get(Base.to_bytesy)0inletis_sign_flipped=int_of_chary_first_bytelxorsignland1inSome(ifis_sign_flipped=0thenyelsenegated_y)inmatchywith|Somey->letp=P(x,y)inifis_in_prime_subgroup~x~ythenSomepelseNone|None->None)letof_compressed_bytes_exnb=matchof_compressed_bytes_optbwith|None->raise(Not_on_curveb)|Somep->pletto_compressed_bytesp=matchpwith|Infinity->Bytes.makeBase.size_in_bytes'\000'|P(x,y)->letx_bytes=Base.to_bytesxinlety_bytes=Base.to_bytesyinlety_first_byte=int_of_char(Bytes.gety_bytes0)inletx_last_byte=int_of_char(Bytes.getx_bytes(Base.size_in_bytes-1))in(* Get the first bit of y, i.e. the sign of y *)letsign_of_y=y_first_byteland0b00000001in(* Set the last bit of the last byte of x to the sign of y *)letx_last_byte_with_y=x_last_bytelor(sign_of_ylsl7)inBytes.setx_bytes(Base.size_in_bytes-1)(char_of_intx_last_byte_with_y);x_bytesendmoduleMakeAffineEdwards(Base:Ff_sig.PRIME)(Scalar:Ff_sig.PRIME)(Params:sigvala:Base.tvald:Base.tvalcofactor:Z.tvalbytes_generator:Bytes.tend):Ec_sig.AffineEdwardsTwithtypeBase.t=Base.tandtypeScalar.t=Scalar.t=struct(* https://www.hyperelliptic.org/EFD/g1p/auto-twisted.html *)(* https://en.wikipedia.org/wiki/Twisted_Edwards_curve *)(* Summary for the complexity:
Addition: 2A + 2AC + 5M + 2MC + 2N + 2DI
Doubling: 3A + 1AC + 1M + 1MC + 2N + 2DI + 1DO + 2SQ
*)exceptionNot_on_curveofBytes.tmoduleScalar=ScalarmoduleBase=BaseincludeParamslet()=(* Addition formula is complete if d is a not a square *)assert(Option.is_none(Base.sqrt_optd))letsize_in_bytes=Base.size_in_bytes*2typet={u:Base.t;v:Base.t}letzero={u=Base.zero;v=Base.one}letis_zero{u;v}=Base.(u=zero)&&Base.(v=one)letto_bytes{u;v}=Bytes.concatBytes.empty[Base.to_bytesu;Base.to_bytesv](* Complexity (update above summary if any change):
2A + 2AC + 5M + 2MC + 2N + 2DI
*)letadd{u=u1;v=v1}{u=u2;v=v2}=letu1v2=Base.(u1*v2)inletv1u2=Base.(v1*u2)inletu1u2v1v2=Base.(u1v2*v1u2)inletv1v2=Base.(v1*v2)inletu1u2=Base.(u1*u2)inletdu1u2v1v2=Base.(d*u1u2v1v2)inletu=Base.((u1v2+v1u2)/(Base.one+du1u2v1v2))inletv=Base.((v1v2+Base.negate(a*u1u2))/(Base.one+Base.negatedu1u2v1v2))in{u;v}(* used in doubling *)lettwo=Base.(one+one)(* Complexity (update above summary if any change)
3A + 1AC + 1M + 1MC + 2N + 2DI + 1DO + 2SQ
*)letdouble{u;v}=letuv=Base.(u*v)inletuu=Base.squareuinletvv=Base.squarevinletneg_uu=Base.negateuuinletneg_vv=Base.negatevvinleta_uu=Base.(a*uu)inleta_neguu=Base.(a*neg_uu)in(* a u^2 v^2 = 1 + d u^2 v^2 --> we can skip one multiplication *)letu'=Base.(doubleuv/(a_uu+vv))inletv'=Base.((vv+a_neguu)/(two+a_neguu+neg_vv))in{u=u';v=v'}letnegate{u;v}={u=Base.negateu;v}leteq{u=u1;v=v1}{u=u2;v=v2}=Base.(u1=u2&&v1=v2)letmulxn=letrecauxxn=lettwo_z=Z.succZ.oneinifZ.equalnZ.zerothenzeroelseifZ.equalnZ.onethenxelseletq,r=Z.ediv_remntwo_zinletx_plus_x=doublexinifZ.equalrZ.zerothenauxx_plus_xqelseaddx(auxx_plus_xq)inauxx(Scalar.to_zn)letis_on_curve~u~v=(* a * u^2 + v^2 = 1 + d u^2 v^2 *)letuu=Base.squareuinletvv=Base.squarevinletuuvv=Base.(uu*vv)inBase.((a*uu)+vv=one+(d*uuvv))letis_in_prime_subgroup~u~v=letp={u;v}inifis_zeropthentrueelsenot(is_zero(mulp(Scalar.of_zcofactor)))letof_bytes_optb=(* no need to copy the bytes [p] because [Bytes.sub] is used and [Bytes.sub]
creates a new buffer *)ifBytes.lengthb<>size_in_bytesthenNoneelseletu_opt=Base.of_bytes_opt(Bytes.subb0Base.size_in_bytes)inletv_opt=Base.of_bytes_opt(Bytes.subbBase.size_in_bytesBase.size_in_bytes)inmatch(u_opt,v_opt)with|Someu,Somev->ifis_on_curve~u~v&&is_in_prime_subgroup~u~vthenSome{u;v}elseNone|_->Noneletof_bytes_exnb=(* no need to copy the bytes [p] because [Bytes.sub] is used in
[of_bytes_opt] and [Bytes.sub] creates a new buffer *)matchof_bytes_optbwithNone->raise(Not_on_curveb)|Somep->pletcheck_bytesb=matchof_bytes_optbwithNone->false|Some_->trueletone=of_bytes_exnbytes_generatorletrecrandom?state()=let()=matchstatewithSomes->Random.set_states|None->()inletu=Base.random?state:None()inletuu=Base.(squareu)inletauu=Base.(a*uu)inletduu=Base.(d*uu)inifBase.(is_oneduu)thenrandom?state:None()else(* a u^2 + v^2 = 1 + d u^2 v^2 *)(* <==> a u^2 + v^2 - d u^2 v^2 = 1 *)(* <==> v^2 - d u^2 v^2 = 1 - a u^2 *)(* <==> v^2 * (1 - d u^2) = 1 - a u^2 *)(* <==> v^2 = (1 - a * u^2) / (1 - d * u^2) *)lettmp=Base.((one+negateauu)/(one+negateduu))inletv_sqrt=Base.(sqrt_opttmp)inmatchv_sqrtwith|None->random?state:None()|Somev->letp=mul{u;v}(Scalar.of_zcofactor)inifeqpzerothenrandom?state:None()elsepletget_u_coordinatep=p.uletget_v_coordinatep=p.v(* https://en.wikipedia.org/wiki/Montgomery_curve *)letto_montgomeryp=match(p.u,p.v)with|u,vwhenBase.(is_zerou&&is_onev)->raise(Invalid_argument"Zero")|u,v->assert(notBase.(eqad));ifBase.is_zerou||Base.(is_zero(one+v))thenNoneelseletone_plus_v=Base.(one+v)inletone_minus_v=Base.(one+negatev)inletx=Base.(one_plus_v/one_minus_v)inlety=Base.(x/u)inSome(x,y)letto_montgomery_curve_parameters()=letgen=to_montgomeryoneinifOption.is_nonegenthenNoneelseletgen=Option.getgeninlettwo=Base.of_string"2"inletfour=Base.of_string"4"inleta_min_d=Base.(a+negated)inletb=Base.(four/a_min_d)inleta=Base.(two*(a+d)/a_min_d)inSome(a,b,Params.cofactor,gen)letfrom_coordinates_opt~u~v=letp={u;v}inifis_on_curve~u~v&&is_in_prime_subgroup~u~vthenSomepelseNoneletfrom_coordinates_exn~u~v=matchfrom_coordinates_opt~u~vwith|None->raise(Not_on_curve(Bytes.concatBytes.empty[Base.to_bytesu;Base.to_bytesv]))|Somep->pletunsafe_from_coordinates~u~v={u;v}endletfrom_affine_weierstrass_to_jacobian_weierstrass(typeaffinejacobianbasescalar)(moduleAffine:Ec_sig.AffineWeierstrassTwithtypet=affineandtypeBase.t=baseandtypeScalar.t=scalar)(moduleJacobian:Ec_sig.JacobianWeierstrassTwithtypet=jacobianandtypeBase.t=baseandtypeScalar.t=scalar)(p_affine:affine):jacobian=ifAffine.is_zerop_affinethenJacobian.zeroelseletx=Affine.get_x_coordinatep_affineinlety=Affine.get_y_coordinatep_affineinJacobian.from_affine_coordinates_exn~x~yletfrom_jacobian_weierstrass_to_affine_weierstrass(typeaffinejacobianbasescalar)(moduleJacobian:Ec_sig.JacobianWeierstrassTwithtypet=jacobianandtypeBase.t=baseandtypeScalar.t=scalar)(moduleAffine:Ec_sig.AffineWeierstrassTwithtypet=affineandtypeBase.t=baseandtypeScalar.t=scalar)(p_jacobian:jacobian):affine=ifJacobian.is_zerop_jacobianthenAffine.zeroelseletx=Jacobian.get_x_coordinatep_jacobianinlety=Jacobian.get_y_coordinatep_jacobianinletz=Jacobian.get_z_coordinatep_jacobianinletzz=Jacobian.Base.squarezinletzzz=Jacobian.Base.(z*zz)inletx'=Jacobian.Base.(x/zz)inlety'=Jacobian.Base.(y/zzz)inAffine.from_coordinates_exn~x:x'~y:y'letfrom_affine_weierstrass_to_projective_weierstrass(typeaffineprojectivebasescalar)(moduleAffine:Ec_sig.AffineWeierstrassTwithtypet=affineandtypeBase.t=baseandtypeScalar.t=scalar)(moduleProjective:Ec_sig.ProjectiveWeierstrassTwithtypet=projectiveandtypeBase.t=baseandtypeScalar.t=scalar)(p_affine:affine):projective=ifAffine.is_zerop_affinethenProjective.zeroelseletx=Affine.get_x_coordinatep_affineinlety=Affine.get_y_coordinatep_affineinProjective.from_affine_coordinates_exn~x~yletfrom_projective_weierstrass_to_affine_weierstrass(typeaffineprojectivebasescalar)(moduleProjective:Ec_sig.ProjectiveWeierstrassTwithtypet=projectiveandtypeBase.t=baseandtypeScalar.t=scalar)(moduleAffine:Ec_sig.AffineWeierstrassTwithtypet=affineandtypeBase.t=baseandtypeScalar.t=scalar)(p_projective:projective):affine=ifProjective.is_zerop_projectivethenAffine.zeroelseletx=Projective.get_x_coordinatep_projectiveinlety=Projective.get_y_coordinatep_projectiveinletz=Projective.get_z_coordinatep_projectiveinletx'=Projective.Base.(x/z)inlety'=Projective.Base.(y/z)inAffine.from_coordinates_exn~x:x'~y:y'letfrom_affine_montgomery_to_affine_weierstrass(typeaffine_mtaffine_wtbasescalar)(moduleAffine_mt:Ec_sig.AffineMontgomeryTwithtypet=affine_mtandtypeBase.t=baseandtypeScalar.t=scalar)(moduleAffine_wt:Ec_sig.AffineWeierstrassTwithtypet=affine_wtandtypeBase.t=baseandtypeScalar.t=scalar)(p_mt:affine_mt):affine_wtoption=letcoords_opt=Affine_mt.to_weierstrassp_mtinOption.bindcoords_opt(fun(x,y)->Affine_wt.from_coordinates_opt~x~y)(* let from_weierstrass_montgomery (type affine_wt affine_mt base scalar)
(module Affine_wt : Ec_sig.AffineWeierstrassT
with type t = affine_wt
and type Base.t = base
and type Scalar.t = scalar)
(module Affine_mt : Ec_sig.AffineMontgomeryT
with type t = affine_mt
and type Base.t = base
and type Scalar.t = scalar) (p_wt : affine_wt) : affine_mt option =
let coords_opt = Affine_wt.to_montgomery p_wt in
Option.bind coords_opt (fun (x, y) -> Affine_mt.from_coordinates_opt ~x ~y) *)letfrom_affine_montgomery_to_affine_edwards(typeaffine_mtaffine_twbasescalar)(moduleAffine_mt:Ec_sig.AffineMontgomeryTwithtypet=affine_mtandtypeBase.t=baseandtypeScalar.t=scalar)(moduleAffine_tw:Ec_sig.AffineEdwardsTwithtypet=affine_twandtypeBase.t=baseandtypeScalar.t=scalar)(p_mt:affine_mt):affine_twoption=letcoords_opt=Affine_mt.to_twistedp_mtinOption.bindcoords_opt(fun(u,v)->Affine_tw.from_coordinates_opt~u~v)letfrom_affine_edwards_to_affine_montgomery(typeaffine_twaffine_mtbasescalar)(moduleAffine_tw:Ec_sig.AffineEdwardsTwithtypet=affine_twandtypeBase.t=baseandtypeScalar.t=scalar)(moduleAffine_mt:Ec_sig.AffineMontgomeryTwithtypet=affine_mtandtypeBase.t=baseandtypeScalar.t=scalar)(p_tw:affine_tw):affine_mtoption=letcoords_opt=Affine_tw.to_montgomeryp_twinOption.bindcoords_opt(fun(x,y)->Affine_mt.from_coordinates_opt~x~y)moduleMakeAffineEdwardsToAffineMontgomery(E:Ec_sig.AffineEdwardsT):Ec_sig.AffineMontgomeryTwithmoduleBase=E.BaseandmoduleScalar=E.Scalar=MakeAffineMontgomery(E.Base)(E.Scalar)(structlettwo=E.Base.(doubleone)letfour=E.Base.(doubletwo)leta_neg_d=E.Base.(E.a+negateE.d)leta=E.Base.(two*(E.a+E.d)/a_neg_d)letb=E.Base.(four/a_neg_d)letcofactor=E.cofactorletbytes_generator=letu=E.get_u_coordinateE.oneinletv=E.get_v_coordinateE.oneinletone_plus_v=E.Base.(one+v)inletone_minus_v=E.Base.(one+negatev)inletx=E.Base.(one_plus_v/one_minus_v)inlety=E.Base.(x/u)inBytes.concatBytes.empty[E.Base.to_bytesx;E.Base.to_bytesy]end)