diff --git a/src/lib_crypto/base58.ml b/src/lib_crypto/base58.ml index ac4f2d8fe..a9087c02d 100644 --- a/src/lib_crypto/base58.ml +++ b/src/lib_crypto/base58.ml @@ -344,6 +344,8 @@ module Prefix = struct (* 33 *) let secp256k1_public_key = "\003\254\226\086" (* sppk(55) *) let p256_public_key = "\003\178\139\127" (* p2pk(55) *) + let secp256k1_scalar = "\038\248\136" (* SSp(53) *) + let secp256k1_element = "\005\092\000" (* GSp(54) *) (* 64 *) let ed25519_secret_key = "\043\246\078\007" (* edsk(98) *) diff --git a/src/lib_crypto/base58.mli b/src/lib_crypto/base58.mli index 685db8e56..6de3fe615 100644 --- a/src/lib_crypto/base58.mli +++ b/src/lib_crypto/base58.mli @@ -53,6 +53,8 @@ module Prefix : sig val generic_signature: string val chain_id: string + val secp256k1_element: string + val secp256k1_scalar: string end diff --git a/src/lib_crypto/pvss.ml b/src/lib_crypto/pvss.ml new file mode 100644 index 000000000..44e5d4567 --- /dev/null +++ b/src/lib_crypto/pvss.ml @@ -0,0 +1,321 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +module H = Blake2B + +(** Polynomial ring (ℤ/qℤ)[X] *) +module PZ_q (Z_q : Znz.ZN) : sig + type t + module Z_q : Znz.ZN + + (** Evaluates the polynomial p at point x *) + val eval : p:t -> x:Z_q.t -> Z_q.t + + (** Builds the polynomial from a list of coefficient, ordered by power. + That is, of_list [a₀; a₁; a₂; …] = a₀ + a₁ x + a₂ x² + … *) + val of_list : Z_q.t list -> t + +end with type Z_q.t = Z_q.t = struct + module Z_q = Z_q + type t = Z_q.t list + let eval ~p ~x = List.fold_right (fun c y -> Z_q.(y * x + c)) p Z_q.zero + let of_list l = l +end + +(** Functor type for an Cyclic group *) +module type CYCLIC_GROUP = sig + type t + include S.B58_DATA with type t := t + include S.ENCODER with type t := t + val name : string + module Z_m : Znz.ZN + val e : t + val g1 : t + val g2 : t + val ( * ) : t -> t -> t + val (=) : t -> t -> bool + val pow : t -> Z_m.t -> t + val to_bits : t -> String.t + val of_bits : String.t -> t option +end + +(** Type of a module that handles proofs for the discrete logarithm + equality equation. *) +module type DLEQ = sig + + (** A DLEQ equation. *) + type equation + + (** A non-interactive zero-knowledge proof-of-knowledge of an + exponent solving the equation. *) + type proof + + val proof_encoding : proof Data_encoding.t + + + (** Group element. *) + type element + + (** Exponent, i.e. an integer modulo the group's order. *) + type exponent + + + (** Sets up a equation of the form + ∀ i, ∃ x(i), b₁ˣ⁽ⁱ⁾ = h₁ᵢ and b₂ᵢˣ⁽ⁱ⁾ = h₂ᵢ. The arguments + are given as b₁, h₁ᵢ, b₂ᵢ, h₂ᵢ *) + val setup_equation : + element -> element list -> element list -> element list -> equation + + (** Creates a zero-knowledge proof of knowledge of the exponent list *) + val make_proof : equation -> exponent list -> proof + + (** Checkes the proof created by make_proof for a given equation *) + val check_proof : equation -> proof -> bool +end + +(** Functor for creating a module handling proofs for the discrete logarithm + equality in cyclic group G *) +module MakeDleq (G : CYCLIC_GROUP) : + DLEQ with type element = G.t and type exponent = G.Z_m.t = +struct + + type element = G.t + type exponent = G.Z_m.t + type equation = element * (element list) * (element list) * (element list) + type proof = exponent * (exponent list) + + let proof_encoding = Data_encoding.( + tup2 G.Z_m.encoding (list G.Z_m.encoding)) + + (* Fiat-Shamir heuristic to derive a random element of ℤ/mℤ from the + hash of a list of group elements *) + let fiat_shamir ?(exponents=[]) elements = + String.concat "||" ( + "tezosftw" :: (List.map G.to_bits elements) @ (List.map G.Z_m.to_bits exponents) + ) |> (fun x -> H.hash_string [x]) |> H.to_string |> G.Z_m.of_bits_exn + + let setup_equation b1 h1_n b2_n h2_n = (b1, h1_n, b2_n, h2_n) + let make_proof (b1, h1_n, b2_n, h2_n) x_n = + (* First, draw blinding factors. Normally these should be picked randomly. To maximize + reproducibility and avoid weak random number generation, we generate the blinding + factor deterministically from the problem parameters and the secret x_n. + TODO: review with cryptographer + *) + let + pseudo_seed = fiat_shamir (b1::(List.concat [h1_n; b2_n; h2_n])) ~exponents:x_n in + let + w_n = List.mapi (fun i __ -> fiat_shamir [] ~exponents:[pseudo_seed; G.Z_m.of_int i]) h1_n in let + a1_n = List.map (G.pow b1) w_n and + a2_n = List.map2 G.pow b2_n w_n in let + (* Pick the challenge, c, following the Fiat-Shamir heuristic. *) + c = fiat_shamir (List.concat [h1_n; h2_n; a1_n; a2_n]) in let + (* rᵢ = wᵢ - c * xᵢ *) + r_n = List.map2 (fun w x -> G.Z_m.(w - c * x)) w_n x_n in + (c, r_n) + + let check_proof (b1, h1_n, b2_n, h2_n) (c, r_n) = + (* First check that the lists have the same sizes. *) + let same_sizes = List.( + Compare.Int.((length h1_n) = (length b2_n) && (length b2_n) = (length h2_n) && + (length h2_n) = (length r_n))) in + + if not same_sizes then false + else + let + a1_n = List.map2 G.( * ) + (List.map (G.pow b1) r_n) + (List.map (fun h1 -> G.pow h1 c) h1_n) + and + a2_n = List.map2 G.( * ) + (List.map2 G.pow b2_n r_n) + (List.map (fun h2 -> G.pow h2 c) h2_n) + in + G.Z_m.(c = fiat_shamir (List.concat [h1_n; h2_n; a1_n; a2_n])) +end + +module type PVSS = sig + + module type ENCODED = sig + type t + include S.B58_DATA with type t := t + include S.ENCODER with type t := t + end + + module Commitment : ENCODED + module Encrypted_share : ENCODED + module Clear_share : ENCODED + + module Public_key : ENCODED + module Secret_key : sig + include ENCODED + val to_public_key : t -> Public_key.t + end + + type proof + + val proof_encoding : proof Data_encoding.t + + val dealer_shares_and_proof: + secret:Secret_key.t -> t:int -> public_keys:Public_key.t list -> + (Encrypted_share.t list * Commitment.t list * proof) + + val check_dealer_proof: + Encrypted_share.t list -> Commitment.t list -> proof:proof -> + public_keys:Public_key.t list -> bool + + val reveal_share : Encrypted_share.t -> secret_key:Secret_key.t + -> public_key:Public_key.t -> Clear_share.t * proof + + val check_revealed_share: + Encrypted_share.t -> Clear_share.t -> public_key:Public_key.t -> proof + -> bool + val reconstruct: Clear_share.t list -> int list -> Public_key.t +end + +module MakePvss (G : CYCLIC_GROUP) : PVSS = struct + + module type ENCODED = sig + type t + include S.B58_DATA with type t := t + include S.ENCODER with type t := t + end + + (* Module to make discrete logarithm equality proofs *) + module Dleq = MakeDleq (G) + type proof = Dleq.proof + + (* Polynomials over ℤ/mℤ *) + module PZ_m = PZ_q (G.Z_m) + + (* A public key is a group element *) + module Public_key = G + + module Secret_key = struct + include G.Z_m + let to_public_key x = G.(pow g2 x) + end + + module Encrypted_share = G + module Clear_share = G + module Commitment = G + + let proof_encoding = Dleq.proof_encoding + + (* generate a "random": polynomial of degree t to hide secret `secret` *) + let random_polynomial secret t = + (* the t-1 coefficients are computed deterministically from + the secret and mapped to G.Z_m *) + + let nonce = [String.concat "||" [G.Z_m.to_bits secret]] + |> H.hash_string |> H.to_string in + + (* TODO: guard against buffer overlow *) + let rec make_coefs = function + | 0 -> [] + | k -> let h = + ( H.hash_string [string_of_int k; "||"; nonce]) + |> H.to_string |> G.Z_m.of_bits_exn in + h :: make_coefs (k-1) in + let coefs = secret :: (make_coefs (t-1)) in + + (* let coefs = secret :: List_Utils.list_init ~f:G.Z_m.random ~n:(t-1) in *) + let poly = PZ_m.of_list coefs + in (coefs, poly) + + (* Hides secret s in a random polynomial of degree t, publishes t commitments + to the polynomial coefficients and n encrypted shares for the holders of + the public keys *) + let dealer_shares_and_proof ~secret ~t ~public_keys = + let coefs, poly = random_polynomial secret t in + let + (* Cⱼ represents the commitment to the coefficients of the polynomial + Cⱼ = g₁^(aⱼ) for j in 0 to t-1 *) + + cC_j = List.map G.(pow g1) coefs and + + (* pᵢ = p(i) for i in 1…n, with i ∈ ℤ/mℤ: points of the polynomial. *) + p_i = List.mapi (fun i _ -> + PZ_m.eval ~p:poly ~x:(i+1 |> G.Z_m.of_int)) public_keys in let + + (* yᵢ = pkᵢᵖ⁽ⁱ⁾ for i ∈ 1…n: the value of p(i) encrypted with pkᵢ, + the public key of the party receiving the iᵗʰ party. The public + keys use the g₂ generator of G. Thus pkᵢ = g₂ˢᵏⁱ *) + y_i = List.map2 G.pow public_keys p_i and + + (* xᵢ = g₁ᵖ⁽ⁱ⁾ for in in 1…n: commitment to polynomial points *) + x_i = List.map G.(pow g1) p_i in let + + equation = Dleq.setup_equation G.g1 x_i public_keys y_i in let + proof = Dleq.make_proof equation p_i + in (y_i, cC_j, proof) + + let check_dealer_proof y_i cC_j ~proof ~public_keys = + + (* Reconstruct Xᵢ from Cⱼ *) + let x_i = + (* prod_C_j_to_the__i_to_the_j = i ↦ Πⱼ₌₀ᵗ⁻¹ Cⱼ^(iʲ) *) + let prod_C_j_to_the__i_to_the_j i = + List.mapi (fun j cC ->G.pow cC (G.Z_m.pow i (Z.of_int j))) + cC_j |> (List.fold_left G.( * ) G.e) + in + List.mapi (fun i _ -> + prod_C_j_to_the__i_to_the_j (i+1 |> G.Z_m.of_int)) y_i + in let + equation = Dleq.setup_equation G.g1 x_i public_keys y_i in + Dleq.check_proof equation proof + + (* reveal a share *) + let reveal_share y ~secret_key ~public_key = + match G.Z_m.inv secret_key with + | None -> failwith "Invalid secret key" + | Some inverse_key -> + let reveal = G.(pow y inverse_key) in + (* y = g₂^(private_key) and public_key = reveal^(private_key) *) + let equation = Dleq.setup_equation G.g2 [public_key] [reveal] [y] in + let proof = Dleq.make_proof equation [secret_key] in + (reveal, proof) + + (* check the validity of a revealed share *) + let check_revealed_share share reveal ~public_key proof = + let equation = Dleq.setup_equation G.g2 [public_key] [reveal] [share] in + Dleq.check_proof equation proof + + (* reconstruct the secret *) + let reconstruct reveals int_indices = + (* check that there enough reveals *) + let indices = List.map (fun x -> G.Z_m.of_int (1+x)) int_indices in + let lagrange i = + List.fold_left G.Z_m.( * ) G.Z_m.one ( + List.map ( + fun j -> + if G.Z_m.(j = i) then G.Z_m.one else + match G.Z_m.(inv (j - i)) with + | None -> failwith "Unexpected error inverting scalar." + | Some inverse -> G.Z_m.(j * inverse) + ) indices) + in let lagrange = List.map lagrange indices in + List.fold_left G.( * ) G.e (List.map2 G.pow reveals lagrange) + +end diff --git a/src/lib_crypto/pvss.mli b/src/lib_crypto/pvss.mli new file mode 100644 index 000000000..7e98eccf9 --- /dev/null +++ b/src/lib_crypto/pvss.mli @@ -0,0 +1,111 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(** PVSS protocol, following + + @see Schoenmakers, B., 1999: + A simple publicly verifiable secret sharing scheme + and its application to electronic voting. Lecture Notes in Computer Science, + pp.148-164. + @see https://www.win.tue.nl/~berry/papers/crypto99.pdf + + The protocol is expressed as a functor parametrized by a cyclic group + of prime order. Algebraic properties are enforced at the type level, + whenever reasonably possible. + +*) + +module type CYCLIC_GROUP = sig + + type t + include S.B58_DATA with type t := t + include S.ENCODER with type t := t + + val name : string + module Z_m : Znz.ZN + val e : t + val g1 : t + val g2 : t + val ( * ) : t -> t -> t + val (=) : t -> t -> bool + val pow : t -> Z_m.t -> t + + (** Binary representation *) + val to_bits : t -> String.t + val of_bits : String.t -> t option + +end + +(** PVSS construction, based on a cyclic group G of prime order *) +module type PVSS = sig + + module type ENCODED = sig + type t + include S.B58_DATA with type t := t + include S.ENCODER with type t := t + end + + module Commitment : ENCODED + module Encrypted_share : ENCODED + module Clear_share : ENCODED + + module Public_key : ENCODED + module Secret_key : sig + include ENCODED + val to_public_key : t -> Public_key.t + end + + type proof + val proof_encoding : proof Data_encoding.t + + val dealer_shares_and_proof: + secret:Secret_key.t -> t:int -> public_keys:Public_key.t list -> + (Encrypted_share.t list * Commitment.t list * proof) + (** Lets a dealer share a secret with a set of participant by breaking it into + pieces, encrypting it with the participant's public keys, and publishing + these encrypted shares. Any t participants can reconstruct the secret. A + zero-knowledge proof is produced showing that the dealer correctly + followed the protocol, making the protocol publicly verifiable. *) + + val check_dealer_proof: + Encrypted_share.t list -> Commitment.t list -> proof:proof -> + public_keys:Public_key.t list -> bool + (** Checks the proof produced by the dealer, given the encrypted shares, + the commitment list, the proof, and the participant's public keys. *) + + val reveal_share : Encrypted_share.t -> secret_key:Secret_key.t + -> public_key:Public_key.t -> Clear_share.t * proof + (** Lets a participant provably decrypt an encrypted share. *) + + val check_revealed_share: + Encrypted_share.t -> Clear_share.t -> public_key:Public_key.t -> proof + -> bool + (** Checks that the participant honestly decrypted its share. *) + + val reconstruct: Clear_share.t list -> int list -> Public_key.t + +end + +module MakePvss : functor (G: CYCLIC_GROUP) -> PVSS diff --git a/src/lib_crypto/pvss_secp256k1.ml b/src/lib_crypto/pvss_secp256k1.ml new file mode 100644 index 000000000..5aa4d4c84 --- /dev/null +++ b/src/lib_crypto/pvss_secp256k1.ml @@ -0,0 +1,62 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +open Secp256k1_group + +module G : Pvss.CYCLIC_GROUP = struct + + module Z_m = struct + include Group.Scalar + let n = Group.order + let ( + ) = Group.Scalar.add + let ( * ) = Group.Scalar.mul + let ( - ) = Group.Scalar.sub + let ( = ) = Group.Scalar.equal + let inv = Group.Scalar.inverse + end + + include Group + let name = "secp256k1" + + (* This pvss algorithm assumes the public keys of the participants receiving + shares are based on g2, so we set g2 to Group.g to match regular Secp256k1 + public keys. + *) + let g1 = Group.h + let g2 = Group.g + + (* We use a multiplicative notation in the pvss module, but + secp256k1 usually uses an additive notation. *) + let ( * ) = Group.(( + )) + let pow x n = Group.mul n x + + let of_bits b = + try + Some (Group.of_bits_exn b) + with _ -> None + +end + +include Pvss.MakePvss (G) diff --git a/src/lib_crypto/pvss_secp256k1.mli b/src/lib_crypto/pvss_secp256k1.mli new file mode 100644 index 000000000..5006f2291 --- /dev/null +++ b/src/lib_crypto/pvss_secp256k1.mli @@ -0,0 +1,26 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +include Pvss.PVSS diff --git a/src/lib_crypto/s.ml b/src/lib_crypto/s.ml index b288b69fd..a1fa11be6 100644 --- a/src/lib_crypto/s.ml +++ b/src/lib_crypto/s.ml @@ -107,6 +107,22 @@ module type ENCODER = sig end +module type PVSS = sig + + type proof + + module Clear_share : sig type t end + module Commitment : sig type t end + module Encrypted_share : sig type t end + + module Public_key : sig + type t + include B58_DATA with type t := t + include ENCODER with type t := t + end + +end + module type INDEXES = sig type t diff --git a/src/lib_crypto/secp256k1_group.ml b/src/lib_crypto/secp256k1_group.ml new file mode 100644 index 000000000..cf54b37cd --- /dev/null +++ b/src/lib_crypto/secp256k1_group.ml @@ -0,0 +1,280 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +module Sp = Libsecp256k1.Internal + +module type SCALAR_SIG = sig + type t + include S.B58_DATA with type t := t + include S.ENCODER with type t := t + val zero : t + val one : t + val of_Z : Z.t -> t + val to_Z : t -> Z.t + val of_int : int -> t + val add: t -> t -> t + val mul: t -> t -> t + val negate: t -> t + val sub : t -> t -> t + val of_bits_exn: string -> t + val to_bits: t -> string + val inverse: t -> t option + val pow: t -> Z.t -> t + val equal : t -> t -> bool +end + +module Group : sig + val order: Z.t + module Scalar : SCALAR_SIG + type t + include S.B58_DATA with type t := t + include S.ENCODER with type t := t + val e: t + val g: t + val h: t + val of_coordinates: x:Z.t -> y:Z.t -> t + val of_bits_exn: string -> t + val to_bits: t -> string + val mul: Scalar.t -> t -> t + val (+): t -> t -> t + val (-): t -> t -> t + val (=): t -> t -> bool +end = struct + + let order = Z.of_string_base 16 "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141" + + let string_rev s = + let len = String.length s in + String.init len (fun i -> s.[len - 1 - i]) + + let b32_of_Z z = + let cs = Cstruct.create 32 in + let bits = Z.to_bits z in + let length = (min 32 (String.length bits)) in + let bits = String.sub bits 0 length in + let bits = string_rev bits in + Cstruct.blit_from_string bits 0 cs (32 - length) length; + cs + + let z_of_b32 b = + b |> Cstruct.to_string |> string_rev |> Z.of_bits + + module Scalar : SCALAR_SIG with type t = Sp.Scalar.t = struct + type t = Sp.Scalar.t + + let zero = Sp.Scalar.zero () + let one = Sp.Scalar.one () + let equal x y = Sp.Scalar.equal x y + + + let of_Z z = + let z = Z.erem z order in + let r = Sp.Scalar.const () in + let cs = b32_of_Z z in + let _ = Sp.Scalar.set_b32 r cs in r + + let to_Z s = + let cs = Cstruct.create 32 in + Sp.Scalar.get_b32 cs s; cs |> z_of_b32 + + let of_int i = i |> Z.of_int |> of_Z + + let pow t n = + Z.powm (to_Z t) n order |> of_Z + + let add x y = + let r = Sp.Scalar.const () in + let _ = Sp.Scalar.add r x y in r + + let mul x y = + let r = Sp.Scalar.const () in + Sp.Scalar.mul r x y; r + + let negate x = + let r = Sp.Scalar.const () in + Sp.Scalar.negate r x; r + + let sub x y = + add x (negate y) + + let of_bits_exn bits = + let r = Sp.Scalar.const () in + (* trim to 32 bytes *) + let cs = Cstruct.create 32 in + Cstruct.blit_from_string bits 0 cs 0 (min (String.length bits) 32); + (* ignore overflow condition, it's always 0 based on the c-code *) + let _ = Sp.Scalar.set_b32 r cs in r + + (* TODO, check that we are less than the order *) + + let to_bits x = + let c = Cstruct.create 32 in + Sp.Scalar.get_b32 c x; Cstruct.to_string c + + let inverse x = + if x = zero then + None else + let r = Sp.Scalar.const () in + Sp.Scalar.inverse r x; Some r + + type Base58.data += + | Data of t + + let b58check_encoding = + Base58.register_encoding + ~prefix: Base58.Prefix.secp256k1_scalar + ~length: 32 + ~to_raw: to_bits + ~of_raw: (fun s -> try Some (of_bits_exn s) with _ -> None) + ~wrap: (fun x -> Data x) + + let title = "Secp256k1_group.Scalar" + let name = "Anscalar for the secp256k1 group" + + include Helpers.MakeB58(struct + type nonrec t = t + let title = title + let name = name + let b58check_encoding = b58check_encoding + end) + + include Helpers.MakeEncoder(struct + type nonrec t = t + let name = name + let title = title + let raw_encoding = Data_encoding.(conv to_bits of_bits_exn string) + let to_b58check = to_b58check + let to_short_b58check = to_short_b58check + let of_b58check = of_b58check + let of_b58check_opt = of_b58check_opt + let of_b58check_exn = of_b58check_exn + end) + end + + type t = Sp.Group.Jacobian.t + (* type ge = Sp.Group.ge *) + + let field_of_Z z = + let fe = Sp.Field.const () in + let cs = b32_of_Z z in + let _ = Sp.Field.set_b32 fe cs in fe + + let group_of_jacobian j = + let r = Sp.Group.of_fields () in + Sp.Group.Jacobian.get_ge r j; r + + let jacobian_of_group g = + let j = Sp.Group.Jacobian.of_fields () in + Sp.Group.Jacobian.set_ge j g; j + + + let of_coordinates ~x ~y = + Sp.Group.of_fields + ~x:(field_of_Z x) ~y:(field_of_Z y) () |> jacobian_of_group + + let e = + Sp.Group.Jacobian.of_fields ~infinity:true () + + let g = + let gx = Z.of_string "55066263022277343669578718895168534326250603453777594175500187360389116729240" + and gy = Z.of_string "32670510020758816978083085130507043184471273380659243275938904335757337482424" in + of_coordinates ~x:gx ~y:gy + + (* To obtain the second generator, take the sha256 hash of the decimal representation of g1_y + python -c "import hashlib;print int(hashlib.sha256('32670510020758816978083085130507043184471273380659243275938904335757337482424').hexdigest(),16)" + *) + let h = + let hx = Z.of_string "54850469061264194188802857211425616972714231399857248865148107587305936171824" + and hy = Z.of_string "6558914719042992724977242403721980463337660510165027616783569279181206179101" in + of_coordinates ~x:hx ~y:hy + + let (+) x y = + let r = Sp.Group.Jacobian.of_fields () in + Sp.Group.Jacobian.add_var r x y; r + + let (-) x y = + let neg_y = Sp.Group.Jacobian.of_fields () in + Sp.Group.Jacobian.neg neg_y y; x + neg_y + + let (=) x y = Sp.Group.Jacobian.is_infinity (x - y) + + let mul s g = + let r = Sp.Group.Jacobian.of_fields () in + Sp.Group.Jacobian.mul r (group_of_jacobian g) s; r + + let to_bits j = + let x = group_of_jacobian j + and buf = Cstruct.create 33 in + let cs = (Sp.Group.to_pubkey ~compress:true buf x) in + Cstruct.to_string cs + + let of_bits_exn bits = + let buf = Cstruct.of_string bits + and x = Sp.Group.of_fields () in + Sp.Group.from_pubkey x buf; + x |> jacobian_of_group + + + module Encoding = struct + type Base58.data += + | Data of t + + let title = "Secp256k1_group.Group" + let name = "An element of secp256k1" + + let b58check_encoding = + Base58.register_encoding + ~prefix: Base58.Prefix.secp256k1_element + ~length: 33 + ~to_raw: to_bits + ~of_raw: (fun s -> try Some (of_bits_exn s) with _ -> None) + ~wrap: (fun x -> Data x) + + include Helpers.MakeB58( + struct + type nonrec t = t + let title = title + let name = name + let b58check_encoding = b58check_encoding + end) + + include Helpers.MakeEncoder( + struct + type nonrec t = t + let name = name + let title = title + let raw_encoding = Data_encoding.(conv to_bits of_bits_exn string) + let to_b58check = to_b58check + let to_short_b58check = to_short_b58check + let of_b58check = of_b58check + let of_b58check_opt = of_b58check_opt + let of_b58check_exn = of_b58check_exn + end + ) + end + + include Encoding + +end diff --git a/src/lib_crypto/secp256k1_group.mli b/src/lib_crypto/secp256k1_group.mli new file mode 100644 index 000000000..aa77d7da2 --- /dev/null +++ b/src/lib_crypto/secp256k1_group.mli @@ -0,0 +1,72 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(** Type for the group of integers modulo the order of the curve ℤ/pℤ *) +module type SCALAR_SIG = sig + + (** Element of the scalar group *) + type t + include S.B58_DATA with type t := t + include S.ENCODER with type t := t + + val zero : t + val one : t + val of_Z : Z.t -> t + val to_Z : t -> Z.t + val of_int : int -> t + val add: t -> t -> t + val mul: t -> t -> t + val negate: t -> t + val sub: t -> t -> t + val of_bits_exn: string -> t + val to_bits: t -> string + val inverse: t -> t option + + (** Modular exponentiation*) + val pow: t -> Z.t -> t + val equal: t -> t -> bool +end + +module Group : sig + + type t + include S.B58_DATA with type t := t + include S.ENCODER with type t := t + + val order: Z.t + module Scalar : SCALAR_SIG + val e: t + val g : t + val h : t + val of_coordinates: x:Z.t -> y:Z.t -> t + val of_bits_exn: string -> t + val to_bits: t -> string + + val mul: Scalar.t -> t -> t + val (+): t -> t -> t + val (-): t -> t -> t + val (=): t -> t -> bool + +end diff --git a/src/lib_crypto/test/dune b/src/lib_crypto/test/dune index f3d0d3858..de622e41e 100644 --- a/src/lib_crypto/test/dune +++ b/src/lib_crypto/test/dune @@ -2,21 +2,25 @@ (names test_merkle test_base58 test_ed25519 - test_blake2b) + test_blake2b + test_pvss) (libraries tezos-stdlib tezos-crypto + tezos-data-encoding alcotest) (flags (:standard -w -9-32 -safe-string -open Tezos_stdlib - -open Tezos_crypto))) + -open Tezos_crypto + -open Tezos_data_encoding))) (alias (name buildtest) (deps test_merkle.exe test_base58.exe test_ed25519.exe - test_blake2b.exe)) + test_blake2b.exe + test_pvss.exe)) (alias (name runtest_merkle) @@ -34,12 +38,17 @@ (name runtest_blake2b) (action (run %{exe:test_blake2b.exe}))) +(alias + (name runtest_pvss) + (action (run %{exe:test_pvss.exe}))) + (alias (name runtest) (deps (alias runtest_merkle) (alias runtest_base58) (alias runtest_ed25519) - (alias runtest_blake2b))) + (alias runtest_blake2b) + (alias runtest_pvss))) (alias (name runtest_indent) diff --git a/src/lib_crypto/test/test_pvss.ml b/src/lib_crypto/test/test_pvss.ml new file mode 100644 index 000000000..724351443 --- /dev/null +++ b/src/lib_crypto/test/test_pvss.ml @@ -0,0 +1,168 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(* pvss tests here *) + +module Pvss = Pvss_secp256k1 +module Sp = Secp256k1_group + +module Setup : sig + + val shares : Pvss.Encrypted_share.t list + val commitments: Pvss.Commitment.t list + val proof: Pvss.proof + + val secret_scalar : Sp.Group.Scalar.t + val secret: Pvss.Secret_key.t + val public_secret: Pvss.Public_key.t + + val other_shares : Pvss.Encrypted_share.t list + val other_commitments: Pvss.Commitment.t list + val other_proof: Pvss.proof + val other_secret: Pvss.Secret_key.t + + type keypair = {secret_key: Pvss.Secret_key.t; public_key: Pvss.Public_key.t} + val public_keys : Pvss.Public_key.t list + val keypairs : keypair list + val reveals : (Pvss.Encrypted_share.t * (Pvss.Clear_share.t * Pvss.proof)) list + + val convert_encoding : 'a Data_encoding.t -> 'b Data_encoding.t -> 'a -> 'b + val group_encoding : Sp.Group.t Data_encoding.t + +end = struct + + type keypair = {secret_key: Pvss.Secret_key.t; public_key: Pvss.Public_key.t} + + let group_encoding = Data_encoding.(conv Sp.Group.to_bits Sp.Group.of_bits_exn string) + let scalar_encoding = Data_encoding.(conv Sp.Group.Scalar.to_bits Sp.Group.Scalar.of_bits_exn string) + + let convert_encoding de1 de2 x = + Data_encoding.Binary.of_bytes_exn de2 + (Data_encoding.Binary.to_bytes_exn de1 x) + + + (** Random value of Z in the range [0,2^256] *) + let rand_Z () = + [Random.int64 Int64.max_int |> Z.of_int64 |> Z.to_bits] + |> Blake2B.hash_string |> Blake2B.to_string |> Z.of_bits + + (** Generates n random keypairs *) + let random_keypairs n = + List.init n + (fun _ -> let s = Sp.Group.Scalar.of_Z (rand_Z ()) in + let secret_key = convert_encoding scalar_encoding Pvss.Secret_key.encoding s in + { secret_key ; public_key = Pvss.Secret_key.to_public_key secret_key }) + + (** Convert a secret_key to a public key *) + let public secret_key = + convert_encoding group_encoding Pvss.Public_key.encoding + (Sp.Group.mul secret_key Sp.Group.g) + + let t = 5 + let n = 8 + + let random_scalar () = + Sp.Group.Scalar.of_Z (rand_Z ()) + + let secret_of_scalar s = + convert_encoding scalar_encoding Pvss.Secret_key.encoding s + + let secret_scalar = random_scalar () + let secret = secret_of_scalar secret_scalar + let public_secret = Pvss.Secret_key.to_public_key secret + let other_secret= secret_of_scalar (random_scalar ()) + + + let keypairs = random_keypairs n + let public_keys = List.map (fun {public_key} -> public_key) keypairs + + let ((shares, commitments, proof), + (other_shares, other_commitments, other_proof)) = + ( + Pvss.dealer_shares_and_proof ~secret ~t ~public_keys, + Pvss.dealer_shares_and_proof ~secret:other_secret ~t ~public_keys + ) + + let reveals = List.map2 ( + fun share keypair -> + (share, Pvss.reveal_share share + ~secret_key:keypair.secret_key ~public_key:keypair.public_key)) + shares keypairs +end + +let test_dealer_proof () = + let shr = (Setup.shares, Setup.other_shares) + and cmt = (Setup.commitments, Setup.other_commitments) + and prf = (Setup.proof, Setup.other_proof) in + + begin + for i = 0 to 1 do + for j = 0 to 1 do + for k = 0 to 1 do + let pick = function 0 -> fst | _ -> snd in + assert ((Pvss.check_dealer_proof + (pick i shr) + (pick j cmt) + ~proof:(pick k prf) ~public_keys:Setup.public_keys) = (i = j && j = k)) + done + done + done + end + +let test_share_reveal () = + + (* check reveal shares *) + let shares_valid = List.map2 (fun (share, (reveal, proof)) public_key -> + Pvss.check_revealed_share share reveal ~public_key:public_key proof) + Setup.reveals Setup.public_keys in + + List.iteri (fun i b -> print_endline (string_of_int i); assert b) + shares_valid + +let test_reconstruct () = + let indices = [0;1;2;3;4] in + let reconstructed = Pvss.reconstruct + (List.map + (fun n -> let (_, (r, _)) = List.nth Setup.reveals n in r) indices + ) + indices + in + assert (Sp.Group.((=)) + (Setup.convert_encoding + Pvss.Public_key.encoding Setup.group_encoding reconstructed) + (Setup.convert_encoding + Pvss.Public_key.encoding Setup.group_encoding Setup.public_secret)) + + +let tests = [ + "dealer_proof", `Quick, test_dealer_proof ; + "reveal", `Quick, test_share_reveal ; + "recontruct", `Quick, test_reconstruct +] + +let () = + Alcotest.run "test-pvss" [ + "pvss", tests + ] diff --git a/src/lib_crypto/znz.ml b/src/lib_crypto/znz.ml new file mode 100644 index 000000000..12f1ffcc2 --- /dev/null +++ b/src/lib_crypto/znz.ml @@ -0,0 +1,116 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +module type ZN = sig + type t + include S.B58_DATA with type t := t + include S.ENCODER with type t := t + val zero : t + val one : t + val n : Z.t + val (+) : t -> t -> t + val ( * ) : t -> t -> t + val (-) : t -> t -> t + val (=) : t -> t -> bool + val of_int : int -> t + val of_Z : Z.t -> t + val to_Z : t -> Z.t + val of_bits_exn : String.t -> t + val to_bits : t -> String.t + val pow : t -> Z.t -> t + val inv : t -> t option +end + +module type INT = sig + val n : Z.t +end + +module MakeZn (N : INT) (B : sig val b58_prefix : string end) : ZN = struct + + type t = Z.t + let n = N.n + let max_char_length = 2 * (Z.numbits n) + let zero = Z.zero + let one = Z.one + let of_Z r = Z.(erem r n) + let to_Z a = a + let of_int u = u |> Z.of_int |> of_Z + + let to_bits h = h |> Zplus.serialize |> (fun s -> String.sub s 0 (String.length s - 1)) + let of_bits_exn bits = + (* Do not process oversized inputs. *) + if Compare.Int.((String.length bits) > max_char_length) then + failwith "input too long" + else + (* Make sure the input is in the range [0, N.n-1]. Do not reduce modulo + N.n for free! *) + let x = Zplus.deserialize (bits) in + if Zplus.(x < zero || x >= N.n) then + failwith "out of range" + else + of_Z x + + let pow a x = Z.powm a Z.(erem x (sub n one)) n + let (+) x y = Z.(erem (add x y) n) + let ( * ) x y = Z.(erem (mul x y) n) + let (-) x y = Z.(erem (sub x y) n) + let (=) x y = Z.equal x y + + let inv a = Zplus.invert a n + + let title = Format.sprintf "Znz.Make(%s)" (Z.to_string N.n) + let name = Format.sprintf "An element of Z/nZ for n = %s" (Z.to_string N.n) + + type Base58.data += + | Data of t + + let b58check_encoding = + Base58.register_encoding + ~prefix: B.b58_prefix + ~length: 32 + ~to_raw: to_bits + ~of_raw: (fun s -> try Some (of_bits_exn s) with _ -> None) + ~wrap: (fun x -> Data x) + + include Helpers.MakeB58(struct + type nonrec t = t + let title = title + let name = name + let b58check_encoding = b58check_encoding + end) + + include Helpers.MakeEncoder(struct + type nonrec t = t + let name = name + let title = title + let raw_encoding = Data_encoding.(conv to_bits of_bits_exn string) + let to_b58check = to_b58check + let to_short_b58check = to_short_b58check + let of_b58check = of_b58check + let of_b58check_opt = of_b58check_opt + let of_b58check_exn = of_b58check_exn + end) + +end diff --git a/src/lib_crypto/znz.mli b/src/lib_crypto/znz.mli new file mode 100644 index 000000000..a9757920b --- /dev/null +++ b/src/lib_crypto/znz.mli @@ -0,0 +1,73 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(** Type for a module representing the ℤ/nℤ ring*) +module type ZN = sig + + type t + include S.B58_DATA with type t := t + include S.ENCODER with type t := t + + val zero : t + val one : t + val n : Z.t + val (+) : t -> t -> t + val ( * ) : t -> t -> t + val (-) : t -> t -> t + val (=) : t -> t -> bool + + (** Converts an integer to a ring element *) + val of_int : int -> t + + (** Converts a Zarith integer to a ring element *) + val of_Z : Z.t -> t + + (** Provides an integer representation between 0 and n-1 of an element *) + val to_Z : t -> Z.t + + (** Converts a string of bytes to an integer modulo n, requires the string of + byte to represent an integer between 0 and n-1 and checks the length of + the string for sanity*) + val of_bits_exn : String.t -> t + + (** Converts a ring element to a byte representation *) + val to_bits : t -> String.t + + (** Modular exponentiation *) + val pow : t -> Z.t -> t + + (** Returns the inverse of a in ℤ/nℤ, maybe *) + val inv : t -> t option + +end + + +(** Type of a module wrapping an integer. *) +module type INT = sig + val n : Z.t +end + +(** Functor to build the ℤ/nℤ ring given n*) +module MakeZn : functor (N : INT) (B : sig val b58_prefix : string end) -> ZN diff --git a/src/lib_crypto/zplus.ml b/src/lib_crypto/zplus.ml new file mode 100644 index 000000000..7da8e3716 --- /dev/null +++ b/src/lib_crypto/zplus.ml @@ -0,0 +1,73 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(* let re_trailing_null = + Re_pcre.regexp "^(.*?)\000*$" + + let remove_trailing_null s = + Re.get (Re.exec re_trailing_null s) 1 *) + +let remove_trailing_null s = + let n = String.length s in + let i = ref (n-1) in + while (!i >= 0) && (String.get s !i = '\000') do + i := !i - 1 + done; String.sub s 0 (!i+1) + +let serialize z = + let n = + if Z.(lt z zero) then + Z.(neg (add (add z z) one)) + else + Z.(add z z) + in + n |> Z.to_bits |> remove_trailing_null + +let deserialize z = + let n = Z.of_bits z in + let z = Z.shift_right_trunc n 1 in + if Z.(n land one = zero) then z else Z.neg z + +let leq a b = (Z.compare a b) <= 0 + +let geq a b = (Z.compare a b) >= 0 + +let lt a b = (Z.compare a b) < 0 + +let gt a b = (Z.compare a b) > 0 + +let (<) = lt +let (>) = gt +let (<=) = leq +let (>=) = geq + +let zero = Z.zero +let one = Z.one + +let invert a n = + try + Some (Z.invert a n) + with + Division_by_zero -> None diff --git a/src/lib_crypto/zplus.mli b/src/lib_crypto/zplus.mli new file mode 100644 index 000000000..b16dcadb7 --- /dev/null +++ b/src/lib_crypto/zplus.mli @@ -0,0 +1,59 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +val serialize: Z.t -> string +val deserialize: string -> Z.t + +val leq: Z.t -> Z.t -> bool +(** Less than or equal. *) + +val geq: Z.t -> Z.t -> bool +(** Greater than or equal. *) + +val lt: Z.t -> Z.t -> bool +(** Less than (and not equal). *) + +val gt: Z.t -> Z.t -> bool +(** Greater than (and not equal). *) + +val (<=): Z.t -> Z.t -> bool +(** Less than or equal. *) + +val (>=): Z.t -> Z.t -> bool +(** Greater than or equal. *) + +val (<): Z.t -> Z.t -> bool +(** Less than (and not equal). *) + +val (>): Z.t -> Z.t -> bool +(** Greater than (and not equal). *) + +val zero: Z.t + +val one: Z.t + +val invert: Z.t -> Z.t -> Z.t option +(** Invert the first argument modulo the second. Returns + none if there is no inverse *)