123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284(*****************************************************************************)(* *)(* 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. *)(* *)(*****************************************************************************)(** Specification for Ed25519 is given in RFC 8032
https://www.rfc-editor.org/rfc/rfc8032.txt *)moduleEd25519=structmoduleCurve=Mec.Curve.Curve25519.AffineEdwardsmoduleP:sigtypesk=Bytes.ttypepk=Curve.ttypesignature={r:Curve.t;s:boollist}typemsg=Bytes.tvalpoint_of_compressed_bytes_opt:Bytes.t->Curve.toptionvalpoint_of_compressed_bytes_exn:Bytes.t->Curve.tvalscalar_of_bytes_exn:Bytes.t->boollistvalneuterize:sk->pkvalsign:sk->msg->signaturevalverify:msg->pk->signature->boolend=structmoduleH=Hacl_star.Hacl.SHA2_512typesk=Bytes.ttypepk=Curve.ttypesignature={r:Curve.t;s:boollist}typemsg=Bytes.tletrecover_xysign=lety2=Curve.Base.mulyyinletx2=Curve.Base.((y2+negateone)/((Curve.d*y2)+one))inifCurve.Base.is_zerox2thenifsign=0thenSomex2elseNoneelseletx_opt=Curve.Base.sqrt_optx2inmatchx_optwith|None->None|Somex->letx_sign=Z.(Curve.Base.to_zxmodof_int2)|>Z.to_intinletx=ifx_sign<>signthenCurve.Base.negatexelsexinSomexletpoint_of_compressed_bytes_optbs=letbs=Bytes.copybsinletlen=Bytes.lengthbsiniflen<>32thenNoneelseletlast_byte=int_of_char@@Bytes.getbs(len-1)inletpx_sign=last_bytelsr7inletlast_byte_without_sign=last_byteland0b01111111inBytes.setbs(len-1)(char_of_intlast_byte_without_sign);letyn=Z.of_bits(Bytes.to_stringbs)inifyn>=Curve.Base.orderthenNoneelseletpy=Curve.Base.of_bytes_optbsinmatchpywith|None->None|Somey->(letpx=recover_xypx_signinmatchpxwith|None->None|Somex->(* NOTE: Curve.from_coordinates_opt also checks
if a point is in the subgroup *)Curve.from_coordinates_opt~u:x~v:y)letpoint_of_compressed_bytes_exnbs=matchpoint_of_compressed_bytes_optbswith|None->raise@@Failure(Format.sprintf"point_of_compressed_bytes_exn: cannot recover a point from \
%s"(Hex.show(Hex.of_bytesbs)))|Somep->p(* nat_to_bytes_le 32 (pow2 255 * (x % 2) + y) *)letpoint_to_compressed_bytesp=letpx=Curve.get_u_coordinatep|>Curve.Base.to_zinletpy=Curve.get_v_coordinatep|>Curve.Base.to_zinletpx_sign=Z.(pxmodof_int2)inletres=Z.(((onelsl255)*px_sign)+py)inBytes.of_string@@Z.to_bitsresletscalar_of_curve_scalars=Curve.Scalar.to_zs|>Utils.bool_list_of_z~nb_bits:(Z.numbitsCurve.Scalar.order)letscalar_of_bytes_exns=letsn=Z.of_bits(Bytes.to_strings)inifsn<Curve.Scalar.orderthenCurve.Scalar.of_bytes_exns|>scalar_of_curve_scalarelseraise@@Failure(Format.sprintf"scalar_of_bytes_exn: scalar is not less than the order %s"(Hex.show(Hex.of_bytess)))(* Compute the expanded keys for the EdDSA signature *)letexpand_keyssk=assert(Bytes.lengthsk=32);(* h <- (h_0, h_1, ..., h_{2b-1}) <- H (sk) *)leth=H.hashskinletb=Bytes.lengthh/2inleth_low=Bytes.subh0binleth_high=Bytes.subhbbin(* s <- 2^n + \sum_i h_i * 2^i for c <= i < n,
where Curve.cofactor = 2^c and c <= n < b.
For Ed25519, c = 3 and n = 254 *)lets=Bytes.set_uint8h_low0(Int.logand(Bytes.get_uint8h_low0)248);Bytes.set_uint8h_low31(Int.logor(Int.logand(Bytes.get_uint8h_low31)127)64);Curve.Scalar.of_bytes_exnh_lowin(* pk <- [s]G *)letpk=Curve.mulCurve.onesin(s,pk,h_high)letneuterizesk=let_s,pk,_prefix=expand_keysskinpk(* h <- H (compressed (R) || compressed (pk) || msg ) mod Curve.Scalar.order *)letcompute_hmsgpkr=letr=point_to_compressed_bytesrinletpk=point_to_compressed_bytespkinH.hash(Bytes.concatBytes.empty[r;pk;msg])|>Curve.Scalar.of_bytes_exnletsignskmsg=lets,pk,prefix=expand_keysskin(* r <- H (prefix || msg) *)letr=H.hash(Bytes.catprefixmsg)|>Curve.Scalar.of_bytes_exnin(* R <- [r]G *)letsig_r=Curve.mulCurve.onerin(* h <- H (compressed (R) || compressed (pk) || msg ) *)leth=compute_hmsgpksig_rin(* s <- (r + h * s) mod Curve.Scalar.order *)letsig_s=Curve.Scalar.(r+(h*s))|>scalar_of_curve_scalarin{r=sig_r;s=sig_s}(* the fact that pk & r are on curve is enforced by the type invariant of Curve.t *)letverifymsgpksignature=(* h <- H (compressed (R) || compressed (pk) || msg ) *)leth=compute_hmsgpksignature.rinletsig_s=Utils.bool_list_to_zsignature.sinifsig_s<Curve.Scalar.orderthen(* [s]G =?= R + [h]pk *)Curve.(eq(mulCurve.one(Curve.Scalar.of_zsig_s))(addsignature.r(mulpkh)))elsefalseendopenLang_coreopenLang_stdlibmoduleV:functor(L:LIB)->sigopenLopenGadget_edwards25519.MakeEdwards25519(L)typepk=pointtypesignature={r:pointrepr;s:boollistrepr}moduleEncoding:sigopenL.Encodingsvalpk_encoding:(Curve.t,pkrepr,pk)encodingvalsignature_encoding:(P.signature,signature,pk*boollist)encodingendvalverify:Bytes.tlrepr->pkrepr->signature->boolreprtend=functor(L:LIB)->structopenLincludeGadget_edwards25519.MakeEdwards25519(L)moduleH=Gadget_sha2.SHA512(L)typepk=pointtypesignature={r:pointrepr;s:boollistrepr}moduleEncoding=structopenL.Encodingsletpk_encoding=point_encodingletsignature_encoding=conv(fun{r;s}->(r,s))(fun(r,s)->{r;s})(fun({r;s}:P.signature)->(r,s))(fun(r,s)->{r;s})(obj2_encodingpoint_encoding(atomic_list_encodingbool_encoding))end(* h <- H (compressed (R) || compressed (pk) || msg ) *)letcompute_hmsgpkr=(* Ed25519 works with little-endian representation
but SHA-512 with big-endian one *)letbytes_change_endiannessb=ret@@to_list(Utils.bool_list_change_endianness(of_listb))inwith_label~label:"Ed25519.compute_h"@@let*r_bytes=to_compressed_bytesrinlet*pk_bytes=to_compressed_bytespkinlet*r_pk_msg=bytes_change_endianness@@Bytes.concat[|msg;pk_bytes;r_bytes|]inlet*h=H.digestr_pk_msginbytes_change_endiannessh(* the fact that pk & r are on curve is enforced by point encodings *)letverifymsgpksignature=let{r;s}=signaturein(* range_check checks if x is in [0; 2^n), n = 253 *)(* s <= Curve.Scalar.order - 1 <==> 0 <= Curve.Scalar.order - 1 - s *)let*sb=Num.scalar_of_bytessinlet*order_minus_s=Num.add~ql:S.(negateone)~qr:S.zero~qc:S.(of_zCurve.Scalar.order+negateone)sbsbinNum.range_check~nb_bits:253order_minus_s>*with_label~label:"Ed25519.verify"@@(* h <- H (compressed (R) || compressed (pk) || msg ) *)let*h=compute_hmsgpkrin(* NOTE: we do not reduce a result of compute_h modulo Curve.Scalar.order *)with_label~label:"Ed25519.scalar_mul"(* we can use multi_scalar_mul once h is reduced:
[s]G =?= R + [h]pk <==> R =?= [s]G - [h]pk *)@@let*base_pointinlet*sg=scalar_mulsbase_pointinlet*hpk=scalar_mulhpkinlet*rhpk=addrhpkinwith_label~label:"Ed25519.check"@@equalsgrhpkendend