val point_mul_g (a: lbytes 32) : ext_point_c
let point_mul_g (a:lbytes 32) : ext_point_c =
  EL.g_is_on_curve ();
  point_mul a g
val point_mul_g (a: lbytes 32) : ext_point_c
let point_mul_g (a: lbytes 32) : ext_point_c =
EL.g_is_on_curve ();
point_mul a g
Spec.Ed25519.fst.checked
Spec.Hash.Definitions.fst.checked Spec.Exponentiation.fsti.checked Spec.Ed25519.PointOps.fst.checked Spec.Ed25519.Lemmas.fsti.checked Spec.Curve25519.fst.checked Spec.Agile.Hash.fsti.checked prims.fst.checked Lib.Sequence.fsti.checked Lib.IntTypes.fsti.checked Lib.Exponentiation.fsti.checked Lib.ByteSequence.fsti.checked FStar.Seq.fst.checked FStar.Pervasives.Native.fst.checked FStar.Pervasives.fsti.checked FStar.Mul.fst.checked
Spec.Ed25519.fst
Lib.ByteSequence.lbytes Spec.Ed25519.point_mul Spec.Ed25519.PointOps.g Prims.unit Spec.Ed25519.Lemmas.g_is_on_curve Spec.Ed25519.ext_point_c
module Spec.Ed25519

open FStar.Mul
open Lib.IntTypes
open Lib.Sequence
open Lib.ByteSequence

open Spec.Curve25519

module LE = Lib.Exponentiation
module SE = Spec.Exponentiation
module EL = Spec.Ed25519.Lemmas

include Spec.Ed25519.PointOps

#reset-options "--fuel 0 --ifuel 0 --z3rlimit 100"

inline_for_extraction
let size_signature: size_nat = 64

let q: n:nat{n < pow2 256} =
  assert_norm(pow2 252 + 27742317777372353535851937790883648493 < pow2 255 - 19);
  (pow2 252 + 27742317777372353535851937790883648493) // Group order

let max_input_length_sha512 = Some?.v (Spec.Hash.Definitions.max_input_length Spec.Hash.Definitions.SHA2_512)
let _: squash(max_input_length_sha512 > pow2 32 + 64) =
  assert_norm (max_input_length_sha512 > pow2 32 + 64)

let sha512_modq (len:nat{len <= max_input_length_sha512}) (s:bytes{length s = len}) : n:nat{n < pow2 256} =
  nat_from_bytes_le (Spec.Agile.Hash.hash Spec.Hash.Definitions.SHA2_512 s) % q

///  Point Multiplication

let aff_point_c = p:aff_point{is_on_curve p}
let aff_point_add_c (p:aff_point_c) (q:aff_point_c) : aff_point_c =
  EL.aff_point_add_lemma p q;
  aff_point_add p q

let mk_ed25519_comm_monoid: LE.comm_monoid aff_point_c = {
  LE.one = aff_point_at_infinity;
  LE.mul = aff_point_add_c;
  LE.lemma_one = EL.aff_point_at_infinity_lemma;
  LE.lemma_mul_assoc = EL.aff_point_add_assoc_lemma;
  LE.lemma_mul_comm = EL.aff_point_add_comm_lemma;
  }

let ext_point_c = p:ext_point{point_inv p}
let mk_to_ed25519_comm_monoid : SE.to_comm_monoid ext_point_c = {
  SE.a_spec = aff_point_c;
  SE.comm_monoid = mk_ed25519_comm_monoid;
  SE.refl = (fun (x:ext_point_c) -> to_aff_point x);
  }

val point_at_inifinity_c: SE.one_st ext_point_c mk_to_ed25519_comm_monoid
let point_at_inifinity_c _ =
  EL.to_aff_point_at_infinity_lemma ();
  point_at_infinity

val point_add_c: SE.mul_st ext_point_c mk_to_ed25519_comm_monoid
let point_add_c p q =
  EL.to_aff_point_add_lemma p q;
  point_add p q

val point_double_c: SE.sqr_st ext_point_c mk_to_ed25519_comm_monoid
let point_double_c p =
  EL.to_aff_point_double_lemma p;
  point_double p

let mk_ed25519_concrete_ops : SE.concrete_ops ext_point_c = {
  SE.to = mk_to_ed25519_comm_monoid;
  SE.one = point_at_inifinity_c;
  SE.mul = point_add_c;
  SE.sqr = point_double_c;
  }

// [a]P
let point_mul (a:lbytes 32) (p:ext_point_c) : ext_point_c =
  SE.exp_fw mk_ed25519_concrete_ops p 256 (nat_from_bytes_le a) 4

// [a1]P1 + [a2]P2
let point_mul_double (a1:lbytes 32) (p1:ext_point_c) (a2:lbytes 32) (p2:ext_point_c) : ext_point_c =
  SE.exp_double_fw mk_ed25519_concrete_ops p1 256 (nat_from_bytes_le a1) p2 (nat_from_bytes_le a2) 5

// [a]G
val point_mul_g (a: lbytes 32) : ext_point_c
a: Lib.ByteSequence.lbytes 32 -> Spec.Ed25519.ext_point_c