From f479ccbe1dc750fbdd6d0a85651606d19412e0d2 Mon Sep 17 00:00:00 2001 From: Sachin Meier Date: Sat, 22 Oct 2022 00:46:24 -0700 Subject: [PATCH 01/16] first draft adaptor signatures --- lib/secp256k1/schnorr.ex | 147 +++++++++++++++++++++++++++++++++++---- 1 file changed, 135 insertions(+), 12 deletions(-) diff --git a/lib/secp256k1/schnorr.ex b/lib/secp256k1/schnorr.ex index 0d21f4b..fac1ed8 100644 --- a/lib/secp256k1/schnorr.ex +++ b/lib/secp256k1/schnorr.ex @@ -41,24 +41,72 @@ defmodule Bitcoinex.Secp256k1.Schnorr do r_point = PrivateKey.to_point(k0) k = Secp256k1.force_even_y(k0) - e = - tagged_hash_challenge(Point.x_bytes(r_point) <> Point.x_bytes(d_point) <> z_bytes) - |> :binary.decode_unsigned() - |> Math.modulo(@n) + e = calculate_e(r_point, d_point, z_bytes) - sig_s = - (k.d + d.d * e) - |> Math.modulo(@n) + sig_s = calculate_s(k, d, e) {:ok, %Signature{r: r_point.x, s: sig_s}} end end end + @spec tweaked_sign(PrivateKey.t(), non_neg_integer, non_neg_integer, Point.t()) :: {:ok, Signature.t(), Point.t()} | {:error, String.t()} + def tweaked_sign(privkey, z, aux, pubtweak) do + case PrivateKey.validate(privkey) do + {:error, msg} -> + {:error, msg} + + {:ok, privkey} -> + z_bytes = Utils.int_to_big(z, 32) + aux_bytes = Utils.int_to_big(aux, 32) + d_point = PrivateKey.to_point(privkey) + d = Secp256k1.force_even_y(privkey) + d_bytes = Utils.int_to_big(d.d, 32) + tagged_aux_hash = tagged_hash_aux(aux_bytes) + t = Utils.xor_bytes(d_bytes, tagged_aux_hash) + + {:ok, k0} = + tagged_hash_nonce(t <> Point.x_bytes(d_point) <> z_bytes) + |> :binary.decode_unsigned() + |> Math.modulo(@n) + |> PrivateKey.new() + + if k0.d == 0 do + {:error, "invalid aux randomness"} + else + r_point = PrivateKey.to_point(k0) + k = Secp256k1.force_even_y(k0) + + tweaked_r_point = Math.add(r_point, pubtweak) + + e = calculate_e(Point.x_bytes(tweaked_r_point), Point.x_bytes(d_point), z_bytes) + + sig_s = calculate_s(k, d, e) + + {:ok, %Signature{r: r_point.x, s: sig_s}, pubtweak} + end + end + end + defp tagged_hash_aux(aux), do: Utils.tagged_hash("BIP0340/aux", aux) defp tagged_hash_nonce(nonce), do: Utils.tagged_hash("BIP0340/nonce", nonce) defp tagged_hash_challenge(chal), do: Utils.tagged_hash("BIP0340/challenge", chal) + defp calculate_s(k, d, e) do + (k.d + d.d * e) + |> Math.modulo(@n) + end + + defp calculate_e(nonce_bytes, pubkey_bytes, msg_bytes) do + tagged_hash_challenge(nonce_bytes <> pubkey_bytes <> msg_bytes) + |> :binary.decode_unsigned() + |> Math.modulo(@n) + end + + def validate_r(r_point, rx) do + !Point.is_inf(r_point) && Point.has_even_y(r_point) && r_point.x == rx + end + @doc """ verify whether the schnorr signature is valid for the given message hash and public key """ @@ -70,16 +118,91 @@ defmodule Bitcoinex.Secp256k1.Schnorr do r_bytes = Utils.int_to_big(r, 32) z_bytes = Utils.int_to_big(z, 32) - e = - tagged_hash_challenge(r_bytes <> Point.x_bytes(pubkey) <> z_bytes) - |> :binary.decode_unsigned() - |> Math.modulo(@n) + e = calculate_e(r_bytes, Point.x_bytes(pubkey), z_bytes) r_point = @generator_point |> Math.multiply(s) |> Math.add(Math.multiply(pubkey, Params.curve().n - e)) - !Point.is_inf(r_point) && Point.has_even_y(r_point) && r_point.x == r + validate_r(r_point, r) + end + + @spec verify_tweaked_signature(Point.t(), non_neg_integer, Signature.t(), Point.t()) :: + boolean | {:error, String.t()} + def verify_tweaked_signature(pubkey, z, %Signature{r: r, s: s}, pubtweak) do + if r >= Params.curve().p || s >= Params.curve().n, do: {:error, "invalid signature"} + + case Point.lift_x(r) do + {:error, err} -> + {:error, err} + + {:ok, given_r_point} -> + tweaked_point = Math.add(given_r_point, pubtweak) + + z_bytes = Utils.int_to_big(z, 32) + + e = calculate_e(Point.x_bytes(tweaked_point), Point.x_bytes(pubkey), z_bytes) + + r_point = + @generator_point + |> Math.multiply(s) + |> Math.add(Math.multiply(pubkey, Params.curve().n - e)) + + validate_r(r_point, r) + end + end + + + @spec extract_tweak(Point.t(), non_neg_integer, Signature.t(), Signature.t()) :: {:ok, non_neg_integer} | {:error, String.t()} + def extract_tweak(pubkey, z, %Signature{s: s}, %Signature{r: tweaked_r, s: tweaked_s}) do + tweaked_r_bytes = Utils.int_to_big(tweaked_r, 32) + + z_bytes = Utils.int_to_big(z, 32) + e = calculate_e(tweaked_r_bytes, Point.x_bytes(pubkey), z_bytes) + + tweaked_r_point = + @generator_point + |> Math.multiply(tweaked_s) + |> Math.add(Math.multiply(pubkey, Params.curve().n - e)) + + if validate_r(tweaked_r_point, tweaked_r) do + if tweaked_s < s do + {:error, "invalid tweak"} + else + {:ok, tweaked_s - s} + end + else + {:error, "failed to extract tweak due to invalid signature"} + end + end + + @spec extract_tweaked_signature(Point.t(), non_neg_integer, Signature.t(), non_neg_integer) :: {:ok, Signature.t()} | {:error, String.t()} + def extract_tweaked_signature(pubkey, z, %Signature{r: r, s: s}, tweak) do + case Point.lift_x(r) do + {:error, err} -> + {:error, err} + + {:ok, r_point} -> + tweak_point = PrivateKey.to_point(tweak) + + tweaked_r = Math.add(r_point, tweak_point).x + tweaked_s = tweak+s + + r_bytes = Utils.int_to_big(r, 32) + z_bytes = Utils.int_to_big(z, 32) + e = calculate_e(r_bytes, Point.x_bytes(pubkey), z_bytes) + + tweaked_r_point = + @generator_point + |> Math.multiply(tweaked_s) + |> Math.add(Math.multiply(pubkey, Params.curve().n - e)) + + if validate_r(tweaked_r_point, tweaked_r) do + {:ok, %Signature{r: tweaked_r, s: tweaked_s}} + else + {:error, "tweak does not produce valid signature"} + end + end end end From 2ccff2e66c93d5454bafd39fd6834f3611482f61 Mon Sep 17 00:00:00 2001 From: Sachin Meier Date: Sat, 22 Oct 2022 00:48:07 -0700 Subject: [PATCH 02/16] fmt --- lib/secp256k1/privatekey.ex | 1 + lib/secp256k1/schnorr.ex | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/secp256k1/privatekey.ex b/lib/secp256k1/privatekey.ex index 77b0a2a..734feb7 100644 --- a/lib/secp256k1/privatekey.ex +++ b/lib/secp256k1/privatekey.ex @@ -54,6 +54,7 @@ defmodule Bitcoinex.Secp256k1.PrivateKey do case new(d) do {:ok, sk} -> to_point(sk) + {:error, msg} -> {:error, msg} end diff --git a/lib/secp256k1/schnorr.ex b/lib/secp256k1/schnorr.ex index fac1ed8..021dade 100644 --- a/lib/secp256k1/schnorr.ex +++ b/lib/secp256k1/schnorr.ex @@ -50,7 +50,8 @@ defmodule Bitcoinex.Secp256k1.Schnorr do end end - @spec tweaked_sign(PrivateKey.t(), non_neg_integer, non_neg_integer, Point.t()) :: {:ok, Signature.t(), Point.t()} | {:error, String.t()} + @spec tweaked_sign(PrivateKey.t(), non_neg_integer, non_neg_integer, Point.t()) :: + {:ok, Signature.t(), Point.t()} | {:error, String.t()} def tweaked_sign(privkey, z, aux, pubtweak) do case PrivateKey.validate(privkey) do {:error, msg} -> @@ -153,8 +154,8 @@ defmodule Bitcoinex.Secp256k1.Schnorr do end end - - @spec extract_tweak(Point.t(), non_neg_integer, Signature.t(), Signature.t()) :: {:ok, non_neg_integer} | {:error, String.t()} + @spec extract_tweak(Point.t(), non_neg_integer, Signature.t(), Signature.t()) :: + {:ok, non_neg_integer} | {:error, String.t()} def extract_tweak(pubkey, z, %Signature{s: s}, %Signature{r: tweaked_r, s: tweaked_s}) do tweaked_r_bytes = Utils.int_to_big(tweaked_r, 32) @@ -177,7 +178,8 @@ defmodule Bitcoinex.Secp256k1.Schnorr do end end - @spec extract_tweaked_signature(Point.t(), non_neg_integer, Signature.t(), non_neg_integer) :: {:ok, Signature.t()} | {:error, String.t()} + @spec extract_tweaked_signature(Point.t(), non_neg_integer, Signature.t(), non_neg_integer) :: + {:ok, Signature.t()} | {:error, String.t()} def extract_tweaked_signature(pubkey, z, %Signature{r: r, s: s}, tweak) do case Point.lift_x(r) do {:error, err} -> @@ -187,7 +189,7 @@ defmodule Bitcoinex.Secp256k1.Schnorr do tweak_point = PrivateKey.to_point(tweak) tweaked_r = Math.add(r_point, tweak_point).x - tweaked_s = tweak+s + tweaked_s = tweak + s r_bytes = Utils.int_to_big(r, 32) z_bytes = Utils.int_to_big(z, 32) From 40372d54aefd47962ab3f0848f9b596928a12d5d Mon Sep 17 00:00:00 2001 From: Sachin Meier Date: Sat, 22 Oct 2022 01:03:36 -0700 Subject: [PATCH 03/16] fix typo --- lib/secp256k1/schnorr.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/secp256k1/schnorr.ex b/lib/secp256k1/schnorr.ex index 021dade..811c488 100644 --- a/lib/secp256k1/schnorr.ex +++ b/lib/secp256k1/schnorr.ex @@ -41,7 +41,7 @@ defmodule Bitcoinex.Secp256k1.Schnorr do r_point = PrivateKey.to_point(k0) k = Secp256k1.force_even_y(k0) - e = calculate_e(r_point, d_point, z_bytes) + e = calculate_e(Point.x_bytes(r_point), Point.x_bytes(d_point), z_bytes) sig_s = calculate_s(k, d, e) From 4baa1f227e84da5191da923776bbdfc3e6272959 Mon Sep 17 00:00:00 2001 From: Sachin Meier Date: Sat, 22 Oct 2022 02:48:51 -0700 Subject: [PATCH 04/16] simplify code & add extract_sig from adaptor --- lib/secp256k1/schnorr.ex | 90 +++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/lib/secp256k1/schnorr.ex b/lib/secp256k1/schnorr.ex index 811c488..907697d 100644 --- a/lib/secp256k1/schnorr.ex +++ b/lib/secp256k1/schnorr.ex @@ -50,9 +50,9 @@ defmodule Bitcoinex.Secp256k1.Schnorr do end end - @spec tweaked_sign(PrivateKey.t(), non_neg_integer, non_neg_integer, Point.t()) :: + @spec sign_for_tweak(PrivateKey.t(), non_neg_integer, non_neg_integer, Point.t()) :: {:ok, Signature.t(), Point.t()} | {:error, String.t()} - def tweaked_sign(privkey, z, aux, pubtweak) do + def sign_for_tweak(privkey, z, aux, pubtweak) do case PrivateKey.validate(privkey) do {:error, msg} -> {:error, msg} @@ -75,8 +75,8 @@ defmodule Bitcoinex.Secp256k1.Schnorr do if k0.d == 0 do {:error, "invalid aux randomness"} else - r_point = PrivateKey.to_point(k0) k = Secp256k1.force_even_y(k0) + r_point = PrivateKey.to_point(k) tweaked_r_point = Math.add(r_point, pubtweak) @@ -93,6 +93,12 @@ defmodule Bitcoinex.Secp256k1.Schnorr do defp tagged_hash_nonce(nonce), do: Utils.tagged_hash("BIP0340/nonce", nonce) defp tagged_hash_challenge(chal), do: Utils.tagged_hash("BIP0340/challenge", chal) + defp calculate_r(pubkey, s, e) do + @generator_point + |> Math.multiply(s) + |> Math.add(Math.multiply(pubkey, Params.curve().n - e)) + end + defp calculate_s(k, d, e) do (k.d + d.d * e) |> Math.modulo(@n) @@ -104,7 +110,7 @@ defmodule Bitcoinex.Secp256k1.Schnorr do |> Math.modulo(@n) end - def validate_r(r_point, rx) do + defp validate_r(r_point, rx) do !Point.is_inf(r_point) && Point.has_even_y(r_point) && r_point.x == rx end @@ -118,20 +124,16 @@ defmodule Bitcoinex.Secp256k1.Schnorr do r_bytes = Utils.int_to_big(r, 32) z_bytes = Utils.int_to_big(z, 32) - e = calculate_e(r_bytes, Point.x_bytes(pubkey), z_bytes) - r_point = - @generator_point - |> Math.multiply(s) - |> Math.add(Math.multiply(pubkey, Params.curve().n - e)) + r_point = calculate_r(pubkey, s, e) validate_r(r_point, r) end - @spec verify_tweaked_signature(Point.t(), non_neg_integer, Signature.t(), Point.t()) :: + @spec verify_untweaked_signature(Point.t(), non_neg_integer, Signature.t(), Point.t()) :: boolean | {:error, String.t()} - def verify_tweaked_signature(pubkey, z, %Signature{r: r, s: s}, pubtweak) do + def verify_untweaked_signature(pubkey, z, %Signature{r: r, s: s}, pubtweak) do if r >= Params.curve().p || s >= Params.curve().n, do: {:error, "invalid signature"} case Point.lift_x(r) do @@ -140,34 +142,33 @@ defmodule Bitcoinex.Secp256k1.Schnorr do {:ok, given_r_point} -> tweaked_point = Math.add(given_r_point, pubtweak) - z_bytes = Utils.int_to_big(z, 32) - e = calculate_e(Point.x_bytes(tweaked_point), Point.x_bytes(pubkey), z_bytes) - r_point = - @generator_point - |> Math.multiply(s) - |> Math.add(Math.multiply(pubkey, Params.curve().n - e)) + r_point = calculate_r(pubkey, s, e) validate_r(r_point, r) end end - @spec extract_tweak(Point.t(), non_neg_integer, Signature.t(), Signature.t()) :: - {:ok, non_neg_integer} | {:error, String.t()} - def extract_tweak(pubkey, z, %Signature{s: s}, %Signature{r: tweaked_r, s: tweaked_s}) do - tweaked_r_bytes = Utils.int_to_big(tweaked_r, 32) + @spec tweak_signature(Signature.t(), non_neg_integer | PrivateKey.t()) :: Signature.t() + def tweak_signature(sig, t = %PrivateKey{}), do: tweak_signature(sig, t.d) - z_bytes = Utils.int_to_big(z, 32) - e = calculate_e(tweaked_r_bytes, Point.x_bytes(pubkey), z_bytes) - - tweaked_r_point = - @generator_point - |> Math.multiply(tweaked_s) - |> Math.add(Math.multiply(pubkey, Params.curve().n - e)) + def tweak_signature(%Signature{r: r, s: s}, tweak) do + {:ok, t} = PrivateKey.new(tweak) + t_point = PrivateKey.to_point(t) + {:ok, r_point} = Point.lift_x(r) + %Signature{r: Math.add(r_point, t_point).x, s: tweak + s} + end - if validate_r(tweaked_r_point, tweaked_r) do + @doc """ + extract_tweak takes a signer pubkey, message hash z, untweaked signature (adaptor signature), + and a completed signature, verifies the signature, and then returns the revealed tweak secret + """ + @spec extract_tweak(Point.t(), non_neg_integer, Signature.t(), Signature.t()) :: + {:ok, non_neg_integer} | {:error, String.t()} + def extract_tweak(pubkey, z, %Signature{s: s}, tweaked_sig = %Signature{s: tweaked_s}) do + if verify_signature(pubkey, z, tweaked_sig) do if tweaked_s < s do {:error, "invalid tweak"} else @@ -178,30 +179,35 @@ defmodule Bitcoinex.Secp256k1.Schnorr do end end - @spec extract_tweaked_signature(Point.t(), non_neg_integer, Signature.t(), non_neg_integer) :: + @doc """ + extract_tweaked_signature takes a signer pubkey, message hash z, untweaked signature (adaptor signature), + and a tweak secret, and uses it to verify the adaptor signature, and returns the complete signature + """ + @spec extract_tweaked_signature( + Point.t(), + non_neg_integer, + Signature.t(), + non_neg_integer | PrivateKey.t() + ) :: {:ok, Signature.t()} | {:error, String.t()} + def extract_tweaked_signature(pubkey, z, sig, t = %PrivateKey{}), + do: extract_tweaked_signature(pubkey, z, sig, t.d) + def extract_tweaked_signature(pubkey, z, %Signature{r: r, s: s}, tweak) do case Point.lift_x(r) do {:error, err} -> {:error, err} {:ok, r_point} -> - tweak_point = PrivateKey.to_point(tweak) - - tweaked_r = Math.add(r_point, tweak_point).x tweaked_s = tweak + s - r_bytes = Utils.int_to_big(r, 32) - z_bytes = Utils.int_to_big(z, 32) - e = calculate_e(r_bytes, Point.x_bytes(pubkey), z_bytes) + tweak_point = PrivateKey.to_point(tweak) + tweaked_r_point = Math.add(r_point, tweak_point) - tweaked_r_point = - @generator_point - |> Math.multiply(tweaked_s) - |> Math.add(Math.multiply(pubkey, Params.curve().n - e)) + tweaked_sig = %Signature{r: tweaked_r_point.x, s: tweaked_s} - if validate_r(tweaked_r_point, tweaked_r) do - {:ok, %Signature{r: tweaked_r, s: tweaked_s}} + if verify_signature(pubkey, z, tweaked_sig) do + {:ok, %Signature{r: tweaked_r_point.x, s: tweaked_s}} else {:error, "tweak does not produce valid signature"} end From a04f43e583661dc262fbebba8bf91a4a353c974e Mon Sep 17 00:00:00 2001 From: Sachin Meier Date: Sat, 22 Oct 2022 02:53:19 -0700 Subject: [PATCH 05/16] make code consistent across sign & adaptor sign --- lib/secp256k1/schnorr.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/secp256k1/schnorr.ex b/lib/secp256k1/schnorr.ex index 907697d..868d711 100644 --- a/lib/secp256k1/schnorr.ex +++ b/lib/secp256k1/schnorr.ex @@ -38,8 +38,8 @@ defmodule Bitcoinex.Secp256k1.Schnorr do if k0.d == 0 do {:error, "invalid aux randomness"} else - r_point = PrivateKey.to_point(k0) k = Secp256k1.force_even_y(k0) + r_point = PrivateKey.to_point(k) e = calculate_e(Point.x_bytes(r_point), Point.x_bytes(d_point), z_bytes) From 47d2a012d799b89d34de241ac79bb0143ae710d2 Mon Sep 17 00:00:00 2001 From: Sachin Meier Date: Thu, 27 Oct 2022 14:11:18 -0700 Subject: [PATCH 06/16] temp: try to fix R+T not even bug --- lib/secp256k1/point.ex | 2 +- lib/secp256k1/privatekey.ex | 5 + lib/secp256k1/schnorr.ex | 101 +++++++++++++------- lib/secp256k1/secp256k1.ex | 9 +- scripts/gen_test_vectors.exs | 58 ++++++++++++ scripts/schnorr_adaptor.exs | 40 ++++++++ scripts/test_broken_adaptor.exs | 30 ++++++ test/secp256k1/adaptor_test.exs | 151 ++++++++++++++++++++++++++++++ test/secp256k1/schnorr_test.exs | 8 +- test/secp256k1/secp256k1_test.exs | 17 +++- 10 files changed, 383 insertions(+), 38 deletions(-) create mode 100644 scripts/gen_test_vectors.exs create mode 100644 scripts/schnorr_adaptor.exs create mode 100644 scripts/test_broken_adaptor.exs create mode 100644 test/secp256k1/adaptor_test.exs diff --git a/lib/secp256k1/point.ex b/lib/secp256k1/point.ex index 2de8ddc..1bb6b7a 100644 --- a/lib/secp256k1/point.ex +++ b/lib/secp256k1/point.ex @@ -124,7 +124,7 @@ defmodule Bitcoinex.Secp256k1.Point do """ @spec x_bytes(t()) :: binary def x_bytes(%__MODULE__{x: x}) do - Bitcoinex.Utils.pad(:binary.encode_unsigned(x), 32, :leading) + Utils.int_to_big(x, 32) end @doc """ diff --git a/lib/secp256k1/privatekey.ex b/lib/secp256k1/privatekey.ex index 734feb7..1f01bf4 100644 --- a/lib/secp256k1/privatekey.ex +++ b/lib/secp256k1/privatekey.ex @@ -60,6 +60,11 @@ defmodule Bitcoinex.Secp256k1.PrivateKey do end end + @spec negate(t()) :: t() + def negate(%__MODULE__{d: d}) do + %__MODULE__{d: @n - d} + end + @doc """ serialize_private_key serializes a private key into hex """ diff --git a/lib/secp256k1/schnorr.ex b/lib/secp256k1/schnorr.ex index 868d711..513d561 100644 --- a/lib/secp256k1/schnorr.ex +++ b/lib/secp256k1/schnorr.ex @@ -7,6 +7,7 @@ defmodule Bitcoinex.Secp256k1.Schnorr do alias Bitcoinex.Utils @n Params.curve().n + @p Params.curve().p @generator_point %Point{ x: Params.curve().g_x, @@ -29,30 +30,18 @@ defmodule Bitcoinex.Secp256k1.Schnorr do tagged_aux_hash = tagged_hash_aux(aux_bytes) t = Utils.xor_bytes(d_bytes, tagged_aux_hash) - {:ok, k0} = - tagged_hash_nonce(t <> Point.x_bytes(d_point) <> z_bytes) - |> :binary.decode_unsigned() - |> Math.modulo(@n) - |> PrivateKey.new() + {:ok, k} = calculate_k(t, d_point, z_bytes) + r_point = PrivateKey.to_point(k) + e = calculate_e(Point.x_bytes(r_point), Point.x_bytes(d_point), z_bytes) + sig_s = calculate_s(k, d, e) - if k0.d == 0 do - {:error, "invalid aux randomness"} - else - k = Secp256k1.force_even_y(k0) - r_point = PrivateKey.to_point(k) - - e = calculate_e(Point.x_bytes(r_point), Point.x_bytes(d_point), z_bytes) - - sig_s = calculate_s(k, d, e) - - {:ok, %Signature{r: r_point.x, s: sig_s}} - end + {:ok, %Signature{r: r_point.x, s: sig_s}} end end @spec sign_for_tweak(PrivateKey.t(), non_neg_integer, non_neg_integer, Point.t()) :: {:ok, Signature.t(), Point.t()} | {:error, String.t()} - def sign_for_tweak(privkey, z, aux, pubtweak) do + def sign_for_tweak(privkey, z, aux, tweak_point) do case PrivateKey.validate(privkey) do {:error, msg} -> {:error, msg} @@ -75,16 +64,28 @@ defmodule Bitcoinex.Secp256k1.Schnorr do if k0.d == 0 do {:error, "invalid aux randomness"} else - k = Secp256k1.force_even_y(k0) + r_point = PrivateKey.to_point(k0) + tweaked_r_point = Math.add(r_point, tweak_point) + IO.puts("!---\n") + IO.puts("k0: " <> to_string(k0.d)) + IO.puts("x: " <> to_string(tweaked_r_point.x) <> "\ny: " <> to_string(tweaked_r_point.y) <> "\n") + # we must ensure that R+T, the final signature's nonce point has even y + k = get_k_for_even_tweaked_nonce(k0, tweaked_r_point) + IO.puts("k1: " <> to_string(k.d)) r_point = PrivateKey.to_point(k) + tweaked_r_point = Math.add(r_point, tweak_point) + IO.puts("x: " <> to_string(tweaked_r_point.x) <> "\ny: " <> to_string(tweaked_r_point.y) <> "\n") + IO.puts("---!\n") + if Point.has_even_y(tweaked_r_point) do + sameK = to_string(k == k0) + {:error, "what is going on: sameK: #{sameK}"} + else + e = calculate_e(Point.x_bytes(tweaked_r_point), Point.x_bytes(d_point), z_bytes) + sig_s = calculate_s(k, d, e) + + {:ok, %Signature{r: r_point.x, s: sig_s}, tweak_point} + end - tweaked_r_point = Math.add(r_point, pubtweak) - - e = calculate_e(Point.x_bytes(tweaked_r_point), Point.x_bytes(d_point), z_bytes) - - sig_s = calculate_s(k, d, e) - - {:ok, %Signature{r: r_point.x, s: sig_s}, pubtweak} end end end @@ -93,6 +94,7 @@ defmodule Bitcoinex.Secp256k1.Schnorr do defp tagged_hash_nonce(nonce), do: Utils.tagged_hash("BIP0340/nonce", nonce) defp tagged_hash_challenge(chal), do: Utils.tagged_hash("BIP0340/challenge", chal) + defp calculate_r(pubkey, s, e) do @generator_point |> Math.multiply(s) @@ -104,6 +106,30 @@ defmodule Bitcoinex.Secp256k1.Schnorr do |> Math.modulo(@n) end + defp get_k_for_even_tweaked_nonce(k, tweaked_point) do + if Point.has_even_y(tweaked_point) do + IO.puts("k unchanged\n") + k + else + IO.puts("k changed\n") + PrivateKey.negate(k) + end + end + + defp calculate_k(t, d_point, z_bytes) do + {:ok, k0} = + tagged_hash_nonce(t <> Point.x_bytes(d_point) <> z_bytes) + |> :binary.decode_unsigned() + |> Math.modulo(@n) + |> PrivateKey.new() + + if k0.d == 0 do + {:error, "invalid aux randomness"} + else + {:ok, Secp256k1.force_even_y(k0)} + end + end + defp calculate_e(nonce_bytes, pubkey_bytes, msg_bytes) do tagged_hash_challenge(nonce_bytes <> pubkey_bytes <> msg_bytes) |> :binary.decode_unsigned() @@ -111,7 +137,15 @@ defmodule Bitcoinex.Secp256k1.Schnorr do end defp validate_r(r_point, rx) do - !Point.is_inf(r_point) && Point.has_even_y(r_point) && r_point.x == rx + cond do + Point.is_inf(r_point) -> + {:error, "R point is infinite"} + !Point.has_even_y(r_point) -> + {:error, "R point is not even"} + r_point.x != rx -> + {:error, "x's do not match #{r_point.x} vs #{rx}"} + true -> true + end end @doc """ @@ -120,7 +154,7 @@ defmodule Bitcoinex.Secp256k1.Schnorr do @spec verify_signature(Point.t(), non_neg_integer, Signature.t()) :: boolean | {:error, String.t()} def verify_signature(pubkey, z, %Signature{r: r, s: s}) do - if r >= Params.curve().p || s >= Params.curve().n, do: {:error, "invalid signature"} + if r >= @p || s >= @n, do: {:error, "invalid signature"} r_bytes = Utils.int_to_big(r, 32) z_bytes = Utils.int_to_big(z, 32) @@ -133,15 +167,14 @@ defmodule Bitcoinex.Secp256k1.Schnorr do @spec verify_untweaked_signature(Point.t(), non_neg_integer, Signature.t(), Point.t()) :: boolean | {:error, String.t()} - def verify_untweaked_signature(pubkey, z, %Signature{r: r, s: s}, pubtweak) do - if r >= Params.curve().p || s >= Params.curve().n, do: {:error, "invalid signature"} - + def verify_untweaked_signature(pubkey, z, %Signature{r: r, s: s}, tweak_point) do + if r >= @p || s >= @n, do: {:error, "invalid signature"} case Point.lift_x(r) do {:error, err} -> {:error, err} {:ok, given_r_point} -> - tweaked_point = Math.add(given_r_point, pubtweak) + tweaked_point = Math.add(given_r_point, tweak_point) z_bytes = Utils.int_to_big(z, 32) e = calculate_e(Point.x_bytes(tweaked_point), Point.x_bytes(pubkey), z_bytes) @@ -153,12 +186,12 @@ defmodule Bitcoinex.Secp256k1.Schnorr do @spec tweak_signature(Signature.t(), non_neg_integer | PrivateKey.t()) :: Signature.t() def tweak_signature(sig, t = %PrivateKey{}), do: tweak_signature(sig, t.d) - def tweak_signature(%Signature{r: r, s: s}, tweak) do {:ok, t} = PrivateKey.new(tweak) t_point = PrivateKey.to_point(t) {:ok, r_point} = Point.lift_x(r) - %Signature{r: Math.add(r_point, t_point).x, s: tweak + s} + tw_s = Math.modulo(tweak+s, @n) + %Signature{r: Math.add(r_point, t_point).x, s: tw_s} end @doc """ diff --git a/lib/secp256k1/secp256k1.ex b/lib/secp256k1/secp256k1.ex index e99cbc2..641b01f 100644 --- a/lib/secp256k1/secp256k1.ex +++ b/lib/secp256k1/secp256k1.ex @@ -113,7 +113,14 @@ defmodule Bitcoinex.Secp256k1 do @spec serialize_signature(t()) :: binary def serialize_signature(%__MODULE__{r: r, s: s}) do - :binary.encode_unsigned(r) <> :binary.encode_unsigned(s) + Utils.int_to_big(r, 32) <> Utils.int_to_big(s, 32) + end + + @spec to_hex(t()) :: binary + def to_hex(sig) do + sig + |> serialize_signature() + |> Base.encode16(case: :lower) end @doc """ diff --git a/scripts/gen_test_vectors.exs b/scripts/gen_test_vectors.exs new file mode 100644 index 0000000..43ed9ca --- /dev/null +++ b/scripts/gen_test_vectors.exs @@ -0,0 +1,58 @@ +alias Bitcoinex.Secp256k1 +alias Bitcoinex.Secp256k1.{Math, PrivateKey, Point, Schnorr, Signature} +alias Bitcoinex.Utils + +to_hex = fn i -> "0x" <> Integer.to_string(i, 16) end + +write_row = fn file, sk, pk, tw, t_point, z, aux, ut_sig, tw_sig, err, is_tweaked_s_even, is_tweaked_s_ooo -> IO.binwrite(file, +to_hex.(sk.d) <> "," <> Point.x_hex(pk) <> "," <> to_hex.(tw.d) <> "," +<> Point.x_hex(t_point) <> "," <> to_hex.(z) <> "," <> to_hex.(aux) <> "," +<> Signature.to_hex(ut_sig) <> "," <> Signature.to_hex(tw_sig) <> "," +<> err <> "," <> to_string(is_tweaked_s_even) <> "," <> to_string(is_tweaked_s_ooo) <> "\n") +end + +order_n = 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_BAAEDCE6_AF48A03B_BFD25E8C_D0364141 + +{:ok, good_file} = File.open("schnorr_adaptor_test_vectors-good.csv", [:write]) +{:ok, bad_file} = File.open("schnorr_adaptor_test_vectors-bad.csv", [:write]) + +IO.binwrite(good_file, "private_key,public_key,tweak_secret,tweak_point,message_hash,aux_rand,untweaked_adaptor_signature,tweaked_signature,is_tweaked_s_even\n") +IO.binwrite(bad_file, "private_key,public_key,tweak_secret,tweak_point,message_hash,aux_rand,untweaked_adaptor_signature,tweaked_signature,is_tweaked_s_even\n") + +for _ <- 1..50 do + ski = :rand.uniform(order_n-1) + {:ok, sk0} = PrivateKey.new(ski) + sk = Secp256k1.force_even_y(sk0) + pk = PrivateKey.to_point(sk) + + # tweak + ti = :rand.uniform(order_n-1) + {:ok, tw} = PrivateKey.new(ti) + tw = Secp256k1.force_even_y(tw) + tw_point = PrivateKey.to_point(tw) + + msg = + :rand.uniform(order_n-1) + |> :binary.encode_unsigned() + z = Utils.double_sha256(msg) |> :binary.decode_unsigned() + + aux = :rand.uniform(order_n-1) + + # create adaptor sig + {:ok, ut_sig, _tw_point} = Schnorr.sign_for_tweak(sk, z, aux, tw_point) + tw_sig = Schnorr.tweak_signature(ut_sig, tw.d) + + # checks + tweaked_s = tw.d+ut_sig.s + is_tweaked_s_ooo = tweaked_s > order_n + {:ok, tweaked_s} = PrivateKey.new(Math.modulo(tweaked_s, order_n)) + tweaked_forced_s = Secp256k1.force_even_y(tweaked_s) + is_tweaked_s_even = tweaked_forced_s == tweaked_s + + case Schnorr.verify_signature(pk, z, tw_sig) do + true -> + write_row.(good_file, sk, pk, tw, tw_point, z, aux, ut_sig, tw_sig, "", is_tweaked_s_even, is_tweaked_s_ooo) + {:error, err} -> + write_row.(bad_file, sk, pk, tw, tw_point, z, aux, ut_sig, tw_sig, err, is_tweaked_s_even, is_tweaked_s_ooo) + end +end diff --git a/scripts/schnorr_adaptor.exs b/scripts/schnorr_adaptor.exs new file mode 100644 index 0000000..932deb5 --- /dev/null +++ b/scripts/schnorr_adaptor.exs @@ -0,0 +1,40 @@ +alias Bitcoinex.Secp256k1 +alias Bitcoinex.Secp256k1.{PrivateKey, Point, Schnorr, Signature} +alias Bitcoinex.Utils +# private key +{:ok, sk} = PrivateKey.new(1234123412341234123412341234) +pk = PrivateKey.to_point(sk) + +# tweak +{:ok, t} = PrivateKey.new(658393766392737484910002828395) +t = Secp256k1.force_even_y(t) +t_point = PrivateKey.to_point(t) + +msg = "tweakin" +z = Utils.double_sha256(msg) |> :binary.decode_unsigned() + +aux = 1203948712823749283 + +# create adaptor sig +{:ok, ut_sig, t_point_} = Schnorr.sign_for_tweak(sk, z, aux, t_point) +t_point_ == t_point + +# adaptor sig is not a valid schnorr sig +!Schnorr.verify_signature(pk, z, ut_sig) + +# verify adaptor signature +Schnorr.verify_untweaked_signature(pk, z, ut_sig, t_point) + +# complete adaptor sig +tw_sig = Schnorr.tweak_signature(ut_sig, t.d) + +# complete sig must be valid schnorr sig +Schnorr.verify_signature(pk, z, tw_sig) + +# extract tweak +{:ok, tweak} = Schnorr.extract_tweak(pk, z, ut_sig, tw_sig) +tweak == t.d + +# extract signature given tweak +{:ok, sig} = Schnorr.extract_tweaked_signature(pk, z, ut_sig, t.d) +sig == tw_sig diff --git a/scripts/test_broken_adaptor.exs b/scripts/test_broken_adaptor.exs new file mode 100644 index 0000000..c9919b3 --- /dev/null +++ b/scripts/test_broken_adaptor.exs @@ -0,0 +1,30 @@ + +alias Bitcoinex.Secp256k1 +alias Bitcoinex.Secp256k1.{PrivateKey, Schnorr, Signature} + +test = %{ + privkey: 0x279D71D68D3EE997019D005BDF703C271001631A7EE12E4C9DAD10C0754912DC, + pubkey: 0x22c63594ea2c2199e0500cdf6dffecdf878441720789c8dfcfb9af06a96fd1e4, + tweak_secret: 0xF8EBFDF85A3AF0C337ECB165EF47D565DE15CBCEEB597A243C3D54DF49B703D5, + tweak_point: 0x6545e169e4d2e940e63207110a9d44dd5d4ca65aeb58e3e566658f62d41bd23f, + message_hash: 0x5736367EBB12EDC15B0FA75319B46D016F86A0E057B9237240D6185C93596367, + aux_rand: 0x7E4E37835DDFC6A82A011073DCB779D02F1F5B52A2937B6ADD5B9DA2528FC5C6, + untweaked_sig: "e2125e2f6d791ce59b604dfc0578a823008a5c86f2f2efbd0de68a4cb19688d817ebac918f08e0078c66a26c664d9f169d66dc54fdd95972e68a69b79797274a", + tweaked_sig: "320ab814c2e7e2567af8e738ce83e9fdc55ef57933dd52b169bba46fd3516e4c10d7aa89e943d0cac45353d25595747dc0cdcb3d39ea335b62f5600a1117e9de" +} + +z = test.message_hash +aux = test.aux_rand + +{:ok, t} = PrivateKey.new(test.tweak_secret) +t2 = Secp256k1.force_even_y(t) +t_point = PrivateKey.to_point(t) + +{:ok, sk} = PrivateKey.new(test.privkey) +sk2 = Secp256k1.force_even_y(sk) + +# use sk2 +pk = PrivateKey.to_point(sk2) +{:ok, ut_sig, t_point_} = Schnorr.sign_for_tweak(sk2, z, aux, t_point) +tw_sig = Schnorr.tweak_signature(ut_sig, t.d) +Schnorr.verify_signature(pk, z, tw_sig) diff --git a/test/secp256k1/adaptor_test.exs b/test/secp256k1/adaptor_test.exs new file mode 100644 index 0000000..127cc6b --- /dev/null +++ b/test/secp256k1/adaptor_test.exs @@ -0,0 +1,151 @@ +defmodule Bitcoinex.Secp256k1.SchnorrTest do + use ExUnit.Case + + alias Bitcoinex.Secp256k1 + alias Bitcoinex.Secp256k1.{PrivateKey, Schnorr, Signature} + + + @schnorr_adaptor_signatures [ + %{ + privkey: 0x279D71D68D3EE997019D005BDF703C271001631A7EE12E4C9DAD10C0754912DC, + pubkey: 0x22c63594ea2c2199e0500cdf6dffecdf878441720789c8dfcfb9af06a96fd1e4, + tweak_secret: 0xF8EBFDF85A3AF0C337ECB165EF47D565DE15CBCEEB597A243C3D54DF49B703D5, + tweak_point: 0x6545e169e4d2e940e63207110a9d44dd5d4ca65aeb58e3e566658f62d41bd23f, + message_hash: 0x5736367EBB12EDC15B0FA75319B46D016F86A0E057B9237240D6185C93596367, + aux_rand: 0x7E4E37835DDFC6A82A011073DCB779D02F1F5B52A2937B6ADD5B9DA2528FC5C6, + untweaked_sig: "e2125e2f6d791ce59b604dfc0578a823008a5c86f2f2efbd0de68a4cb19688d817ebac918f08e0078c66a26c664d9f169d66dc54fdd95972e68a69b79797274a", + tweaked_sig: "320ab814c2e7e2567af8e738ce83e9fdc55ef57933dd52b169bba46fd3516e4c10d7aa89e943d0cac45353d25595747dc0cdcb3d39ea335b62f5600a1117e9de" + }, + %{ + privkey: 0x6F0B8C685BEB787F3121851AC13DAD9549C235217C5F578D9E7BE3B3845D8773, + pubkey: 0x825fa6ec886022247b7b98d24d47a6f03d3a47f2fa3916a244eba7114ea94fa1, + tweak_secret: 0x76D2C1FE7E575063C8888D935645973AC3AE1FFD1C97F25D14F7567657D7F748, + tweak_point: 0xb99378d7cf782cf6ff265ef8e3ba447b36da89224c0682ca61cc9916a4fec4bf, + message_hash: 0xA193926FAFF8CE363937D8655892E60D45661C5FC510BC711E4B5049A6B4DD30, + aux_rand: 0x76C66B394932DC5704EC0FB4CE4D76D388707396AC64D9C35BFC081F11D51124, + untweaked_sig: "864897cd95c22dda7fce0435fe365c658118e52d578ba77c25d1f1e63ff3cd0916b38c325759c310e467a0ec026d8362b2f5155adb19a17eb06a5c661b039be1", + tweaked_sig: "e277ad505af9e82abcb122c3df410459494af86315988d4596bf0fb71f64bf338d864e30d5b11374acf02e7f58b31a9d76a33557f7b193dbc561b2dc72db9329" + }, + %{ + privkey: 0x624FB68C6C2474358EF57A9810BA5A17855D0FF8676D5D80E93DF314D5C3D277, + pubkey: 0x4af83421073f24e51235c43ffb85aa94dc3139af958a2532c6b412832fbbacc4, + tweak_secret: 0xF1997F60060268C14B81744869AEB04098A93704C3BDA2A640A1B26F74E246F4, + tweak_point: 0xb9dc16baaf262b3a60a1979833f9ca350e2219121fdb43d3fd2045d3142bb71f, + message_hash: 0x97FF15503ECA5167E871E04998F3C90976E8FAFF9AD1AF54F045153BA41E1663, + aux_rand: 0x71C1FE1F3A27B04EEC4142AC1612A7C6419B1740716E2C29CEE319E718950A04, + untweaked_sig: "08365373469000bca3fa28a95469f6331a20d4f03429372573b3c2d3d3d5b0f6801f053ece6e34cdf9fa8f43ec2ed3b730c388c0e444dd45f9cc1f920adac25f", + tweaked_sig: "9e3deb23f27730ce1be854b0145cc9bf99e3ee10491d28308160c7b65b0a56e871b8849ed4709d8f457c038c55dd83f90ebde2def8b9dfb07a9b7374af86c812" + }, + %{ + privkey: 0xB5196100AAC42311C9B33B1746CA0B38AC4547936DDC4AFFF547A862A4BF9890, + pubkey: 0x5b279f79dcf2414b66160515de53ac471a043d6d9f62fd007c09ad90704ffd28, + tweak_secret: 0xA69690763B4034FCF0F269E80464643E521475BFE08162632971AE0BDAA7AE8E, + tweak_point: 0xe9f219bb7c0a390a7df96a4f33b7f7d6179a9a4aa216ed1a750ddba1719072f0, + message_hash: 0xAD6F06747576C8EC210113A2C070A7A5688616EE841FA61026D2FF1CF9F7970F, + aux_rand: 0x63241AA5C78B99A849025EB3056E84DB011769DC8BBE782FAFA6E89D2BEE0C16, + untweaked_sig: "ef3766f37e9235e9652418bd60c54cd8e9c2db06f7effc542415fc76fac9c5cb75cc36cc9d5c20d303c30e43c2be82708141b8f721f75c7f889ebb24da81c818", + tweaked_sig: "aa9788a104f497f788bf4979a791a04c8408fd2f82075d5810f5f85394d567761c62c742d89c55cff4b5782bc722e6b018a751d053301ea6f23e0aa3e4f33565" + }, + %{ + privkey: 0x45AC71188B8B3B180D806ABB0289AB23357BF7CE866BFDB13509C68BAC5C88C4, + pubkey: 0x8b688d7ce32ab13bed3f0c5f119791f8b76913809c22983c8b6713d6fdc6f5a3, + tweak_secret: 0x6D2B06A08C0F20A64FA06DADF35F992F4DA441A29541268FB5F9AA9B7977A886, + tweak_point: 0x4d01a75b79ad34ce9d1dec59c581881db6a05865807705558a31e5b19047b024, + message_hash: 0x1E3D05CC94304EB6CEF384D5ADF36827A6CA4299D70B821B60240995F75BB6CB, + aux_rand: 0x7EFC5AD98D6AA0542A9534FA8637AB87A1B8E48FBAFD89AC6EA9348B44EEA049, + untweaked_sig: "655244da978cb1fa37690f3fe98c0e78b58390207781dae0d57f74dde06c0c3f6c8463e2c5f1e4dcabdfa050d9f18e3dfdd4bd70b55a024f3beab45fecb6e255", + tweaked_sig: "29abdd275d062b0bef51113fabaf3a70652387e7fa227c54b968b4943a7f4312d9af6a8352010582fb800dfecd51276d4b78ff134a9b28def1e45efb662e8adb" + }, + %{ + privkey: 0x24D807AB16645D504C75AA9B6355444FC291298AABEB4DAC5EB070898AD645B0, + pubkey: 0xf7609d85839d360fdba6a7bcde848807baeb6417d5ed4258c1c8bc4650543c7a, + tweak_secret: 0x4ABE441BF15DBB0378C2763FCA1E967EFA2636C6292A80711DA55AC7810CAA2A, + tweak_point: 0x6690f7703df1a815d8b4391889c47d8d8837369c6f2e00d5eb92abf1b90a206f, + message_hash: 0x9AE6F80954DA7A3D615987FC0CE97CC4E4567F5425A733DBD4C0BD496A3D5622, + aux_rand: 0xA421B1D6C0DA447395A19B0CF5BBB9879DAE8303CFD33C46F001DBE7EFE9853, + untweaked_sig: "96b4d6d0a19e7f42eea9ee4ad8d36a19e039623eeea47a6fb1658df2e20f7e23cbc02c5c59d7961cf88402a346584af229cb795295c5ea108440bd7de5ea01f0", + tweaked_sig: "a57c39c39b18fd5e6f572253e19227252a4a81a4ac3dc1ef804e6b306b142b10167e70784b355120714678e31076e1726942d3320fa7ca45e213b9b896c06ad9" + }, + %{ + privkey: 0x7EDA1030020279FA24CC33B79A665B73A9E715EB1EB2C6DBEBEE21AD7BCF1E2C, + pubkey: 0x65c57e92bd052d623ac708c34a9a73851e37c81cb763b9fe1ff88437ce42245f, + tweak_secret: 0x1DE275F6F723416882A18E0F4CCD398B7AA9C2A701BFD16D448A118BF82D3027, + tweak_point: 0xcbc6ed86f4442c6403d5c0a3b8157489c5cc55d5137bbd4648c58301979b6f6c, + message_hash: 0xA744D50F65508DDE9278AECAD6A2759BE3A83709877B427E4098085DB27B9826, + aux_rand: 0x12F582D5FA0B2F6245985FBC7C7220C55D441D027B64BA2A7289B9A15E5106F7, + untweaked_sig: "454d9f3fe892e1f31638a258b135ddb74beaed682936830b1d233432e7a2a575d9953241a92d0172bf8df6e198dc7a9052638337483e0881ccb96ba38ea973d3", + tweaked_sig: "db076ae6615ccd083a0d8e6d9e78c28cba696f4c4d3fbfb5c5a55b23d4de0d0af777a838a05042db422f84f0e5a9b41bcd0d45de49fdd9ef11437d2f86d6a3fa" + }, + %{ + privkey: 0x95896701D713F8AA383176C2BCD61F8EC55A29C5169AEBE9A8A32AC9803F8E7D, + pubkey: 0xe7d2b5e311e4f7e81d05c5c56a94f0e5438da78b2e512f78b540114dcf931080, + tweak_secret: 0x7038F53CA5C13C1534A595BC74799657805CB864A21AC78618E661E2A27F6DE4, + tweak_point: 0xe76f186a03ef4ca41b55ff18e2de2f713cf9a1f97f23f627681a2a47bea24a95, + message_hash: 0x4928F4C94434C570F12CF548F4B2F4C9124F5E6D1371020D55EC3D508DA49D61, + aux_rand: 0x51DDDD197BF29EE74235C80CECBE06C8E012BFFC0E227E87A966AD9BB6E6A07B, + untweaked_sig: "46d4e627dc2a8d3864f4e6738f88ab891e5e51da7b08251218d11b0f56f047b4d91271d08e0ba2ecde0169ce02307e49bca6cc749e3c6869dad5ace39f09c943", + tweaked_sig: "83ba54b75d4516cb376b51fc8fb9cd560aae652a2ff8cded96cb052ed3b1e12e494b670d33ccdf0212a6ff8a76aa14a28254a7f2910e8fb433e9b0397152f5e6" + }, + %{ + privkey: 0x695563958BE07A20700F6B624C2EBA9F336C47ADF7B8E202388600132D2E7CF3, + pubkey: 0x97826ac90a2e8ebf9c2f94c5f23863aed915b4c4e77701b9ef7b43e9c501d0f3, + tweak_secret: 0x6FDDBC57B407184C1D1C0B9040A14A19EF8824222431F39CB89FED2C9D67CE41, + tweak_point: 0xe772ca746c6f40e607167f6d95414dab603fb36e0a4e473ab69d6e1036c209ea, + message_hash: 0x9A6BBFEF82261C8EFA9CD7F3481728D68554B2E698187797EAF60A734B6307D9, + aux_rand: 0x1ED0360C5241E28038735B43C6705AE2A1A971CDD94FCED6817C005ED2DF8237, + untweaked_sig: "d44e4a61254b90b671a278ee8ae73add71c5cb89b9fda0611cabb3ab46c4bb5d2511324a297a4027f3bef12a79e07a16d1419922e0d6c205066b538efc655dfe", + tweaked_sig: "840a95a2d67b1fe09584a2b9f353277c318d4b21774a777a2dd2814de4e9d22094eeeea1dd81587410dafcbaba81c430c0c9bd450508b5a1bf0b40bb99cd2c3f" + } + ] + + describe "adaptor signature tests" do + test "test adaptor signatures" do + for t <- @schnorr_adaptor_signatures do + {:ok, sk} = PrivateKey.new(t.privkey) + sk = Secp256k1.force_even_y(sk) + pk = PrivateKey.to_point(sk) + # check pubkey is correct + assert t.pubkey == pk.x + + {:ok, tw} = PrivateKey.new(t.tweak_secret) + tw = Secp256k1.force_even_y(tw) + tw_point = PrivateKey.to_point(tw) + # check tweak point is correct + assert t.tweak_point == tw_point.x + + # parse sigs + {:ok, c_ut_sig} = Signature.parse_signature(t.untweaked_sig) + {:ok, c_tw_sig} = Signature.parse_signature(t.tweaked_sig) + + {:ok, ut_sig, t_point} = Schnorr.sign_for_tweak(sk, t.message_hash, t.aux_rand, tw_point) + # check tweak point hasn't changed + assert t_point == tw_point + # check untweaked sig + assert c_ut_sig == ut_sig + # adaptor sig is not a valid schnorr sig, must fail + assert !Schnorr.verify_signature(pk, t.message_hash, ut_sig) + # verify adaptor signature + assert Schnorr.verify_untweaked_signature(pk, t.message_hash, ut_sig, tw_point) + # complete adaptor sig + tw_sig = Schnorr.tweak_signature(ut_sig, tw) + # check sig + assert c_tw_sig == tw_sig + # ensure tweaked sig is same when using tweak private key and integer + tw_sig0 = Schnorr.tweak_signature(ut_sig, tw.d) + assert tw_sig == tw_sig0 + + # complete sig must be valid schnorr sig + assert Schnorr.verify_signature(pk, t.message_hash, tw_sig) + + # extract tweak secret + {:ok, tweak} = Schnorr.extract_tweak(pk, t.message_hash, ut_sig, tw_sig) + assert tweak == tw.d + assert t.tweak_secret == tweak + + # extract signature given tweak + {:ok, sig} = Schnorr.extract_tweaked_signature(pk, t.message_hash, ut_sig, t.tweak_secret) + assert sig == tw_sig + end + end + end +end diff --git a/test/secp256k1/schnorr_test.exs b/test/secp256k1/schnorr_test.exs index d5aa295..522d940 100644 --- a/test/secp256k1/schnorr_test.exs +++ b/test/secp256k1/schnorr_test.exs @@ -5,6 +5,7 @@ defmodule Bitcoinex.Secp256k1.SchnorrTest do alias Bitcoinex.Utils alias Bitcoinex.Secp256k1 alias Bitcoinex.Secp256k1.{Point, PrivateKey, Schnorr, Signature} + # alias Bitcoinex.Secp256k1.{PrivateKey} # BIP340 official test vectors: # https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv @@ -153,6 +154,9 @@ defmodule Bitcoinex.Secp256k1.SchnorrTest do } ] + + + describe "sign/3" do test "sign" do for t <- @schnorr_signatures_with_secrets do @@ -260,7 +264,7 @@ defmodule Bitcoinex.Secp256k1.SchnorrTest do for _ <- 1..1000 do secret = - 32 + 31 |> :crypto.strong_rand_bytes() |> :binary.decode_unsigned() @@ -271,4 +275,6 @@ defmodule Bitcoinex.Secp256k1.SchnorrTest do end end end + + end diff --git a/test/secp256k1/secp256k1_test.exs b/test/secp256k1/secp256k1_test.exs index a723d01..d03547d 100644 --- a/test/secp256k1/secp256k1_test.exs +++ b/test/secp256k1/secp256k1_test.exs @@ -3,7 +3,7 @@ defmodule Bitcoinex.Secp256k1.Secp256k1Test do doctest Bitcoinex.Secp256k1 alias Bitcoinex.Secp256k1 - alias Bitcoinex.Secp256k1.{Signature} + alias Bitcoinex.Secp256k1.{Signature, PrivateKey} @valid_der_signatures [ %{ @@ -165,4 +165,19 @@ defmodule Bitcoinex.Secp256k1.Secp256k1Test do end end end + + describe "force_even_y/1" do + test "force_even_y" do + for _ <- 1..1000 do + secret = + 31 + |> :crypto.strong_rand_bytes() + |> :binary.decode_unsigned() + + privkey = Secp256k1.force_even_y(%PrivateKey{d: secret}) + pubkey = PrivateKey.to_point(privkey) + assert rem(pubkey.y,2) == 0 + end + end + end end From 05f5edd06dc9ff0416a4ba57f69386fd93e0112a Mon Sep 17 00:00:00 2001 From: Sachin Meier Date: Fri, 27 Jan 2023 22:43:31 -0800 Subject: [PATCH 07/16] temp fix adaptors --- .tool-versions | 4 +- lib/secp256k1/schnorr.ex | 94 +++++++++++++++++++++++++--------------- 2 files changed, 60 insertions(+), 38 deletions(-) diff --git a/.tool-versions b/.tool-versions index 7ffb442..440d747 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -elixir 1.10.4-otp-22 -erlang 22.3.4.1 +elixir 1.14.2-otp-25 +erlang 25.1.2 diff --git a/lib/secp256k1/schnorr.ex b/lib/secp256k1/schnorr.ex index 513d561..5dfa7d2 100644 --- a/lib/secp256k1/schnorr.ex +++ b/lib/secp256k1/schnorr.ex @@ -40,7 +40,7 @@ defmodule Bitcoinex.Secp256k1.Schnorr do end @spec sign_for_tweak(PrivateKey.t(), non_neg_integer, non_neg_integer, Point.t()) :: - {:ok, Signature.t(), Point.t()} | {:error, String.t()} + {:ok, Signature.t(), Point.t(), bool} | {:error, String.t()} def sign_for_tweak(privkey, z, aux, tweak_point) do case PrivateKey.validate(privkey) do {:error, msg} -> @@ -63,29 +63,19 @@ defmodule Bitcoinex.Secp256k1.Schnorr do if k0.d == 0 do {:error, "invalid aux randomness"} - else + + r_point = PrivateKey.to_point(k0) tweaked_r_point = Math.add(r_point, tweak_point) - IO.puts("!---\n") - IO.puts("k0: " <> to_string(k0.d)) - IO.puts("x: " <> to_string(tweaked_r_point.x) <> "\ny: " <> to_string(tweaked_r_point.y) <> "\n") # we must ensure that R+T, the final signature's nonce point has even y - k = get_k_for_even_tweaked_nonce(k0, tweaked_r_point) - IO.puts("k1: " <> to_string(k.d)) + {k, was_negated} = get_k_for_even_tweaked_nonce(k0, tweaked_r_point) r_point = PrivateKey.to_point(k) tweaked_r_point = Math.add(r_point, tweak_point) - IO.puts("x: " <> to_string(tweaked_r_point.x) <> "\ny: " <> to_string(tweaked_r_point.y) <> "\n") - IO.puts("---!\n") - if Point.has_even_y(tweaked_r_point) do - sameK = to_string(k == k0) - {:error, "what is going on: sameK: #{sameK}"} - else - e = calculate_e(Point.x_bytes(tweaked_r_point), Point.x_bytes(d_point), z_bytes) - sig_s = calculate_s(k, d, e) - - {:ok, %Signature{r: r_point.x, s: sig_s}, tweak_point} - end + e = calculate_e(Point.x_bytes(tweaked_r_point), Point.x_bytes(d_point), z_bytes) + sig_s = calculate_s(k, d, e) + + {:ok, %Signature{r: r_point.x, s: sig_s}, tweak_point, was_negated} end end end @@ -106,13 +96,15 @@ defmodule Bitcoinex.Secp256k1.Schnorr do |> Math.modulo(@n) end + # returns new_k, was_negated defp get_k_for_even_tweaked_nonce(k, tweaked_point) do if Point.has_even_y(tweaked_point) do IO.puts("k unchanged\n") - k - else + {k, false} + + IO.puts("k changed\n") - PrivateKey.negate(k) + {PrivateKey.negate(k), true} end end @@ -125,7 +117,8 @@ defmodule Bitcoinex.Secp256k1.Schnorr do if k0.d == 0 do {:error, "invalid aux randomness"} - else + + {:ok, Secp256k1.force_even_y(k0)} end end @@ -165,35 +158,61 @@ defmodule Bitcoinex.Secp256k1.Schnorr do validate_r(r_point, r) end - @spec verify_untweaked_signature(Point.t(), non_neg_integer, Signature.t(), Point.t()) :: + @spec verify_untweaked_signature(Point.t(), non_neg_integer, Signature.t(), Point.t(), boolean) :: boolean | {:error, String.t()} - def verify_untweaked_signature(pubkey, z, %Signature{r: r, s: s}, tweak_point) do - if r >= @p || s >= @n, do: {:error, "invalid signature"} + def verify_untweaked_signature(_, _, %Signature{r: r, s: s}, _, _) when r >= @p || s >= @n, do: {:error, "invalid signature"} + def verify_untweaked_signature(pubkey, z, %Signature{r: r, s: s}, tweak_point, was_negated) do + z_bytes = Utils.int_to_big(z, 32) case Point.lift_x(r) do {:error, err} -> {:error, err} {:ok, given_r_point} -> - tweaked_point = Math.add(given_r_point, tweak_point) - z_bytes = Utils.int_to_big(z, 32) - e = calculate_e(Point.x_bytes(tweaked_point), Point.x_bytes(pubkey), z_bytes) - - r_point = calculate_r(pubkey, s, e) + case get_even_tweaked_point(given_r_point, tweak_point, was_negated) do + {:error, err} -> + {:error, err} + + {:ok, tweaked_point} -> + e = calculate_e(Point.x_bytes(tweaked_point), Point.x_bytes(pubkey), z_bytes) + r_point = calculate_r(pubkey, s, e) + validate_r(r_point, r) + end + end + end - validate_r(r_point, r) + # bool is was_negated. + defp get_even_tweaked_point(r_point, tweak_point, false) do + Math.add(given_r_point, tweak_point) + end + defp get_even_tweaked_point(r_point, tweak_point, true) do + # force tweaked_point to have even y + tweaked_point = get_even_tweaked_point(r_point, tweak_point, false) + case Point.lift_x(tweaked_point.x) do + {:ok, tweaked_point} -> {:ok, tweaked_point} + {:error, err} -> {:error, err} end end @spec tweak_signature(Signature.t(), non_neg_integer | PrivateKey.t()) :: Signature.t() def tweak_signature(sig, t = %PrivateKey{}), do: tweak_signature(sig, t.d) - def tweak_signature(%Signature{r: r, s: s}, tweak) do - {:ok, t} = PrivateKey.new(tweak) + def tweak_signature(%Signature{r: r, s: s}, tweak, was_negated) do + t = get_even_tweak(tweak, was_negated) t_point = PrivateKey.to_point(t) {:ok, r_point} = Point.lift_x(r) tw_s = Math.modulo(tweak+s, @n) %Signature{r: Math.add(r_point, t_point).x, s: tw_s} end + def get_even_tweak(tweak, false) do + {:ok, t} = PrivateKey.new(tweak) + t + end + def get_even_tweak(tweak, true) do + tweak + |> get_even_tweak(false) + |> PrivateKey.negate() + end + @doc """ extract_tweak takes a signer pubkey, message hash z, untweaked signature (adaptor signature), and a completed signature, verifies the signature, and then returns the revealed tweak secret @@ -204,10 +223,12 @@ defmodule Bitcoinex.Secp256k1.Schnorr do if verify_signature(pubkey, z, tweaked_sig) do if tweaked_s < s do {:error, "invalid tweak"} - else + + {:ok, tweaked_s - s} end - else + + {:error, "failed to extract tweak due to invalid signature"} end end @@ -241,7 +262,8 @@ defmodule Bitcoinex.Secp256k1.Schnorr do if verify_signature(pubkey, z, tweaked_sig) do {:ok, %Signature{r: tweaked_r_point.x, s: tweaked_s}} - else + + {:error, "tweak does not produce valid signature"} end end From b7d57390805c3fe43b3f837a1e6f7c4654e7317b Mon Sep 17 00:00:00 2001 From: Sachin Meier Date: Sun, 29 Jan 2023 22:54:55 -0800 Subject: [PATCH 08/16] fix schnorr adaptor signatures --- lib/secp256k1/point.ex | 10 ++ lib/secp256k1/schnorr.ex | 260 +++++++++++++----------------- test/secp256k1/adaptor_test.exs | 151 ----------------- test/secp256k1/schnorr_test.exs | 63 +++++++- test/secp256k1/secp256k1_test.exs | 2 +- 5 files changed, 183 insertions(+), 303 deletions(-) delete mode 100644 test/secp256k1/adaptor_test.exs diff --git a/lib/secp256k1/point.ex b/lib/secp256k1/point.ex index 8793347..50259bd 100644 --- a/lib/secp256k1/point.ex +++ b/lib/secp256k1/point.ex @@ -105,6 +105,16 @@ defmodule Bitcoinex.Secp256k1.Point do end end + @doc """ + negate returns the pubkey with the same x but the other y. + It does this by passing y % 2 == 0 as y_is_odd to Secp256k1.get_y. + """ + @spec negate(t()) :: t() + def negate(%__MODULE__{x: x, y: y}) do + {:ok, y} = Secp256k1.get_y(x, (y &&& 1) == 0) + %__MODULE__{x: x, y: y} + end + @doc """ sec serializes a compressed public key to binary """ diff --git a/lib/secp256k1/schnorr.ex b/lib/secp256k1/schnorr.ex index 5de2132..7e612cc 100644 --- a/lib/secp256k1/schnorr.ex +++ b/lib/secp256k1/schnorr.ex @@ -36,6 +36,7 @@ defmodule Bitcoinex.Secp256k1.Schnorr do t = Utils.xor_bytes(d_bytes, tagged_aux_hash) {:ok, k0} = calculate_k(t, d_point, z_bytes) + if k0.d == 0 do {:error, "invalid aux randomness"} else @@ -46,8 +47,8 @@ defmodule Bitcoinex.Secp256k1.Schnorr do {:error, msg} k -> - e = calculate_e(Point.x_bytes(r_point) <> Point.x_bytes(d_point) <> z_bytes) - sig_s = calculate_s(k,d,e) + e = calculate_e(Point.x_bytes(r_point), Point.x_bytes(d_point), z_bytes) + sig_s = calculate_s(k, d, e) {:ok, %Signature{r: r_point.x, s: sig_s}} end @@ -56,51 +57,10 @@ defmodule Bitcoinex.Secp256k1.Schnorr do end end - @spec sign_for_tweak(PrivateKey.t(), non_neg_integer, non_neg_integer, Point.t()) :: - {:ok, Signature.t(), Point.t(), bool} | {:error, String.t()} - def sign_for_tweak(privkey, z, aux, tweak_point) do - case PrivateKey.validate(privkey) do - {:error, msg} -> - {:error, msg} - - {:ok, privkey} -> - z_bytes = Utils.int_to_big(z, 32) - aux_bytes = Utils.int_to_big(aux, 32) - d_point = PrivateKey.to_point(privkey) - d = Secp256k1.force_even_y(privkey) - d_bytes = Utils.int_to_big(d.d, 32) - tagged_aux_hash = tagged_hash_aux(aux_bytes) - t = Utils.xor_bytes(d_bytes, tagged_aux_hash) - - {:ok, k0} = - tagged_hash_nonce(t <> Point.x_bytes(d_point) <> z_bytes) - |> :binary.decode_unsigned() - |> Math.modulo(@n) - |> PrivateKey.new() - - if k0.d == 0 do - {:error, "invalid aux randomness"} - else - r_point = PrivateKey.to_point(k0) - tweaked_r_point = Math.add(r_point, tweak_point) - # we must ensure that R+T, the final signature's nonce point has even y - {k, was_negated} = get_k_for_even_tweaked_nonce(k0, tweaked_r_point) - r_point = PrivateKey.to_point(k) - tweaked_r_point = Math.add(r_point, tweak_point) - - e = calculate_e(Point.x_bytes(tweaked_r_point), Point.x_bytes(d_point), z_bytes) - sig_s = calculate_s(k, d, e) - - {:ok, %Signature{r: r_point.x, s: sig_s}, tweak_point, was_negated} - end - end - end - defp tagged_hash_aux(aux), do: Utils.tagged_hash("BIP0340/aux", aux) defp tagged_hash_nonce(nonce), do: Utils.tagged_hash("BIP0340/nonce", nonce) defp tagged_hash_challenge(chal), do: Utils.tagged_hash("BIP0340/challenge", chal) - defp calculate_r(pubkey, s, e) do @generator_point |> Math.multiply(s) @@ -112,17 +72,6 @@ defmodule Bitcoinex.Secp256k1.Schnorr do |> Math.modulo(@n) end - # returns new_k, was_negated - defp get_k_for_even_tweaked_nonce(k, tweaked_point) do - if Point.has_even_y(tweaked_point) do - IO.puts("k unchanged\n") - {k, false} - else - IO.puts("k changed\n") - {PrivateKey.negate(k), true} - end - end - defp calculate_k(t, d_point, z_bytes) do {:ok, k0} = tagged_hash_nonce(t <> Point.x_bytes(d_point) <> z_bytes) @@ -143,20 +92,39 @@ defmodule Bitcoinex.Secp256k1.Schnorr do |> Math.modulo(@n) end - defp validate_r(r_point, rx) do + defp partial_validate_r(r_point, rx) do cond do Point.is_inf(r_point) -> {:error, "R point is infinite"} - !Point.has_even_y(r_point) -> - {:error, "R point is not even"} + r_point.x != rx -> {:error, "x's do not match #{r_point.x} vs #{rx}"} - true -> true + # TODO ensure encrypted signatures are allwoed to have odd Y values + # !Point.has_even_y(r_point) -> + # {:error, "R point is not even"} + true -> + true + end + end + + defp validate_r(r_point, rx) do + cond do + Point.is_inf(r_point) -> + # {:error, "R point is infinite"} + false + !Point.has_even_y(r_point) -> + # {:error, "R point is not even"} + false + r_point.x != rx -> + # {:error, "x's do not match #{r_point.x} vs #{rx}"} + false + true -> + true end end @doc """ - verify whether the schnorr signature is valid for the given message hash and public key + verify_signature verifies whether the Schnorr signature is valid for the given message hash and public key """ @spec verify_signature(Point.t(), non_neg_integer, Signature.t()) :: boolean | {:error, String.t()} @@ -174,113 +142,105 @@ defmodule Bitcoinex.Secp256k1.Schnorr do validate_r(r_point, r) end - @spec verify_untweaked_signature(Point.t(), non_neg_integer, Signature.t(), Point.t(), boolean) :: - boolean | {:error, String.t()} - def verify_untweaked_signature(_, _, %Signature{r: r, s: s}, _, _) when r >= @p || s >= @n, do: {:error, "invalid signature"} - def verify_untweaked_signature(pubkey, z, %Signature{r: r, s: s}, tweak_point, was_negated) do + # negate a secret + defp conditional_negate(d, true), do: %PrivateKey{d: d} |> PrivateKey.negate() + defp conditional_negate(d, false), do: %PrivateKey{d: d} + + # negate a point (switches parity of P.y) + defp conditional_negate_point(point, true), do: Point.negate(point) + defp conditional_negate_point(point, false), do: point + + # Adaptor/Encrypted Signatures + + @doc """ + encrypted_sign signs a message hash z with Private Key sk but encrypts the signature using the tweak_point + as the encryption key. The signer need not know the decryption key / tweak itself, which can later be used + to decrypt the signature into a valid Schnorr signature. This produces an Adaptor Signature. + """ + @spec encrypted_sign(PrivateKey.t(), non_neg_integer(), non_neg_integer(), Point.t()) :: {:ok, Signature.t(), boolean} + def encrypted_sign(sk = %PrivateKey{}, z, aux, tweak_point = %Point{}) do z_bytes = Utils.int_to_big(z, 32) - case Point.lift_x(r) do - {:error, err} -> - {:error, err} - - {:ok, given_r_point} -> - case get_even_tweaked_point(given_r_point, tweak_point, was_negated) do - {:error, err} -> - {:error, err} - - {:ok, tweaked_point} -> - e = calculate_e(Point.x_bytes(tweaked_point), Point.x_bytes(pubkey), z_bytes) - r_point = calculate_r(pubkey, s, e) - validate_r(r_point, r) - end - end - end + aux_bytes = Utils.int_to_big(aux, 32) + d_point = PrivateKey.to_point(sk) - # bool is was_negated. - defp get_even_tweaked_point(r_point, tweak_point, false) do - Math.add(given_r_point, tweak_point) - end - defp get_even_tweaked_point(r_point, tweak_point, true) do - # force tweaked_point to have even y - tweaked_point = get_even_tweaked_point(r_point, tweak_point, false) - case Point.lift_x(tweaked_point.x) do - {:ok, tweaked_point} -> {:ok, tweaked_point} - {:error, err} -> {:error, err} - end - end + d = Secp256k1.force_even_y(sk) + d_bytes = Utils.int_to_big(d.d, 32) + tagged_aux_hash = tagged_hash_aux(aux_bytes) + t = Utils.xor_bytes(d_bytes, tagged_aux_hash) + # TODO always add tweak_point to the nonce to commit to it as well + {:ok, k0} = calculate_k(t, d_point, z_bytes) - @spec tweak_signature(Signature.t(), non_neg_integer | PrivateKey.t()) :: Signature.t() - def tweak_signature(sig, t = %PrivateKey{}), do: tweak_signature(sig, t.d) - def tweak_signature(%Signature{r: r, s: s}, tweak, was_negated) do - t = get_even_tweak(tweak, was_negated) - t_point = PrivateKey.to_point(t) - {:ok, r_point} = Point.lift_x(r) - tw_s = Math.modulo(tweak+s, @n) - %Signature{r: Math.add(r_point, t_point).x, s: tw_s} - end + r_point = PrivateKey.to_point(k0) + tweaked_r_point = Math.add(r_point, tweak_point) + # ensure (R+T).y is even, if not, negate it, negate k, and set was_negated = true + {tweaked_r_point, was_negated} = make_point_even(tweaked_r_point) + k = conditional_negate(k0.d, was_negated) - def get_even_tweak(tweak, false) do - {:ok, t} = PrivateKey.new(tweak) - t - end - def get_even_tweak(tweak, true) do - tweak - |> get_even_tweak(false) - |> PrivateKey.negate() + e = calculate_e(Point.x_bytes(tweaked_r_point), Point.x_bytes(d_point), z_bytes) + s = calculate_s(k, d, e) + # we return Signature{R+T,s}, not a valid signature since s is untweaked. + {:ok, %Signature{r: tweaked_r_point.x, s: s}, was_negated} end @doc """ - extract_tweak takes a signer pubkey, message hash z, untweaked signature (adaptor signature), - and a completed signature, verifies the signature, and then returns the revealed tweak secret + verify_encrypted_signature verifies that an encrypted signature commits to a tweak_point / encryption key. + This is different from a regular Schnorr signature verification, as encrypted signatures are not valid Schnorr Signatures. """ - @spec extract_tweak(Point.t(), non_neg_integer, Signature.t(), Signature.t()) :: - {:ok, non_neg_integer} | {:error, String.t()} - def extract_tweak(pubkey, z, %Signature{s: s}, tweaked_sig = %Signature{s: tweaked_s}) do - if verify_signature(pubkey, z, tweaked_sig) do - if tweaked_s < s do - {:error, "invalid tweak"} + @spec verify_encrypted_signature(Signature.t(), Point.t(), non_neg_integer(), Point.t(), boolean) :: boolean + def verify_encrypted_signature( + %Signature{r: tweaked_r, s: s}, + pk = %Point{}, + z, + tweak_point = %Point{}, + was_negated + ) do + z_bytes = Utils.int_to_big(z, 32) - else - {:ok, tweaked_s - s} - end + {:ok, tweaked_r_point} = Point.lift_x(tweaked_r) + # This is subtracting the tweak_point (T) from the tweaked_point (R + T) to get the original R + tweak_point = conditional_negate_point(tweak_point, !was_negated) + r_point = Math.add(tweaked_r_point, tweak_point) + + e = calculate_e(Point.x_bytes(tweaked_r_point), Point.x_bytes(pk), z_bytes) + r_point2 = calculate_r(pk, s, e) + partial_validate_r(r_point, r_point2.x) + end + defp make_point_even(point) do + if Point.has_even_y(point) do + {point, false} else - {:error, "failed to extract tweak due to invalid signature"} + {Point.negate(point), true} end end @doc """ - extract_tweaked_signature takes a signer pubkey, message hash z, untweaked signature (adaptor signature), - and a tweak secret, and uses it to verify the adaptor signature, and returns the complete signature + decrypt_signature uses the tweak/decryption key to transform an + adaptor/encrypted signature into a final, valid Schnorr signature. """ - @spec extract_tweaked_signature( - Point.t(), - non_neg_integer, - Signature.t(), - non_neg_integer | PrivateKey.t() - ) :: - {:ok, Signature.t()} | {:error, String.t()} - def extract_tweaked_signature(pubkey, z, sig, t = %PrivateKey{}), - do: extract_tweaked_signature(pubkey, z, sig, t.d) - - def extract_tweaked_signature(pubkey, z, %Signature{r: r, s: s}, tweak) do - case Point.lift_x(r) do - {:error, err} -> - {:error, err} - - {:ok, r_point} -> - tweaked_s = tweak + s - - tweak_point = PrivateKey.to_point(tweak) - tweaked_r_point = Math.add(r_point, tweak_point) - - tweaked_sig = %Signature{r: tweaked_r_point.x, s: tweaked_s} + @spec decrypt_signature(Signature.t(), PrivateKey.t(), boolean) :: Signature.t() + def decrypt_signature(%Signature{r: r, s: s}, tweak, was_negated) do + tweak = conditional_negate(tweak, was_negated) + final_s = Math.modulo(tweak.d + s, @n) + %Signature{r: r, s: final_s} + end - if verify_signature(pubkey, z, tweaked_sig) do - {:ok, %Signature{r: tweaked_r_point.x, s: tweaked_s}} - else - {:error, "tweak does not produce valid signature"} - end - end + @doc """ + recover_decryption_key recovers the tweak or decryption key by + subtracting final_sig.s - encrypted_sig.s (mod n). The tweak is + negated if the original R+T point was negated during signing. + """ + @spec recover_decryption_key(Signature.t(), Signature.t(), boolean) :: + PrivateKey.t() | {:error, String.t()} + def recover_decryption_key(%Signature{r: enc_r}, %Signature{r: r}, _, _) when enc_r != r, + do: {:error, "invalid signature pair"} + + def recover_decryption_key( + _encrypted_sig = %Signature{s: enc_s}, + _sig = %Signature{s: s}, + was_negated + ) do + t = Math.modulo(s - enc_s, @n) + conditional_negate(t, was_negated) end end diff --git a/test/secp256k1/adaptor_test.exs b/test/secp256k1/adaptor_test.exs deleted file mode 100644 index 127cc6b..0000000 --- a/test/secp256k1/adaptor_test.exs +++ /dev/null @@ -1,151 +0,0 @@ -defmodule Bitcoinex.Secp256k1.SchnorrTest do - use ExUnit.Case - - alias Bitcoinex.Secp256k1 - alias Bitcoinex.Secp256k1.{PrivateKey, Schnorr, Signature} - - - @schnorr_adaptor_signatures [ - %{ - privkey: 0x279D71D68D3EE997019D005BDF703C271001631A7EE12E4C9DAD10C0754912DC, - pubkey: 0x22c63594ea2c2199e0500cdf6dffecdf878441720789c8dfcfb9af06a96fd1e4, - tweak_secret: 0xF8EBFDF85A3AF0C337ECB165EF47D565DE15CBCEEB597A243C3D54DF49B703D5, - tweak_point: 0x6545e169e4d2e940e63207110a9d44dd5d4ca65aeb58e3e566658f62d41bd23f, - message_hash: 0x5736367EBB12EDC15B0FA75319B46D016F86A0E057B9237240D6185C93596367, - aux_rand: 0x7E4E37835DDFC6A82A011073DCB779D02F1F5B52A2937B6ADD5B9DA2528FC5C6, - untweaked_sig: "e2125e2f6d791ce59b604dfc0578a823008a5c86f2f2efbd0de68a4cb19688d817ebac918f08e0078c66a26c664d9f169d66dc54fdd95972e68a69b79797274a", - tweaked_sig: "320ab814c2e7e2567af8e738ce83e9fdc55ef57933dd52b169bba46fd3516e4c10d7aa89e943d0cac45353d25595747dc0cdcb3d39ea335b62f5600a1117e9de" - }, - %{ - privkey: 0x6F0B8C685BEB787F3121851AC13DAD9549C235217C5F578D9E7BE3B3845D8773, - pubkey: 0x825fa6ec886022247b7b98d24d47a6f03d3a47f2fa3916a244eba7114ea94fa1, - tweak_secret: 0x76D2C1FE7E575063C8888D935645973AC3AE1FFD1C97F25D14F7567657D7F748, - tweak_point: 0xb99378d7cf782cf6ff265ef8e3ba447b36da89224c0682ca61cc9916a4fec4bf, - message_hash: 0xA193926FAFF8CE363937D8655892E60D45661C5FC510BC711E4B5049A6B4DD30, - aux_rand: 0x76C66B394932DC5704EC0FB4CE4D76D388707396AC64D9C35BFC081F11D51124, - untweaked_sig: "864897cd95c22dda7fce0435fe365c658118e52d578ba77c25d1f1e63ff3cd0916b38c325759c310e467a0ec026d8362b2f5155adb19a17eb06a5c661b039be1", - tweaked_sig: "e277ad505af9e82abcb122c3df410459494af86315988d4596bf0fb71f64bf338d864e30d5b11374acf02e7f58b31a9d76a33557f7b193dbc561b2dc72db9329" - }, - %{ - privkey: 0x624FB68C6C2474358EF57A9810BA5A17855D0FF8676D5D80E93DF314D5C3D277, - pubkey: 0x4af83421073f24e51235c43ffb85aa94dc3139af958a2532c6b412832fbbacc4, - tweak_secret: 0xF1997F60060268C14B81744869AEB04098A93704C3BDA2A640A1B26F74E246F4, - tweak_point: 0xb9dc16baaf262b3a60a1979833f9ca350e2219121fdb43d3fd2045d3142bb71f, - message_hash: 0x97FF15503ECA5167E871E04998F3C90976E8FAFF9AD1AF54F045153BA41E1663, - aux_rand: 0x71C1FE1F3A27B04EEC4142AC1612A7C6419B1740716E2C29CEE319E718950A04, - untweaked_sig: "08365373469000bca3fa28a95469f6331a20d4f03429372573b3c2d3d3d5b0f6801f053ece6e34cdf9fa8f43ec2ed3b730c388c0e444dd45f9cc1f920adac25f", - tweaked_sig: "9e3deb23f27730ce1be854b0145cc9bf99e3ee10491d28308160c7b65b0a56e871b8849ed4709d8f457c038c55dd83f90ebde2def8b9dfb07a9b7374af86c812" - }, - %{ - privkey: 0xB5196100AAC42311C9B33B1746CA0B38AC4547936DDC4AFFF547A862A4BF9890, - pubkey: 0x5b279f79dcf2414b66160515de53ac471a043d6d9f62fd007c09ad90704ffd28, - tweak_secret: 0xA69690763B4034FCF0F269E80464643E521475BFE08162632971AE0BDAA7AE8E, - tweak_point: 0xe9f219bb7c0a390a7df96a4f33b7f7d6179a9a4aa216ed1a750ddba1719072f0, - message_hash: 0xAD6F06747576C8EC210113A2C070A7A5688616EE841FA61026D2FF1CF9F7970F, - aux_rand: 0x63241AA5C78B99A849025EB3056E84DB011769DC8BBE782FAFA6E89D2BEE0C16, - untweaked_sig: "ef3766f37e9235e9652418bd60c54cd8e9c2db06f7effc542415fc76fac9c5cb75cc36cc9d5c20d303c30e43c2be82708141b8f721f75c7f889ebb24da81c818", - tweaked_sig: "aa9788a104f497f788bf4979a791a04c8408fd2f82075d5810f5f85394d567761c62c742d89c55cff4b5782bc722e6b018a751d053301ea6f23e0aa3e4f33565" - }, - %{ - privkey: 0x45AC71188B8B3B180D806ABB0289AB23357BF7CE866BFDB13509C68BAC5C88C4, - pubkey: 0x8b688d7ce32ab13bed3f0c5f119791f8b76913809c22983c8b6713d6fdc6f5a3, - tweak_secret: 0x6D2B06A08C0F20A64FA06DADF35F992F4DA441A29541268FB5F9AA9B7977A886, - tweak_point: 0x4d01a75b79ad34ce9d1dec59c581881db6a05865807705558a31e5b19047b024, - message_hash: 0x1E3D05CC94304EB6CEF384D5ADF36827A6CA4299D70B821B60240995F75BB6CB, - aux_rand: 0x7EFC5AD98D6AA0542A9534FA8637AB87A1B8E48FBAFD89AC6EA9348B44EEA049, - untweaked_sig: "655244da978cb1fa37690f3fe98c0e78b58390207781dae0d57f74dde06c0c3f6c8463e2c5f1e4dcabdfa050d9f18e3dfdd4bd70b55a024f3beab45fecb6e255", - tweaked_sig: "29abdd275d062b0bef51113fabaf3a70652387e7fa227c54b968b4943a7f4312d9af6a8352010582fb800dfecd51276d4b78ff134a9b28def1e45efb662e8adb" - }, - %{ - privkey: 0x24D807AB16645D504C75AA9B6355444FC291298AABEB4DAC5EB070898AD645B0, - pubkey: 0xf7609d85839d360fdba6a7bcde848807baeb6417d5ed4258c1c8bc4650543c7a, - tweak_secret: 0x4ABE441BF15DBB0378C2763FCA1E967EFA2636C6292A80711DA55AC7810CAA2A, - tweak_point: 0x6690f7703df1a815d8b4391889c47d8d8837369c6f2e00d5eb92abf1b90a206f, - message_hash: 0x9AE6F80954DA7A3D615987FC0CE97CC4E4567F5425A733DBD4C0BD496A3D5622, - aux_rand: 0xA421B1D6C0DA447395A19B0CF5BBB9879DAE8303CFD33C46F001DBE7EFE9853, - untweaked_sig: "96b4d6d0a19e7f42eea9ee4ad8d36a19e039623eeea47a6fb1658df2e20f7e23cbc02c5c59d7961cf88402a346584af229cb795295c5ea108440bd7de5ea01f0", - tweaked_sig: "a57c39c39b18fd5e6f572253e19227252a4a81a4ac3dc1ef804e6b306b142b10167e70784b355120714678e31076e1726942d3320fa7ca45e213b9b896c06ad9" - }, - %{ - privkey: 0x7EDA1030020279FA24CC33B79A665B73A9E715EB1EB2C6DBEBEE21AD7BCF1E2C, - pubkey: 0x65c57e92bd052d623ac708c34a9a73851e37c81cb763b9fe1ff88437ce42245f, - tweak_secret: 0x1DE275F6F723416882A18E0F4CCD398B7AA9C2A701BFD16D448A118BF82D3027, - tweak_point: 0xcbc6ed86f4442c6403d5c0a3b8157489c5cc55d5137bbd4648c58301979b6f6c, - message_hash: 0xA744D50F65508DDE9278AECAD6A2759BE3A83709877B427E4098085DB27B9826, - aux_rand: 0x12F582D5FA0B2F6245985FBC7C7220C55D441D027B64BA2A7289B9A15E5106F7, - untweaked_sig: "454d9f3fe892e1f31638a258b135ddb74beaed682936830b1d233432e7a2a575d9953241a92d0172bf8df6e198dc7a9052638337483e0881ccb96ba38ea973d3", - tweaked_sig: "db076ae6615ccd083a0d8e6d9e78c28cba696f4c4d3fbfb5c5a55b23d4de0d0af777a838a05042db422f84f0e5a9b41bcd0d45de49fdd9ef11437d2f86d6a3fa" - }, - %{ - privkey: 0x95896701D713F8AA383176C2BCD61F8EC55A29C5169AEBE9A8A32AC9803F8E7D, - pubkey: 0xe7d2b5e311e4f7e81d05c5c56a94f0e5438da78b2e512f78b540114dcf931080, - tweak_secret: 0x7038F53CA5C13C1534A595BC74799657805CB864A21AC78618E661E2A27F6DE4, - tweak_point: 0xe76f186a03ef4ca41b55ff18e2de2f713cf9a1f97f23f627681a2a47bea24a95, - message_hash: 0x4928F4C94434C570F12CF548F4B2F4C9124F5E6D1371020D55EC3D508DA49D61, - aux_rand: 0x51DDDD197BF29EE74235C80CECBE06C8E012BFFC0E227E87A966AD9BB6E6A07B, - untweaked_sig: "46d4e627dc2a8d3864f4e6738f88ab891e5e51da7b08251218d11b0f56f047b4d91271d08e0ba2ecde0169ce02307e49bca6cc749e3c6869dad5ace39f09c943", - tweaked_sig: "83ba54b75d4516cb376b51fc8fb9cd560aae652a2ff8cded96cb052ed3b1e12e494b670d33ccdf0212a6ff8a76aa14a28254a7f2910e8fb433e9b0397152f5e6" - }, - %{ - privkey: 0x695563958BE07A20700F6B624C2EBA9F336C47ADF7B8E202388600132D2E7CF3, - pubkey: 0x97826ac90a2e8ebf9c2f94c5f23863aed915b4c4e77701b9ef7b43e9c501d0f3, - tweak_secret: 0x6FDDBC57B407184C1D1C0B9040A14A19EF8824222431F39CB89FED2C9D67CE41, - tweak_point: 0xe772ca746c6f40e607167f6d95414dab603fb36e0a4e473ab69d6e1036c209ea, - message_hash: 0x9A6BBFEF82261C8EFA9CD7F3481728D68554B2E698187797EAF60A734B6307D9, - aux_rand: 0x1ED0360C5241E28038735B43C6705AE2A1A971CDD94FCED6817C005ED2DF8237, - untweaked_sig: "d44e4a61254b90b671a278ee8ae73add71c5cb89b9fda0611cabb3ab46c4bb5d2511324a297a4027f3bef12a79e07a16d1419922e0d6c205066b538efc655dfe", - tweaked_sig: "840a95a2d67b1fe09584a2b9f353277c318d4b21774a777a2dd2814de4e9d22094eeeea1dd81587410dafcbaba81c430c0c9bd450508b5a1bf0b40bb99cd2c3f" - } - ] - - describe "adaptor signature tests" do - test "test adaptor signatures" do - for t <- @schnorr_adaptor_signatures do - {:ok, sk} = PrivateKey.new(t.privkey) - sk = Secp256k1.force_even_y(sk) - pk = PrivateKey.to_point(sk) - # check pubkey is correct - assert t.pubkey == pk.x - - {:ok, tw} = PrivateKey.new(t.tweak_secret) - tw = Secp256k1.force_even_y(tw) - tw_point = PrivateKey.to_point(tw) - # check tweak point is correct - assert t.tweak_point == tw_point.x - - # parse sigs - {:ok, c_ut_sig} = Signature.parse_signature(t.untweaked_sig) - {:ok, c_tw_sig} = Signature.parse_signature(t.tweaked_sig) - - {:ok, ut_sig, t_point} = Schnorr.sign_for_tweak(sk, t.message_hash, t.aux_rand, tw_point) - # check tweak point hasn't changed - assert t_point == tw_point - # check untweaked sig - assert c_ut_sig == ut_sig - # adaptor sig is not a valid schnorr sig, must fail - assert !Schnorr.verify_signature(pk, t.message_hash, ut_sig) - # verify adaptor signature - assert Schnorr.verify_untweaked_signature(pk, t.message_hash, ut_sig, tw_point) - # complete adaptor sig - tw_sig = Schnorr.tweak_signature(ut_sig, tw) - # check sig - assert c_tw_sig == tw_sig - # ensure tweaked sig is same when using tweak private key and integer - tw_sig0 = Schnorr.tweak_signature(ut_sig, tw.d) - assert tw_sig == tw_sig0 - - # complete sig must be valid schnorr sig - assert Schnorr.verify_signature(pk, t.message_hash, tw_sig) - - # extract tweak secret - {:ok, tweak} = Schnorr.extract_tweak(pk, t.message_hash, ut_sig, tw_sig) - assert tweak == tw.d - assert t.tweak_secret == tweak - - # extract signature given tweak - {:ok, sig} = Schnorr.extract_tweaked_signature(pk, t.message_hash, ut_sig, t.tweak_secret) - assert sig == tw_sig - end - end - end -end diff --git a/test/secp256k1/schnorr_test.exs b/test/secp256k1/schnorr_test.exs index 522d940..454178e 100644 --- a/test/secp256k1/schnorr_test.exs +++ b/test/secp256k1/schnorr_test.exs @@ -4,9 +4,11 @@ defmodule Bitcoinex.Secp256k1.SchnorrTest do alias Bitcoinex.Utils alias Bitcoinex.Secp256k1 - alias Bitcoinex.Secp256k1.{Point, PrivateKey, Schnorr, Signature} + alias Bitcoinex.Secp256k1.{Params, Point, PrivateKey, Schnorr, Signature} # alias Bitcoinex.Secp256k1.{PrivateKey} + @n Params.curve().n + # BIP340 official test vectors: # https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv @schnorr_signatures_with_secrets [ @@ -154,8 +156,24 @@ defmodule Bitcoinex.Secp256k1.SchnorrTest do } ] + def get_rand_values_for_encrypted_sig() do + sk_int = :rand.uniform(@n - 1) + {:ok, sk} = PrivateKey.new(sk_int) + sk = Secp256k1.force_even_y(sk) + pk = PrivateKey.to_point(sk) + + # tweak + tweak_int = :rand.uniform(@n - 1) + {:ok, tweak} = PrivateKey.new(tweak_int) + tweak_point = PrivateKey.to_point(tweak) + msg = :rand.uniform(@n - 1) |> :binary.encode_unsigned() + z = Utils.double_sha256(msg) |> :binary.decode_unsigned() + aux = :rand.uniform(@n - 1) + + {sk, pk, tweak, tweak_point, z, aux} + end describe "sign/3" do test "sign" do @@ -276,5 +294,48 @@ defmodule Bitcoinex.Secp256k1.SchnorrTest do end end + describe "encrypted signature testing" do + test "encrypted_sign/4 and verify_encrypted_signature/5" do + for _ <- 1..100 do + {sk, pk, _tweak, tweak_point, z, aux} = get_rand_values_for_encrypted_sig() + + # create adaptor sig + {:ok, ut_sig, was_negated} = Schnorr.encrypted_sign(sk, z, aux, tweak_point) + assert Schnorr.verify_encrypted_signature(ut_sig, pk, z, tweak_point, was_negated) + end + end + + test "encrypt & decrypt signature" do + for _ <- 1..100 do + {sk, pk, tweak, tweak_point, z, aux} = get_rand_values_for_encrypted_sig() + + # create adaptor sig + {:ok, ut_sig, was_negated} = Schnorr.encrypted_sign(sk, z, aux, tweak_point) + assert Schnorr.verify_encrypted_signature(ut_sig, pk, z, tweak_point, was_negated) + # decrypt to real Schnorr Signature using tweak + sig = Schnorr.decrypt_signature(ut_sig, tweak.d, was_negated) + # ensure valid Schnorr signature + assert Schnorr.verify_signature(pk, z, sig) + end + end + + test "encrypt & recover descryption key" do + for _ <- 1..100 do + {sk, pk, tweak, tweak_point, z, aux} = get_rand_values_for_encrypted_sig() + + # create adaptor sig + {:ok, ut_sig, was_negated} = Schnorr.encrypted_sign(sk, z, aux, tweak_point) + assert Schnorr.verify_encrypted_signature(ut_sig, pk, z, tweak_point, was_negated) + + # decrypt to real Schnorr Signature using tweak + sig = Schnorr.decrypt_signature(ut_sig, tweak.d, was_negated) + # ensure valid Schnorr signature + assert Schnorr.verify_signature(pk, z, sig) + + recovered_tweak = Schnorr.recover_decryption_key(ut_sig, sig, was_negated) + assert recovered_tweak == tweak + end + end + end end diff --git a/test/secp256k1/secp256k1_test.exs b/test/secp256k1/secp256k1_test.exs index d03547d..4a6c4f5 100644 --- a/test/secp256k1/secp256k1_test.exs +++ b/test/secp256k1/secp256k1_test.exs @@ -176,7 +176,7 @@ defmodule Bitcoinex.Secp256k1.Secp256k1Test do privkey = Secp256k1.force_even_y(%PrivateKey{d: secret}) pubkey = PrivateKey.to_point(privkey) - assert rem(pubkey.y,2) == 0 + assert rem(pubkey.y, 2) == 0 end end end From ce1f86926ca76d9d004da15405a9c1bed378398d Mon Sep 17 00:00:00 2001 From: Sachin Meier Date: Tue, 31 Jan 2023 01:07:03 -0800 Subject: [PATCH 09/16] finish adaptor signatures! --- lib/secp256k1/schnorr.ex | 13 ++++++------ .../schnorr_adaptor_test_vectors.csv | 21 +++++++++++++++++++ test/secp256k1/schnorr_test.exs | 12 +++++------ 3 files changed, 34 insertions(+), 12 deletions(-) create mode 100644 test/secp256k1/schnorr_adaptor_test_vectors.csv diff --git a/lib/secp256k1/schnorr.ex b/lib/secp256k1/schnorr.ex index 7e612cc..0534fee 100644 --- a/lib/secp256k1/schnorr.ex +++ b/lib/secp256k1/schnorr.ex @@ -92,6 +92,7 @@ defmodule Bitcoinex.Secp256k1.Schnorr do |> Math.modulo(@n) end + # this is just like validate_r but without the R.y evenness check defp partial_validate_r(r_point, rx) do cond do Point.is_inf(r_point) -> @@ -99,9 +100,6 @@ defmodule Bitcoinex.Secp256k1.Schnorr do r_point.x != rx -> {:error, "x's do not match #{r_point.x} vs #{rx}"} - # TODO ensure encrypted signatures are allwoed to have odd Y values - # !Point.has_even_y(r_point) -> - # {:error, "R point is not even"} true -> true end @@ -158,7 +156,7 @@ defmodule Bitcoinex.Secp256k1.Schnorr do to decrypt the signature into a valid Schnorr signature. This produces an Adaptor Signature. """ @spec encrypted_sign(PrivateKey.t(), non_neg_integer(), non_neg_integer(), Point.t()) :: {:ok, Signature.t(), boolean} - def encrypted_sign(sk = %PrivateKey{}, z, aux, tweak_point = %Point{}) do + def encrypted_sign(sk = %PrivateKey{}, z, aux, %Point{x: tweak_point_x}) do z_bytes = Utils.int_to_big(z, 32) aux_bytes = Utils.int_to_big(aux, 32) d_point = PrivateKey.to_point(sk) @@ -171,6 +169,7 @@ defmodule Bitcoinex.Secp256k1.Schnorr do {:ok, k0} = calculate_k(t, d_point, z_bytes) r_point = PrivateKey.to_point(k0) + {:ok, tweak_point} = Point.lift_x(tweak_point_x) tweaked_r_point = Math.add(r_point, tweak_point) # ensure (R+T).y is even, if not, negate it, negate k, and set was_negated = true {tweaked_r_point, was_negated} = make_point_even(tweaked_r_point) @@ -191,11 +190,12 @@ defmodule Bitcoinex.Secp256k1.Schnorr do %Signature{r: tweaked_r, s: s}, pk = %Point{}, z, - tweak_point = %Point{}, + %Point{x: tweak_point_x}, was_negated ) do z_bytes = Utils.int_to_big(z, 32) + {:ok, tweak_point} = Point.lift_x(tweak_point_x) {:ok, tweaked_r_point} = Point.lift_x(tweaked_r) # This is subtracting the tweak_point (T) from the tweaked_point (R + T) to get the original R tweak_point = conditional_negate_point(tweak_point, !was_negated) @@ -220,7 +220,8 @@ defmodule Bitcoinex.Secp256k1.Schnorr do """ @spec decrypt_signature(Signature.t(), PrivateKey.t(), boolean) :: Signature.t() def decrypt_signature(%Signature{r: r, s: s}, tweak, was_negated) do - tweak = conditional_negate(tweak, was_negated) + tweak = Secp256k1.force_even_y(tweak) + tweak = conditional_negate(tweak.d, was_negated) final_s = Math.modulo(tweak.d + s, @n) %Signature{r: r, s: final_s} end diff --git a/test/secp256k1/schnorr_adaptor_test_vectors.csv b/test/secp256k1/schnorr_adaptor_test_vectors.csv new file mode 100644 index 0000000..32d1f7b --- /dev/null +++ b/test/secp256k1/schnorr_adaptor_test_vectors.csv @@ -0,0 +1,21 @@ +private_key,public_key,tweak_secret,tweak_point,message_hash,aux_rand,untweaked_adaptor_signature,tweaked_signature,was_negated +"64d399f840a0153ae7573fcd736b2b6dc8d31417514ab67622eff2354ee22241","03077b0ff8da086bbd941887ea62d90f0ecdac8acb093254fb4d69dc97d65f56","44f76c9909398b59a6fc09f9cd446476ca91e00cffe8499c1c326e2f8b9289ca","c8d33563fe4ae990f75cc0baa7526df881d95759f1dd31fa10ea3d614b8d6bef","69d1666105256708b242da64ac91b8d455119375ddaf588487f5dfd31c843f9e","f1a3588373fe6a90cf4b8d91295f41693b606f46d41f0d221bd6ebff75817f23","1527c51cd4caf8319f3017e7d06dea2faa4aa7663fed96aea69b5244c34f3588e3fec4d6f7f890d943e8d5b3acc39799c94d925a62e3fda2291cb9bf2b537e6a","1527c51cd4caf8319f3017e7d06dea2faa4aa7663fed96aea69b5244c34f358828f6317001321c32eae4dfad7a07fc11d9309580b383a702857cc961e6afc6f3",false +"332b18ac4de2ee1b125bfa35e1b0ae692c900219ae36cdc94de0de9b7b82ec35","57270515af6b0cfc28cc621c18522ce7c490bff838d6fb3e057a9d518d975464","ddbfb38f862619928f205a925ad5d7f17fffefd2526d8b37a83b863d090f8ded","9bd47e739d691b7c81c83b08678b38afc4199060ceae0e1e61fa013dc2eff161","44d013b587c4b9982cefacf9e4014e96f52485cc7648815f519e5c7acf817b95","5a238ecfc487b6a1f52e9fda6047a3a6db0c5a4a6c581671908f81c35f744c34","019ae9f0058af51040046fd20f81e3c4db38af40107a01649beb82694084331d894ef7dd5635592f2de62c9e456ad757658dfd5f6b3a3d08e23755c04981f905","019ae9f0058af51040046fd20f81e3c4db38af40107a01649beb82694084331dab8f444dd00f3f9c9ec5d20bea94ff64a03cea73c815520cf9ce2e1010a8ac59",true +"a6f9e49b8e4b714d760dbd8f01abfe04e2108f5accd7ce81091d8f8a04d4e3ef","60e3b83eec196e0ea1195ae64580d3345654e162266800f1c5507bd7acc166ef","a78f44c328c5506fe347688640de9b085560d399be6ecb23dbf06ef3239d9117","e8314bb4e986ab44904e66adc3b272a626b5804cc89bf8a9338296bd93eee97c","7a14183e12079562ea17b59a9156b1a74bb7fdd6bb16413c4a6ac4599b2c3818","4d1860c32ea63d2d59856108d3eb55e14d962cb106daf333bf721cbeaf1de6b1","0d7755f38ce499b4757528e2dbc6045a10e4242babfd9bee595f03a83b8a559a55545812984531783a3b9cd6fca829fb14c8dc7731242c1fbe916d2c3c9f965c","0d7755f38ce499b4757528e2dbc6045a10e4242babfd9bee595f03a83b8a559aadc5134f6f7fe10856f43450bbc98ef17a16e5c421fe0137a2735cc5e9384686",true +"3ecdc22e7f8f527ba66ef75754ff386db60739ad691e8713b00c7d0e18650365","e3047a46759d8e47b22dd384cf8fe8631fefa466ea41b75030cea308c3360d45","8263cfdf5b3034958b7e503907ea95722deff04f0fdbbf76adbae6d79774f2de","95e028af7e8890ba89705fdd05e26f6c68050eea5f17c65095cccb903da76f8d","6c1ea354705a73aad3006aedab07ee311d343b1421b7f4daa4380c389a793180","eea79caadc04e2df90c29494e002bb1cf096c11996eecd8ff9c4733ff133c12c","d49a04f7cf7f64414ef67c1e113ec85a8fd683d6b102584dedd5b1737b8feccb47a6a2a1a3a2dafe60ea9e54b1ba83e99b0125d89cea8d516b9b5a6fa85bfb19","d49a04f7cf7f64414ef67c1e113ec85a8fd683d6b102584dedd5b1737b8feccbca0a7280fed30f93ec68ee8db9a5195bc8f11627acc64cc8195641473fd0edf7",false +"7789778cfe9d60aa50c339a11b36ffbbc5d350c345ab14e56f207f430deafca5","031551b96dfef020553900283837e36f1f669dc1b7857129a66a23dc3cef7b04","19db69599941098c1f375b8608094dbad22d72e1fc62cd65285321a029b63de6","4ee81875e67869acd8d49b53dcdfeea2c64360b4470d548cd0a66916e354e565","db7aa4a8785c8cab4d42f6c1f282982818f5b528b3d6beb5b99486de031bf4fd","03ea829059d5d7d36ff9554b3718a0177a04de8e5ff33730c72e480477840042","b847785cfbdfc4cabab8c87499e50898834cf2f9fbd5407d3aeaccd6c234410d8824b1197afe7727177b54d6378c6e3dbc60fabdfc24b60f572ce0637b02089b","b847785cfbdfc4cabab8c87499e50898834cf2f9fbd5407d3aeaccd6c234410da2001a73143f80b336b2b05c3f95bbf88e8e6d9ff88783747f800203a4b84681",false +"2c42d0167ee5cfa88a0cc1c16fa0c9dc38c01d47f30e4c5423c707aecdd365aa","b97f1f0ce685329fce080aff4c9a17f3dfc6a14bfe9689290e9b89ec7f24bb5d","f06324b650ec674ccefa36c4e19c4e700fa09bedd1f7129c44745479dffab65d","0f638142959e8e6dc979685bb4292b646389db2ec418f87629bac97dc6c21478","f72df7b15f31c5abb0db5af07efc53199afe533af041512b47276d7ab90336be","6b117aabe372c146924929aa00990d7392eca59fc31b416a833a7e2a56ab73a8","f5b66cdfb1c916c9ceb705f8fceeee166d7aec4a71910562a1b19d4507f2518dedade54f20ed0f0345cc22a4374971ce64f4bb7cfd0e03574e0bab7f4dd1a051","f5b66cdfb1c916c9ceb705f8fceeee166d7aec4a71910562a1b19d4507f2518dfd4ac098d000a7b676d1ebdf55ad235d1002fc75da5f90f6c969b5923e0d2b35",true +"0570c938ab1e31af3244c1c8de80bfac01da36cb00708506aa9d6294b1020275","a162076078883fe9ce67f32a28eb637796fe5a20c3ee5a4dcf5795d54142152a","cb97db136151e7fd66b36622e2f6ac9f1244e951b5bf15f9a3f38aee73be4ce3","6ba7a5b30ac5bd0e5b7641830cb74367b41136823f8be7100e3c14d80b3eebcd","78ca0adc4d4d892c4bb8060c10b69d3b05630a5b5189877b9f20691897988174","4b8d63dc59a313c09548593d2002a5af4ca601b37a98bb299d8605062ea8c88a","febe05ae7e214eb33f6fabcf7bd4297b1b06e14f3bcb8170e3db5cc02aadb6d4538757ed880d9f67d61947202c7d6ba6e82ad5bf99f51b6c318009f26c59acf5","febe05ae7e214eb33f6fabcf7bd4297b1b06e14f3bcb8170e3db5cc02aadb6d41f1f3300e95f87653cccad430f7418473fc0e22aa06b912a15a136540fe1b897",false +"cfeb57aa91b08f7df563c54cd5c37fe9354c34c7a3a0942a73a1bd28d01ca785","6379d1dcf2da0e5c90e05d02b32a5c969dd21ea986f2df5a329ce2154c2f54fb","6c8f2a3b7bae9cd68637fbaac2fc1b62d5ecacd24ad97163659e7016ed79ad35","bcd420b56d103b29f50376335b959b76ff1102f319cc0e8c791615cac203c0fe","91d16e79b8869ae5d5cabfb4818405613bb9a89b552370309e44dc923c5071ae","fea792688415e4ea1927a8287dbf938aff1795c590cd89fc83a6190820f4a619","d5743de4c0be2194c9c3c7343e0e48a1f4a02627691933355f7ffcd5a2fcb0427bf317bebab7ded50ebf1b253845b43da1ad71cb5d0bd93f8fc895a7f2ebc3d5","d5743de4c0be2194c9c3c7343e0e48a1f4a02627691933355f7ffcd5a2fcb0420f63ed833f0941fe88871f7a754998dacbc0c4f9123267dc2a2a2591057216a0",true +"94c063c368f720e36575be96aa0075d73e669b9a0cb4188306d8bb4cb1cfb1e1","637cf87ec03dd68084474aa9f22bcc94a0985048d0a38b24e12b821f492ff4b5","c9fb6ba3efe3064bfd7625a53f254e90c59f35969ee3a65e110d6437fd24b435","c33096b024f6871aa9958e620501df9adc43760b6213e0a0783d143e1f36e8d7","68d398b891283b42e4bda770d3cb644be617e82a93cbd281ceae542d66e3bf55","54c1eec0ece9844fd5de9512e72b4e3452cf5472dc4934f0a7ef59323468d42a","e651742898c15685204152e9f98abb1d313ae35f4f2a156847de4ca9ce2fd9afd56205207bda7af8933d3ac71f5ef475aa03bbe8fe78733ba42f1271106a174d","e651742898c15685204152e9f98abb1d313ae35f4f2a156847de4ca9ce2fd9af9f5d70c46bbd814490b3606c5e844307b4f41498ee13795df56a181c3d588a41",false +"9e0278e0e6f8a0767dd81ecea025adf55dfaf913f9b4ff347115956fd0fb327d","535b5a59e5879920b490835ac9dbf23a82435bd083b2cc66be5be9997e2ce681","c47f351884757e03385b7c5a60d43895fa4b88dc9388d20c773c0aa6704b7ee0","93373b7570b569d3945cdc306b18b29b5f71a502f2636accb4a0b34be6d73203","8d96a34cf601b53ea3f0f1b0a5ca315f89f8d968f148c071560fca35ce97d1d4","e57adc1b2b869f8e07d97d2725fcffe19338c737a78c6d9112b34cbc5fa7c1b0","93f023cc065d5b422cbfe35b3a75e4a5aeb59b91ea500dac2cb95768519cb2b74b08551ea14588ec3a29e1d2d88d629721bbfbc7af3f5785dd7e41ca03f7d857","93f023cc065d5b422cbfe35b3a75e4a5aeb59b91ea500dac2cb95768519cb2b70f878a3725bb06ef72855e2d39619b2e6158a7bd937f895694e7ede3a40d15f6",false +"e29f4867a64d1199a19777f91c9ec2f9d15dd08a93409144ea0064cb60c76a3e","85524318955cb8c63933e6e0c06b8fe561861628548539bf760c2f1840cf8ae7","3b415bb6c72bce59fc88681de981ae96875d5cccb98755e195b3e4399340171c","f86e55a60819bc5b9ba93746753f4fc0db87c10de5b7d25f1fe242b3c1bb6ebd","1ec2865fb83a22cddf06a22491d861b685e987d115e89c4d49b93672944553df","fd6e924750d9359ff32feacc6e61991d6da690d312514b5b896bcb3365f05bdb","013a94ce22a0fed453e1e7959ca619d4a9676fe863aa3450cfd2081b9829b7cf69bee8aad31fb077fc38eeef227519f5a1ceb06692d362c11ab34351c8a15381","013a94ce22a0fed453e1e7959ca619d4a9676fe863aa3450cfd2081b9829b7cf2e7d8cf40bf3e21dffb086d138f36b5f1a715399d94c0cdf84ff5f1835613c65",true +"5745cbbd4a3f91aace64d6d5dfd81787b40c11f9c6a934bdf3fc80b2c8e28fb8","c99af68ee4b762410875e5bda87af74683541ed597966c64c96f8c486ff1f19a","627977f34498f34ed896fb5019fb9ffa2dc7dd238bf3777a3701186df35299c7","0a801b75ed3ae344de1b255f53930fc82fade6a0ba0e0b07d8b109a2b9cc3701","47406b77cca9234a06bdb4fc65ac76649e392ea780bdb81a6fc6cf3221f1354c","652839026578293d7ce1969b4d457739e722a2f5952857889da87a7d1b6f0f89","89a576153cf1bc3bc7ba7322b431a2c76851430fd4185d8369dc6ed24ef74495faca5ac0354f0a837a66214ea462f7f4e7212218776ca7ea37eb8b77ce53cd7e","89a576153cf1bc3bc7ba7322b431a2c76851430fd4185d8369dc6ed24ef744959850e2ccf0b61734a1cf25fe8a6757fab95944f4eb79307000ea7309db0133b7",true +"0c097d304c27a23e91e639407341eb33bfc90f74184130251bb0553dbe2de854","2b0868c00a5799f5686afe3550ca15d94ee2a6a81b1ecbda18e0735ca7ff7a56","6dc0f73722daa9eef9d0dcf931841a07f53c844b30a08c3f89898e619bc855f3","5c44f4f8428117f9ee1ad3711ec1120ba5a8545cc7d5aace491a215b49d617ec","52f98f0d574cae4db1cd30cb67a3e65c44dedb16f07c7bd728bf0fb83d0ad408","460d47e19edb129b81d62e6b59f76dd7bd4c61a725da3338d706c0e755ac2ae9","79beeded1b00c785dd0a466baa5b650f436ae169d0782b0d46baf5c481768c8e9c67dee427981c760ab8c6b90ce3d9f22d07007d16845eb9f80fe1dc948efbf7","79beeded1b00c785dd0a466baa5b650f436ae169d0782b0d46baf5c481768c8e0a28d61b4a72c6650489a3b23e67f3fb6794a7e197dc4abdc1c711b1602110a9",false +"b1b904d991387680de13c606565643e4930d2bf79b721db4530401c3b92cf3cf","63cdee00d9f07194399fe6518512073190b71dc2a94084681e49a172158f90ff","b6928d5994fa41ebd66a22a826cd6ad4015a23feab9ae45f1eaac309007fca73","38388ce16e2c5e1eab89b4e56233dacb58d4aa286ae2806d5debd117c9c529f9","6eab75f6bf9de09d363e4fc2447d0fc03feb78a5fb06ccb39daf648e13fd3045","41f2e0be320908f40428a195e471e425864ab7acbafdb11e642a68be1598c2c3","09cf78f977503f3a0e231d33155f1e65d100eedd0af68e5d40fa9e392138f2cb97acb68f70542aa1f6641f228d0df53b3a3930e82cac62d5cd466b89ca9049e4","09cf78f977503f3a0e231d33155f1e65d100eedd0af68e5d40fa9e392138f2cbe11a2935db59e8b61ff9fc7a66408a65f38de9d0305a1eb26e6e070d9a46c0b2",true +"7a444d39de9cf6852cf431a622af15acb852dce5034735166bdc4cccf6d984d3","52853e8d893caeb43c6ab322625641ccece988667bf9d64670a4df4750b83d7c","bf611892850c5e814215dfecd9629ed0de56376bab46fe361db5c0ba4fe916d8","103dd130009f424fdc25eeb5a1392a36fe4e008290267722ac24eb492db00ead","49a5fbb6bdb0cd167e5fb7b4b8544fcd87a3433e66292173c101efcd97756349","7666bd5dce4e4ad0df76672bcf6f8123adf4f5314894b808fbf739e3e3e687bd","6c9610699213a961ceb63a32459b5408c3a20035dc0c8b126b67d04bba9bce9abd8f6cb88cc6090f789d6726cda594f72d0335653a5b18c27655fb38dee47d56","6c9610699213a961ceb63a32459b5408c3a20035dc0c8b126b67d04bba9bce9afe2e542607b9aa8e36878739f442f625095bdae03e5cbac81872990b5f31a7bf",true +"c19d1686e76d5b777af0924e6154615abb7db030a22e407a397ce7e301d87742","0ba3c133ab7dee2839552fc9e43601ab3cc501c21891f2fd34fce78e43b63e67","2e5b908a7466490f23b30f5b75bbf10ba0d9c901a8fa1188f5ba6b1d07277174","5fba25cf7f5fb85e24900d14f789604dd0eca2eafa53ef2a41e55a79740709b0","ca543d2652c3ff13ac98641cb65df9dd0f719706363dcfa74592b373a638e308","da2cde0c16fd3cebf8c84ec28dbf1a951f43ff930795b610df8cec40749a25e2","4f9a66e8ccd5f1c68e906c7ffc8a5284065e2960a13c8af05fd671a4a2f8f93e39584c6b03ef20b2edf449b5f6fb7a00a42eadc9aba1f3652cd143d37651700a","4f9a66e8ccd5f1c68e906c7ffc8a5284065e2960a13c8af05fd671a4a2f8f93e0afcbbe08f88d7a3ca413a5a813f88f50354e4c802a7e1dc3716d8b66f29fe96",true +"29e362a7bfef5fb7f4918ec19e25befa8a4a43a23c8ec7c6028f97226773a7bd","2102f0eed266ed80ec7568cb20eeead902f0c35309f5ba7c1567b819a8e1bbb7","6c82609f56f6c11866cc83ee2498d551c17ae68d72904b4af73ff8e5f45c3284","f9f9be4fc4d63060a3c3c0656679f3d757775f8f8641a71f1525e53b57a1d99b","c78fc24af3b81ec075e1e1764b4d67ea07b5a9dda2e1504e159eb427025fb80b","15968a700ee7598db61ab47d210a27f7d0d82c5d25739b6141b095de9e00942d","676d626dad99593a9aaff30b64f38c513858b95a3f76269c211a1ff05525f39afe63ee86c0240d1f2bda706dcb386ab6b1a3d73cb44250f9fd409b764a4e2bc9","676d626dad99593a9aaff30b64f38c513858b95a3f76269c211a1ff05525f39a6ae64f26171ace3792a6f45befd14009b86fe0e37789fc0934ae35cf6e741d0c",false +"b01defc1ec85fdd6381f62ecb596239e91245b940b18489071526064a538baef","baab11fd4992c19e6a7692944ff665c08e1fb06c7a26b0f2ee2253fc9f7227e8","a0dfd48fa85f67b08b3fdbb9a6bb71c74b4239148f65f35caa37b1d1800fce44","025342259c0aa8df863a2f151ff0c2b1d6064997d1c54c42e5b62d033e83c7af","f5032f3bb2f6767a6f95b3559b74e14edde45ab47f8ff5c48542d748374c5144","08a35c8959bf4a10b85c75b7ef260d39f865876f4bdb66511b53dccf3f1a9353","3eaec73e10aa4b1f870d426dd0570dd5266ab32a93564393aad145dac625d9933033dc488afdcf7a9ac18515f96a4ac4e73db690bde3f49e2ce53141f437f0a8","3eaec73e10aa4b1f870d426dd0570dd5266ab32a93564393aad145dac625d993d113b0d8335d372b260160cfa025bc8c327fefa54d49e7fad71ce3137447beec",false +"dea7c5fe87a447632a53dcf40716ad99bd04d983405d851f44ff2671f8f8a6ea","07f04f60aa3367725401b8a813c0575007438714f3b6afa9150acdae4867f2f8","840da049a59cc5b8830e54aee306788b2b6104655f0a23a9404671f821fe9573","4ba30d52534bbed6bb67fa5e1bdfb1bdb983843a56887e43e652e89ccaae519e","d568dee1f5d4d1b400641fee5afd68e176e234acda24b0eb2516f739a8e87dcf","cab1f1f1229e93ad2efb76649c8091717ace3ba01461ac559295c5e0e006d07d","956bbdab0e1ebbbec714dd126a749b4f94bc7e55258eea4ce589e50ef0153370f1f321db528ee4f907e8b66903f080575efd5d11bb2518d9ec021e6b4c0e1156","956bbdab0e1ebbbec714dd126a749b4f94bc7e55258eea4ce589e50ef01533706de58191acf21f4084da61ba20ea07cc339c58ac5c1af530abbbac732a0f7be3",true +"17936333370f03b826196c144d2051b3da132aa00f8bb5c12482a577cad7c31f","54dbd2074d2787dc5a3e64f197bc7beb59987a4d3ed70fc818cd48ff08efeff1","059bcae22f27b9d0b66912406950cd96ac21c89b859b5f0fa401dc4ef9c6ff4d","951bf3b6eec23b6670b605a98998da6e38664d9f64cdd4f3573bc2b6f443b98e","28592ae85cb8d559e3bbe91793ad9c1463f03d7070d2e97d33b3c26d69d2fc82","2386074d572d93c77e096f6fc448521cc180f9257d9f3fb5347b7abaeb08c00c","4d081829ae9d9a9d560fa11e1122d780ef5c765719bb537d5daae655606d4814c38510e3df3375f4789be40145260d0aab790cc40e6c567e8face06b61ef0f7d","4d081829ae9d9a9d560fa11e1122d780ef5c765719bb537d5daae655606d4814c920dbc60e5b2fc52f04f641ae76daa1579ad55f9407b58e33aebcba5bb60eca",false diff --git a/test/secp256k1/schnorr_test.exs b/test/secp256k1/schnorr_test.exs index 454178e..167ab0b 100644 --- a/test/secp256k1/schnorr_test.exs +++ b/test/secp256k1/schnorr_test.exs @@ -296,7 +296,7 @@ defmodule Bitcoinex.Secp256k1.SchnorrTest do describe "encrypted signature testing" do test "encrypted_sign/4 and verify_encrypted_signature/5" do - for _ <- 1..100 do + for _ <- 1..1000 do {sk, pk, _tweak, tweak_point, z, aux} = get_rand_values_for_encrypted_sig() # create adaptor sig @@ -306,7 +306,7 @@ defmodule Bitcoinex.Secp256k1.SchnorrTest do end test "encrypt & decrypt signature" do - for _ <- 1..100 do + for _ <- 1..1000 do {sk, pk, tweak, tweak_point, z, aux} = get_rand_values_for_encrypted_sig() # create adaptor sig @@ -314,14 +314,14 @@ defmodule Bitcoinex.Secp256k1.SchnorrTest do assert Schnorr.verify_encrypted_signature(ut_sig, pk, z, tweak_point, was_negated) # decrypt to real Schnorr Signature using tweak - sig = Schnorr.decrypt_signature(ut_sig, tweak.d, was_negated) + sig = Schnorr.decrypt_signature(ut_sig, tweak, was_negated) # ensure valid Schnorr signature assert Schnorr.verify_signature(pk, z, sig) end end test "encrypt & recover descryption key" do - for _ <- 1..100 do + for _ <- 1..1000 do {sk, pk, tweak, tweak_point, z, aux} = get_rand_values_for_encrypted_sig() # create adaptor sig @@ -329,12 +329,12 @@ defmodule Bitcoinex.Secp256k1.SchnorrTest do assert Schnorr.verify_encrypted_signature(ut_sig, pk, z, tweak_point, was_negated) # decrypt to real Schnorr Signature using tweak - sig = Schnorr.decrypt_signature(ut_sig, tweak.d, was_negated) + sig = Schnorr.decrypt_signature(ut_sig, tweak, was_negated) # ensure valid Schnorr signature assert Schnorr.verify_signature(pk, z, sig) recovered_tweak = Schnorr.recover_decryption_key(ut_sig, sig, was_negated) - assert recovered_tweak == tweak + assert recovered_tweak == Secp256k1.force_even_y(tweak) end end end From 6a4e775f9c19350f3094bea1511705a0685193a1 Mon Sep 17 00:00:00 2001 From: Sachin Meier Date: Tue, 31 Jan 2023 01:14:39 -0800 Subject: [PATCH 10/16] add comments, make tests realistic by force even tweak --- lib/secp256k1/schnorr.ex | 3 +++ test/secp256k1/schnorr_test.exs | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/secp256k1/schnorr.ex b/lib/secp256k1/schnorr.ex index 0534fee..9a991bb 100644 --- a/lib/secp256k1/schnorr.ex +++ b/lib/secp256k1/schnorr.ex @@ -169,6 +169,7 @@ defmodule Bitcoinex.Secp256k1.Schnorr do {:ok, k0} = calculate_k(t, d_point, z_bytes) r_point = PrivateKey.to_point(k0) + # ensure that tweak_point has even Y {:ok, tweak_point} = Point.lift_x(tweak_point_x) tweaked_r_point = Math.add(r_point, tweak_point) # ensure (R+T).y is even, if not, negate it, negate k, and set was_negated = true @@ -195,6 +196,7 @@ defmodule Bitcoinex.Secp256k1.Schnorr do ) do z_bytes = Utils.int_to_big(z, 32) + # ensure that tweak_point has even Y {:ok, tweak_point} = Point.lift_x(tweak_point_x) {:ok, tweaked_r_point} = Point.lift_x(tweaked_r) # This is subtracting the tweak_point (T) from the tweaked_point (R + T) to get the original R @@ -220,6 +222,7 @@ defmodule Bitcoinex.Secp256k1.Schnorr do """ @spec decrypt_signature(Signature.t(), PrivateKey.t(), boolean) :: Signature.t() def decrypt_signature(%Signature{r: r, s: s}, tweak, was_negated) do + # force even on tweak is a backup. the passed tweak should already be properly negated tweak = Secp256k1.force_even_y(tweak) tweak = conditional_negate(tweak.d, was_negated) final_s = Math.modulo(tweak.d + s, @n) diff --git a/test/secp256k1/schnorr_test.exs b/test/secp256k1/schnorr_test.exs index 167ab0b..228d9c1 100644 --- a/test/secp256k1/schnorr_test.exs +++ b/test/secp256k1/schnorr_test.exs @@ -165,6 +165,7 @@ defmodule Bitcoinex.Secp256k1.SchnorrTest do # tweak tweak_int = :rand.uniform(@n - 1) {:ok, tweak} = PrivateKey.new(tweak_int) + tweak = Secp256k1.force_even_y(tweak) tweak_point = PrivateKey.to_point(tweak) msg = :rand.uniform(@n - 1) |> :binary.encode_unsigned() @@ -334,7 +335,7 @@ defmodule Bitcoinex.Secp256k1.SchnorrTest do assert Schnorr.verify_signature(pk, z, sig) recovered_tweak = Schnorr.recover_decryption_key(ut_sig, sig, was_negated) - assert recovered_tweak == Secp256k1.force_even_y(tweak) + assert recovered_tweak == tweak end end end From 8bccf204fb38b39c3a3cbc52ac66f747b1cd6037 Mon Sep 17 00:00:00 2001 From: Sachin Meier Date: Tue, 31 Jan 2023 01:16:26 -0800 Subject: [PATCH 11/16] fmt --- lib/secp256k1/schnorr.ex | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/secp256k1/schnorr.ex b/lib/secp256k1/schnorr.ex index 9a991bb..c7a6f44 100644 --- a/lib/secp256k1/schnorr.ex +++ b/lib/secp256k1/schnorr.ex @@ -100,6 +100,7 @@ defmodule Bitcoinex.Secp256k1.Schnorr do r_point.x != rx -> {:error, "x's do not match #{r_point.x} vs #{rx}"} + true -> true end @@ -110,12 +111,15 @@ defmodule Bitcoinex.Secp256k1.Schnorr do Point.is_inf(r_point) -> # {:error, "R point is infinite"} false + !Point.has_even_y(r_point) -> # {:error, "R point is not even"} false + r_point.x != rx -> # {:error, "x's do not match #{r_point.x} vs #{rx}"} false + true -> true end @@ -155,7 +159,8 @@ defmodule Bitcoinex.Secp256k1.Schnorr do as the encryption key. The signer need not know the decryption key / tweak itself, which can later be used to decrypt the signature into a valid Schnorr signature. This produces an Adaptor Signature. """ - @spec encrypted_sign(PrivateKey.t(), non_neg_integer(), non_neg_integer(), Point.t()) :: {:ok, Signature.t(), boolean} + @spec encrypted_sign(PrivateKey.t(), non_neg_integer(), non_neg_integer(), Point.t()) :: + {:ok, Signature.t(), boolean} def encrypted_sign(sk = %PrivateKey{}, z, aux, %Point{x: tweak_point_x}) do z_bytes = Utils.int_to_big(z, 32) aux_bytes = Utils.int_to_big(aux, 32) @@ -186,7 +191,13 @@ defmodule Bitcoinex.Secp256k1.Schnorr do verify_encrypted_signature verifies that an encrypted signature commits to a tweak_point / encryption key. This is different from a regular Schnorr signature verification, as encrypted signatures are not valid Schnorr Signatures. """ - @spec verify_encrypted_signature(Signature.t(), Point.t(), non_neg_integer(), Point.t(), boolean) :: boolean + @spec verify_encrypted_signature( + Signature.t(), + Point.t(), + non_neg_integer(), + Point.t(), + boolean + ) :: boolean def verify_encrypted_signature( %Signature{r: tweaked_r, s: s}, pk = %Point{}, From f4dc7300f91ae8547fcd7bb6f975e126a3e5925b Mon Sep 17 00:00:00 2001 From: Sachin Meier Date: Tue, 31 Jan 2023 23:01:37 -0800 Subject: [PATCH 12/16] do not assume even tweak --- lib/secp256k1/schnorr.ex | 8 +--- .../schnorr_adaptor_test_vectors.csv | 41 +++++++++---------- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/lib/secp256k1/schnorr.ex b/lib/secp256k1/schnorr.ex index c7a6f44..824d286 100644 --- a/lib/secp256k1/schnorr.ex +++ b/lib/secp256k1/schnorr.ex @@ -161,7 +161,7 @@ defmodule Bitcoinex.Secp256k1.Schnorr do """ @spec encrypted_sign(PrivateKey.t(), non_neg_integer(), non_neg_integer(), Point.t()) :: {:ok, Signature.t(), boolean} - def encrypted_sign(sk = %PrivateKey{}, z, aux, %Point{x: tweak_point_x}) do + def encrypted_sign(sk = %PrivateKey{}, z, aux,tweak_point = %Point{}) do z_bytes = Utils.int_to_big(z, 32) aux_bytes = Utils.int_to_big(aux, 32) d_point = PrivateKey.to_point(sk) @@ -175,7 +175,6 @@ defmodule Bitcoinex.Secp256k1.Schnorr do r_point = PrivateKey.to_point(k0) # ensure that tweak_point has even Y - {:ok, tweak_point} = Point.lift_x(tweak_point_x) tweaked_r_point = Math.add(r_point, tweak_point) # ensure (R+T).y is even, if not, negate it, negate k, and set was_negated = true {tweaked_r_point, was_negated} = make_point_even(tweaked_r_point) @@ -202,13 +201,11 @@ defmodule Bitcoinex.Secp256k1.Schnorr do %Signature{r: tweaked_r, s: s}, pk = %Point{}, z, - %Point{x: tweak_point_x}, + tweak_point = %Point{}, was_negated ) do z_bytes = Utils.int_to_big(z, 32) - # ensure that tweak_point has even Y - {:ok, tweak_point} = Point.lift_x(tweak_point_x) {:ok, tweaked_r_point} = Point.lift_x(tweaked_r) # This is subtracting the tweak_point (T) from the tweaked_point (R + T) to get the original R tweak_point = conditional_negate_point(tweak_point, !was_negated) @@ -234,7 +231,6 @@ defmodule Bitcoinex.Secp256k1.Schnorr do @spec decrypt_signature(Signature.t(), PrivateKey.t(), boolean) :: Signature.t() def decrypt_signature(%Signature{r: r, s: s}, tweak, was_negated) do # force even on tweak is a backup. the passed tweak should already be properly negated - tweak = Secp256k1.force_even_y(tweak) tweak = conditional_negate(tweak.d, was_negated) final_s = Math.modulo(tweak.d + s, @n) %Signature{r: r, s: final_s} diff --git a/test/secp256k1/schnorr_adaptor_test_vectors.csv b/test/secp256k1/schnorr_adaptor_test_vectors.csv index 32d1f7b..2fae775 100644 --- a/test/secp256k1/schnorr_adaptor_test_vectors.csv +++ b/test/secp256k1/schnorr_adaptor_test_vectors.csv @@ -1,21 +1,20 @@ -private_key,public_key,tweak_secret,tweak_point,message_hash,aux_rand,untweaked_adaptor_signature,tweaked_signature,was_negated -"64d399f840a0153ae7573fcd736b2b6dc8d31417514ab67622eff2354ee22241","03077b0ff8da086bbd941887ea62d90f0ecdac8acb093254fb4d69dc97d65f56","44f76c9909398b59a6fc09f9cd446476ca91e00cffe8499c1c326e2f8b9289ca","c8d33563fe4ae990f75cc0baa7526df881d95759f1dd31fa10ea3d614b8d6bef","69d1666105256708b242da64ac91b8d455119375ddaf588487f5dfd31c843f9e","f1a3588373fe6a90cf4b8d91295f41693b606f46d41f0d221bd6ebff75817f23","1527c51cd4caf8319f3017e7d06dea2faa4aa7663fed96aea69b5244c34f3588e3fec4d6f7f890d943e8d5b3acc39799c94d925a62e3fda2291cb9bf2b537e6a","1527c51cd4caf8319f3017e7d06dea2faa4aa7663fed96aea69b5244c34f358828f6317001321c32eae4dfad7a07fc11d9309580b383a702857cc961e6afc6f3",false -"332b18ac4de2ee1b125bfa35e1b0ae692c900219ae36cdc94de0de9b7b82ec35","57270515af6b0cfc28cc621c18522ce7c490bff838d6fb3e057a9d518d975464","ddbfb38f862619928f205a925ad5d7f17fffefd2526d8b37a83b863d090f8ded","9bd47e739d691b7c81c83b08678b38afc4199060ceae0e1e61fa013dc2eff161","44d013b587c4b9982cefacf9e4014e96f52485cc7648815f519e5c7acf817b95","5a238ecfc487b6a1f52e9fda6047a3a6db0c5a4a6c581671908f81c35f744c34","019ae9f0058af51040046fd20f81e3c4db38af40107a01649beb82694084331d894ef7dd5635592f2de62c9e456ad757658dfd5f6b3a3d08e23755c04981f905","019ae9f0058af51040046fd20f81e3c4db38af40107a01649beb82694084331dab8f444dd00f3f9c9ec5d20bea94ff64a03cea73c815520cf9ce2e1010a8ac59",true -"a6f9e49b8e4b714d760dbd8f01abfe04e2108f5accd7ce81091d8f8a04d4e3ef","60e3b83eec196e0ea1195ae64580d3345654e162266800f1c5507bd7acc166ef","a78f44c328c5506fe347688640de9b085560d399be6ecb23dbf06ef3239d9117","e8314bb4e986ab44904e66adc3b272a626b5804cc89bf8a9338296bd93eee97c","7a14183e12079562ea17b59a9156b1a74bb7fdd6bb16413c4a6ac4599b2c3818","4d1860c32ea63d2d59856108d3eb55e14d962cb106daf333bf721cbeaf1de6b1","0d7755f38ce499b4757528e2dbc6045a10e4242babfd9bee595f03a83b8a559a55545812984531783a3b9cd6fca829fb14c8dc7731242c1fbe916d2c3c9f965c","0d7755f38ce499b4757528e2dbc6045a10e4242babfd9bee595f03a83b8a559aadc5134f6f7fe10856f43450bbc98ef17a16e5c421fe0137a2735cc5e9384686",true -"3ecdc22e7f8f527ba66ef75754ff386db60739ad691e8713b00c7d0e18650365","e3047a46759d8e47b22dd384cf8fe8631fefa466ea41b75030cea308c3360d45","8263cfdf5b3034958b7e503907ea95722deff04f0fdbbf76adbae6d79774f2de","95e028af7e8890ba89705fdd05e26f6c68050eea5f17c65095cccb903da76f8d","6c1ea354705a73aad3006aedab07ee311d343b1421b7f4daa4380c389a793180","eea79caadc04e2df90c29494e002bb1cf096c11996eecd8ff9c4733ff133c12c","d49a04f7cf7f64414ef67c1e113ec85a8fd683d6b102584dedd5b1737b8feccb47a6a2a1a3a2dafe60ea9e54b1ba83e99b0125d89cea8d516b9b5a6fa85bfb19","d49a04f7cf7f64414ef67c1e113ec85a8fd683d6b102584dedd5b1737b8feccbca0a7280fed30f93ec68ee8db9a5195bc8f11627acc64cc8195641473fd0edf7",false -"7789778cfe9d60aa50c339a11b36ffbbc5d350c345ab14e56f207f430deafca5","031551b96dfef020553900283837e36f1f669dc1b7857129a66a23dc3cef7b04","19db69599941098c1f375b8608094dbad22d72e1fc62cd65285321a029b63de6","4ee81875e67869acd8d49b53dcdfeea2c64360b4470d548cd0a66916e354e565","db7aa4a8785c8cab4d42f6c1f282982818f5b528b3d6beb5b99486de031bf4fd","03ea829059d5d7d36ff9554b3718a0177a04de8e5ff33730c72e480477840042","b847785cfbdfc4cabab8c87499e50898834cf2f9fbd5407d3aeaccd6c234410d8824b1197afe7727177b54d6378c6e3dbc60fabdfc24b60f572ce0637b02089b","b847785cfbdfc4cabab8c87499e50898834cf2f9fbd5407d3aeaccd6c234410da2001a73143f80b336b2b05c3f95bbf88e8e6d9ff88783747f800203a4b84681",false -"2c42d0167ee5cfa88a0cc1c16fa0c9dc38c01d47f30e4c5423c707aecdd365aa","b97f1f0ce685329fce080aff4c9a17f3dfc6a14bfe9689290e9b89ec7f24bb5d","f06324b650ec674ccefa36c4e19c4e700fa09bedd1f7129c44745479dffab65d","0f638142959e8e6dc979685bb4292b646389db2ec418f87629bac97dc6c21478","f72df7b15f31c5abb0db5af07efc53199afe533af041512b47276d7ab90336be","6b117aabe372c146924929aa00990d7392eca59fc31b416a833a7e2a56ab73a8","f5b66cdfb1c916c9ceb705f8fceeee166d7aec4a71910562a1b19d4507f2518dedade54f20ed0f0345cc22a4374971ce64f4bb7cfd0e03574e0bab7f4dd1a051","f5b66cdfb1c916c9ceb705f8fceeee166d7aec4a71910562a1b19d4507f2518dfd4ac098d000a7b676d1ebdf55ad235d1002fc75da5f90f6c969b5923e0d2b35",true -"0570c938ab1e31af3244c1c8de80bfac01da36cb00708506aa9d6294b1020275","a162076078883fe9ce67f32a28eb637796fe5a20c3ee5a4dcf5795d54142152a","cb97db136151e7fd66b36622e2f6ac9f1244e951b5bf15f9a3f38aee73be4ce3","6ba7a5b30ac5bd0e5b7641830cb74367b41136823f8be7100e3c14d80b3eebcd","78ca0adc4d4d892c4bb8060c10b69d3b05630a5b5189877b9f20691897988174","4b8d63dc59a313c09548593d2002a5af4ca601b37a98bb299d8605062ea8c88a","febe05ae7e214eb33f6fabcf7bd4297b1b06e14f3bcb8170e3db5cc02aadb6d4538757ed880d9f67d61947202c7d6ba6e82ad5bf99f51b6c318009f26c59acf5","febe05ae7e214eb33f6fabcf7bd4297b1b06e14f3bcb8170e3db5cc02aadb6d41f1f3300e95f87653cccad430f7418473fc0e22aa06b912a15a136540fe1b897",false -"cfeb57aa91b08f7df563c54cd5c37fe9354c34c7a3a0942a73a1bd28d01ca785","6379d1dcf2da0e5c90e05d02b32a5c969dd21ea986f2df5a329ce2154c2f54fb","6c8f2a3b7bae9cd68637fbaac2fc1b62d5ecacd24ad97163659e7016ed79ad35","bcd420b56d103b29f50376335b959b76ff1102f319cc0e8c791615cac203c0fe","91d16e79b8869ae5d5cabfb4818405613bb9a89b552370309e44dc923c5071ae","fea792688415e4ea1927a8287dbf938aff1795c590cd89fc83a6190820f4a619","d5743de4c0be2194c9c3c7343e0e48a1f4a02627691933355f7ffcd5a2fcb0427bf317bebab7ded50ebf1b253845b43da1ad71cb5d0bd93f8fc895a7f2ebc3d5","d5743de4c0be2194c9c3c7343e0e48a1f4a02627691933355f7ffcd5a2fcb0420f63ed833f0941fe88871f7a754998dacbc0c4f9123267dc2a2a2591057216a0",true -"94c063c368f720e36575be96aa0075d73e669b9a0cb4188306d8bb4cb1cfb1e1","637cf87ec03dd68084474aa9f22bcc94a0985048d0a38b24e12b821f492ff4b5","c9fb6ba3efe3064bfd7625a53f254e90c59f35969ee3a65e110d6437fd24b435","c33096b024f6871aa9958e620501df9adc43760b6213e0a0783d143e1f36e8d7","68d398b891283b42e4bda770d3cb644be617e82a93cbd281ceae542d66e3bf55","54c1eec0ece9844fd5de9512e72b4e3452cf5472dc4934f0a7ef59323468d42a","e651742898c15685204152e9f98abb1d313ae35f4f2a156847de4ca9ce2fd9afd56205207bda7af8933d3ac71f5ef475aa03bbe8fe78733ba42f1271106a174d","e651742898c15685204152e9f98abb1d313ae35f4f2a156847de4ca9ce2fd9af9f5d70c46bbd814490b3606c5e844307b4f41498ee13795df56a181c3d588a41",false -"9e0278e0e6f8a0767dd81ecea025adf55dfaf913f9b4ff347115956fd0fb327d","535b5a59e5879920b490835ac9dbf23a82435bd083b2cc66be5be9997e2ce681","c47f351884757e03385b7c5a60d43895fa4b88dc9388d20c773c0aa6704b7ee0","93373b7570b569d3945cdc306b18b29b5f71a502f2636accb4a0b34be6d73203","8d96a34cf601b53ea3f0f1b0a5ca315f89f8d968f148c071560fca35ce97d1d4","e57adc1b2b869f8e07d97d2725fcffe19338c737a78c6d9112b34cbc5fa7c1b0","93f023cc065d5b422cbfe35b3a75e4a5aeb59b91ea500dac2cb95768519cb2b74b08551ea14588ec3a29e1d2d88d629721bbfbc7af3f5785dd7e41ca03f7d857","93f023cc065d5b422cbfe35b3a75e4a5aeb59b91ea500dac2cb95768519cb2b70f878a3725bb06ef72855e2d39619b2e6158a7bd937f895694e7ede3a40d15f6",false -"e29f4867a64d1199a19777f91c9ec2f9d15dd08a93409144ea0064cb60c76a3e","85524318955cb8c63933e6e0c06b8fe561861628548539bf760c2f1840cf8ae7","3b415bb6c72bce59fc88681de981ae96875d5cccb98755e195b3e4399340171c","f86e55a60819bc5b9ba93746753f4fc0db87c10de5b7d25f1fe242b3c1bb6ebd","1ec2865fb83a22cddf06a22491d861b685e987d115e89c4d49b93672944553df","fd6e924750d9359ff32feacc6e61991d6da690d312514b5b896bcb3365f05bdb","013a94ce22a0fed453e1e7959ca619d4a9676fe863aa3450cfd2081b9829b7cf69bee8aad31fb077fc38eeef227519f5a1ceb06692d362c11ab34351c8a15381","013a94ce22a0fed453e1e7959ca619d4a9676fe863aa3450cfd2081b9829b7cf2e7d8cf40bf3e21dffb086d138f36b5f1a715399d94c0cdf84ff5f1835613c65",true -"5745cbbd4a3f91aace64d6d5dfd81787b40c11f9c6a934bdf3fc80b2c8e28fb8","c99af68ee4b762410875e5bda87af74683541ed597966c64c96f8c486ff1f19a","627977f34498f34ed896fb5019fb9ffa2dc7dd238bf3777a3701186df35299c7","0a801b75ed3ae344de1b255f53930fc82fade6a0ba0e0b07d8b109a2b9cc3701","47406b77cca9234a06bdb4fc65ac76649e392ea780bdb81a6fc6cf3221f1354c","652839026578293d7ce1969b4d457739e722a2f5952857889da87a7d1b6f0f89","89a576153cf1bc3bc7ba7322b431a2c76851430fd4185d8369dc6ed24ef74495faca5ac0354f0a837a66214ea462f7f4e7212218776ca7ea37eb8b77ce53cd7e","89a576153cf1bc3bc7ba7322b431a2c76851430fd4185d8369dc6ed24ef744959850e2ccf0b61734a1cf25fe8a6757fab95944f4eb79307000ea7309db0133b7",true -"0c097d304c27a23e91e639407341eb33bfc90f74184130251bb0553dbe2de854","2b0868c00a5799f5686afe3550ca15d94ee2a6a81b1ecbda18e0735ca7ff7a56","6dc0f73722daa9eef9d0dcf931841a07f53c844b30a08c3f89898e619bc855f3","5c44f4f8428117f9ee1ad3711ec1120ba5a8545cc7d5aace491a215b49d617ec","52f98f0d574cae4db1cd30cb67a3e65c44dedb16f07c7bd728bf0fb83d0ad408","460d47e19edb129b81d62e6b59f76dd7bd4c61a725da3338d706c0e755ac2ae9","79beeded1b00c785dd0a466baa5b650f436ae169d0782b0d46baf5c481768c8e9c67dee427981c760ab8c6b90ce3d9f22d07007d16845eb9f80fe1dc948efbf7","79beeded1b00c785dd0a466baa5b650f436ae169d0782b0d46baf5c481768c8e0a28d61b4a72c6650489a3b23e67f3fb6794a7e197dc4abdc1c711b1602110a9",false -"b1b904d991387680de13c606565643e4930d2bf79b721db4530401c3b92cf3cf","63cdee00d9f07194399fe6518512073190b71dc2a94084681e49a172158f90ff","b6928d5994fa41ebd66a22a826cd6ad4015a23feab9ae45f1eaac309007fca73","38388ce16e2c5e1eab89b4e56233dacb58d4aa286ae2806d5debd117c9c529f9","6eab75f6bf9de09d363e4fc2447d0fc03feb78a5fb06ccb39daf648e13fd3045","41f2e0be320908f40428a195e471e425864ab7acbafdb11e642a68be1598c2c3","09cf78f977503f3a0e231d33155f1e65d100eedd0af68e5d40fa9e392138f2cb97acb68f70542aa1f6641f228d0df53b3a3930e82cac62d5cd466b89ca9049e4","09cf78f977503f3a0e231d33155f1e65d100eedd0af68e5d40fa9e392138f2cbe11a2935db59e8b61ff9fc7a66408a65f38de9d0305a1eb26e6e070d9a46c0b2",true -"7a444d39de9cf6852cf431a622af15acb852dce5034735166bdc4cccf6d984d3","52853e8d893caeb43c6ab322625641ccece988667bf9d64670a4df4750b83d7c","bf611892850c5e814215dfecd9629ed0de56376bab46fe361db5c0ba4fe916d8","103dd130009f424fdc25eeb5a1392a36fe4e008290267722ac24eb492db00ead","49a5fbb6bdb0cd167e5fb7b4b8544fcd87a3433e66292173c101efcd97756349","7666bd5dce4e4ad0df76672bcf6f8123adf4f5314894b808fbf739e3e3e687bd","6c9610699213a961ceb63a32459b5408c3a20035dc0c8b126b67d04bba9bce9abd8f6cb88cc6090f789d6726cda594f72d0335653a5b18c27655fb38dee47d56","6c9610699213a961ceb63a32459b5408c3a20035dc0c8b126b67d04bba9bce9afe2e542607b9aa8e36878739f442f625095bdae03e5cbac81872990b5f31a7bf",true -"c19d1686e76d5b777af0924e6154615abb7db030a22e407a397ce7e301d87742","0ba3c133ab7dee2839552fc9e43601ab3cc501c21891f2fd34fce78e43b63e67","2e5b908a7466490f23b30f5b75bbf10ba0d9c901a8fa1188f5ba6b1d07277174","5fba25cf7f5fb85e24900d14f789604dd0eca2eafa53ef2a41e55a79740709b0","ca543d2652c3ff13ac98641cb65df9dd0f719706363dcfa74592b373a638e308","da2cde0c16fd3cebf8c84ec28dbf1a951f43ff930795b610df8cec40749a25e2","4f9a66e8ccd5f1c68e906c7ffc8a5284065e2960a13c8af05fd671a4a2f8f93e39584c6b03ef20b2edf449b5f6fb7a00a42eadc9aba1f3652cd143d37651700a","4f9a66e8ccd5f1c68e906c7ffc8a5284065e2960a13c8af05fd671a4a2f8f93e0afcbbe08f88d7a3ca413a5a813f88f50354e4c802a7e1dc3716d8b66f29fe96",true -"29e362a7bfef5fb7f4918ec19e25befa8a4a43a23c8ec7c6028f97226773a7bd","2102f0eed266ed80ec7568cb20eeead902f0c35309f5ba7c1567b819a8e1bbb7","6c82609f56f6c11866cc83ee2498d551c17ae68d72904b4af73ff8e5f45c3284","f9f9be4fc4d63060a3c3c0656679f3d757775f8f8641a71f1525e53b57a1d99b","c78fc24af3b81ec075e1e1764b4d67ea07b5a9dda2e1504e159eb427025fb80b","15968a700ee7598db61ab47d210a27f7d0d82c5d25739b6141b095de9e00942d","676d626dad99593a9aaff30b64f38c513858b95a3f76269c211a1ff05525f39afe63ee86c0240d1f2bda706dcb386ab6b1a3d73cb44250f9fd409b764a4e2bc9","676d626dad99593a9aaff30b64f38c513858b95a3f76269c211a1ff05525f39a6ae64f26171ace3792a6f45befd14009b86fe0e37789fc0934ae35cf6e741d0c",false -"b01defc1ec85fdd6381f62ecb596239e91245b940b18489071526064a538baef","baab11fd4992c19e6a7692944ff665c08e1fb06c7a26b0f2ee2253fc9f7227e8","a0dfd48fa85f67b08b3fdbb9a6bb71c74b4239148f65f35caa37b1d1800fce44","025342259c0aa8df863a2f151ff0c2b1d6064997d1c54c42e5b62d033e83c7af","f5032f3bb2f6767a6f95b3559b74e14edde45ab47f8ff5c48542d748374c5144","08a35c8959bf4a10b85c75b7ef260d39f865876f4bdb66511b53dccf3f1a9353","3eaec73e10aa4b1f870d426dd0570dd5266ab32a93564393aad145dac625d9933033dc488afdcf7a9ac18515f96a4ac4e73db690bde3f49e2ce53141f437f0a8","3eaec73e10aa4b1f870d426dd0570dd5266ab32a93564393aad145dac625d993d113b0d8335d372b260160cfa025bc8c327fefa54d49e7fad71ce3137447beec",false -"dea7c5fe87a447632a53dcf40716ad99bd04d983405d851f44ff2671f8f8a6ea","07f04f60aa3367725401b8a813c0575007438714f3b6afa9150acdae4867f2f8","840da049a59cc5b8830e54aee306788b2b6104655f0a23a9404671f821fe9573","4ba30d52534bbed6bb67fa5e1bdfb1bdb983843a56887e43e652e89ccaae519e","d568dee1f5d4d1b400641fee5afd68e176e234acda24b0eb2516f739a8e87dcf","cab1f1f1229e93ad2efb76649c8091717ace3ba01461ac559295c5e0e006d07d","956bbdab0e1ebbbec714dd126a749b4f94bc7e55258eea4ce589e50ef0153370f1f321db528ee4f907e8b66903f080575efd5d11bb2518d9ec021e6b4c0e1156","956bbdab0e1ebbbec714dd126a749b4f94bc7e55258eea4ce589e50ef01533706de58191acf21f4084da61ba20ea07cc339c58ac5c1af530abbbac732a0f7be3",true -"17936333370f03b826196c144d2051b3da132aa00f8bb5c12482a577cad7c31f","54dbd2074d2787dc5a3e64f197bc7beb59987a4d3ed70fc818cd48ff08efeff1","059bcae22f27b9d0b66912406950cd96ac21c89b859b5f0fa401dc4ef9c6ff4d","951bf3b6eec23b6670b605a98998da6e38664d9f64cdd4f3573bc2b6f443b98e","28592ae85cb8d559e3bbe91793ad9c1463f03d7070d2e97d33b3c26d69d2fc82","2386074d572d93c77e096f6fc448521cc180f9257d9f3fb5347b7abaeb08c00c","4d081829ae9d9a9d560fa11e1122d780ef5c765719bb537d5daae655606d4814c38510e3df3375f4789be40145260d0aab790cc40e6c567e8face06b61ef0f7d","4d081829ae9d9a9d560fa11e1122d780ef5c765719bb537d5daae655606d4814c920dbc60e5b2fc52f04f641ae76daa1579ad55f9407b58e33aebcba5bb60eca",false +"01138d3785033d091a3fdb849d9a1291f5a6d1d24a81bdddf8881b10204e6136","02a13ad24a8a69cb200f25fbb73c2d3f403a0718ca4b02864b4c26b033cacfe2ff","665900ff1ca9e0bd80c12815122c6d928505e2b23e0d2121341fb1d9bf474f3c","03911b512d73c3602d8ae4455bee5cc82d442c00d9754b403f5b3eb83e98ed2ed9","a10616fb1ec1e3e212c30ccc0fbb5f8a3e37c9a821e35aed3e7431776ffd7c8b","eb41b70f96364551f2a8dea5177d8f7b0456dde9b21df232c9cfe1801b5d7646","99e9a06a6e865d6fa64aa4c9464e8215db7031d5a170433fe3b7d15ab5d10098fa3daffb44ed51687508d3e64825f8aaf7a4ae4ad6c57965d0caadc1e3f7b65d","99e9a06a6e865d6fa64aa4c9464e8215db7031d5a170433fe3b7d15ab5d100986096b0fa61973225f5c9fbfb5a52663ec1fbb4166589fa4b4518010ed308c458",false +"20778aa439316b57aa4637c831f974a3b87b2c4e6eb95f073fda0cfa79bbdaa3","02bf1d005c2b11c3bfac8f5d518ad7b4796b6dcc37f418dcc9ba79d22abca18746","86e8589b1adfe4421f98dd88031345a3b3d0c5bc92fd50c4c50df38d64afd274","0255ac067d96a6c45deef413141ae55a7a00ca277b13bee4bac667fced556ead18","dc909e9617b5e440b3e0a2e85a7c82cc2e31e3fc650a6b119e60c65e98f0118f","20eee2ddffddd43f02d884b1844ecc6e387cc25300d0f835ff4973b2126a514b","22e48a3678437d52795bd83000ff0a2510db0cd0de65d3c8b25b222b0632bebbd6d19cef77faa6e6c60412b3782f8092745486b49476472fc1410da6c3510f00","22e48a3678437d52795bd83000ff0a2510db0cd0de65d3c8b25b222b0632bebb4fe944545d1ac2a4a66b352b751c3aeec083c0f80178f66afc331a195ea13c8c",true +"7a34b0f93dbbd0c91b588a16fe4bd5d23f1903671f6de4403213cc3ce214ce38","027fb795e7e29f605bca05761496ef55de514d1d4cb67b1bee84597ded249ab024","6828ca09e3098a5a71b3be510794c1159c0eff6db384a830575c62a0006eaae0","033dd2db0ac9870447c63d4003eb183b0545fb86ccf929111666138c31bae62a28","be4a83480af33dd41908f009b2d9d4cb218d230acb7c1d48c40a97985e2bb2a4","4a1d8c39d92fa7405efe687837683cbc8e20f01621132161324e78d8b6c469f5","1021d59e8395ed425dc40625f2fea249549a86621a2edce57a114bbf8a1127622cea4b231d718eb55b2a253ce9dc23a1e76824d74ec8174951fed0fec49de82a","1021d59e8395ed425dc40625f2fea249549a86621a2edce57a114bbf8a112762c4c181193a68045ae97666ebe247628b060802504a8c0f54ba74cceb94657e8b",true +"547abc10d9ad870d554edc78028d21893f915d718b5d7849fa127a37213cddb7","0217f7e8f23c85208c10e06d55e90abafcd0bdbda49098a63a11cba7c1b16cc4b0","65c8f90e572f1828c167377f64f40796ee8aec5ce2baee05c762cf812f33883b","0269aa06097d53b56a77e39b46cde9b337084fb70a1f8c610518bb3e9c6b228445","a4c968687391d781c22729a941b6564df3c94a8eb9433f6b73f0eebba0e94318","d9fa09c670a30b031a1e0e3f72743506044f6b39a89e73c0bf87d6accd5b94ad","593acb43862897e5af5f5429e9e9142d09c75378ec70128f700c869cb6bacbfa0d527137644c31169cace2948c580d7a96347a6637b61f95ffcc6ec513b88d03","593acb43862897e5af5f5429e9e9142d09c75378ec70128f700c869cb6bacbfa731b6a45bb7b493f5e141a13f14c151184bf66c31a710d9bc72f3e4642ec153e",false +"593cc37fd28d28005adbedfcdecd18a071330e7ad7d853d738c77c61bbb83ff9","02c923dae361f1bc224a246a0592000c051b03b821ab26f5edbf69db6256d8a665","467b336868572cef5496b4f011ebb73839c7cf0c7763f1161eb1bfbdeec40d26","03d31be8ac7b423907dfe5c9834d3900247ac43b7dcadc8465ebc54b128c8910f5","81f3576f1a142901d056da8588b164a8464efa4f41cbceddccdc6f235ce49f59","86d81f4359857bbf39fbb128103d3bad5f46da4a52eea554f30d5954cbde7cb4","c435b682a7763d516da6e6483668e56fcef6ded57ccfd90ef225270d47cdc3f718482a85147e73108256664fd3eea00f5fd74042d8ceb331b678d631500b2712","c435b682a7763d516da6e6483668e56fcef6ded57ccfd90ef225270d47cdc3f7d1ccf71cac2746212dbfb15fc202e8d5e0be4e1d10b3625757997500317d5b2d",true +"2eba8f149e45f2af5c2afe5f51990d204cf240d22fc9e2078c48d480cf5ca6bf","02cd768be3e2d46d7734ebb40c87c08177b16abc092a6ae89d0aae0ff965dff8eb","ba297393b1c9aa874ba18f0351b56d52ead9a2d0a740be41779e9fe9cac59734","02b86184c2c2ab98441bb98c63bf0d98d38b8aa7e25ade0dcc116f7f6ff6c0a4db","47966fb320c357639f981bc883cbfc84d23153f09d88aa671a21a6199690ba8f","40fb47e380da85678b0ddd36c99c049f31c912ea83efbca9bc069a45151aeb4f","34bddf1a2bcc8893189cc3f719393b83d8077dbe85a8ce2bb02c4847815260550f31a7e7d3ef1661db2d70d2e408948fb21739ba17fe6a8f970559a0b4ba6bf0","34bddf1a2bcc8893189cc3f719393b83d8077dbe85a8ce2bb02c484781526055c95b1b7b85b8c0e926ceffd635be01e29cf0dc8abf3f28d10ea3f98a7f800324",false +"11ca0283f12577e4f37d0addd595c16a24ecba5ad9e6c2c6ea484525507ee953","028aad61e58c99b051ba6f95eb920e293d0f6d6fe4b9b1d55dff16b2ec368c587e","c6b5be059c076f99855f2854ac597e3d5a43310c6442592e1e5d1a8dbfaef62d","0317409274a1cf439bb11c3c0a01a7a7fbcec7ecefbee1330dc7fba93c530bc4ea","6fa2ad2be58417feca91d9c7d79b1077becd0c60a002be0e3fefaf86dc63a4cf","909676b82df8909a1a86b0a470f1fd5865a1fbc615381ead571b0b25c0b7da70","91d927a751000f0c7c72a76bf0ad15a315b131205ca964e03f8aec6925eadc8d07d3f00cad97c89b46a42fd84d47669ded22c11bd73e1ecc78fce1e2d77817bf","91d927a751000f0c7c72a76bf0ad15a315b131205ca964e03f8aec6925eadc8dce89ae12499f3834cc03582cf9a0e4db4765f2283b8077fa9759fc7097270dec",false +"29e17ae4aa2b13df44c3131c6c237c29be522e2a6eeb711a8d1f24eb137aaf0d","02edcdb8d9f4eaf8f2c9be1314886d56b1ff9f7a1892f86df38c366533356b5452","32f2f3121b5d265815c0527bea181b23e87e0d594e3af6d263eda72e7c72f645","032eba5528c77755be8ceb944dd1e886ae747c6e1390b90235941dc6e18ba7c9de","5be83e6ada36f33de34535159ab5c4fde57a60826eae6a20547a8a8203cdae8f","540c1ef21bd5a46c0a6e31a1922717aeda414903c07ed68060f17df40e7d29a0","ce472e6c3d7e75ec2683a295344c12890c2f99ddfab53a292ed1817e10f9f1069c2963013648549d786aeeca133da9ed186ff51e43a05eaa193f98743a955657","ce472e6c3d7e75ec2683a295344c12890c2f99ddfab53a292ed1817e10f9f10669366fef1aeb2e4562aa9c4e29258ec92ff1e7c4f56567d7b551f145be226012",true +"3504bee0a1d65cdbed68e8decfe9075dca5db9dc97b6aeff701ff6dac421c5c1","02c016e5f5b76175063f6336480320cd7dc23076a6d700ae34cfdb48268dac65b9","a2113b65d633a93e4196c3d68dadc7e227f8dd9d06e4d2bc5b5a3ac55ee567f5","02abbee001326b50704b9b17efcf98702dc74600749d0730328d2d5f04ccd2cde1","fdd6b40c8bb467cfc316d310ccdd0ef9ec6efd89932bccdfb8d1381e96ff5480","fbb700a915376fda2a157861af255025b510694919cba7fb5fd7a7f0744b65c1","e2b92af7dc9e15c4db7eae369fa8ae47283723930ccdd5b61848864cf7c9fffbc83aa93cdce4a1f0027b29fd82c5293c59bfa277c08ad4c48e4a243557e06265","e2b92af7dc9e15c4db7eae369fa8ae47283723930ccdd5b61848864cf7c9fffb26296dd706b0f8b1c0e46626f517615a31c6c4dab9a6020832efe96ff8fafa70",true +"84ca840ee3800300d023a01a439fbe62051efa7711781d5e17dccf24f8aba61c","026688fd24a8d11df5e166b14edab4083718859bd97928a70e05d8673ade2e8c16","fbfe17a13ae16e0adf77cf96e9af145d2fc02580ce19cf98023a3b5f9ad04f85","0221e822b2b559396c6eabdc9a5d2cff7c27861287c4ee0579a9ea435fa6b0b8d6","35cc81ff675a06d4042035116930fd52dc0a8512066e65fa454ac1f05eca8dc8","fc98beeab724efccb4ea955d51ce5cc300fad15e2811a7917fed46dc04639285","b42581ccd0779f6371209ff597344213e8e48851b6c95e80c51a75736f88d0ac078c830c97fd15e9d42ac9dc4038477df6dc171a862061bed25012d1541e4759","b42581ccd0779f6371209ff597344213e8e48851b6c95e80c51a75736f88d0ac0b8e6b6b5d1ba7def4b2fa455689331f81cace80674f32628fe835fe89843915",true +"79a7b3f13d359db306ea1778dd4c7b891793e5e57025b35e8ed7895d570b86ea","02ea1a0c4081c7a9f86352e00b135e6fd2bd0ad17c50fd22a9517472e62af05380","be025a17089052495761ef09365783b24ccf6844bf476ce54f445d0416f512b2","02987da0d941a03f75cdfe7582b90e58a80d0c80bf3073bdc42f8dc65b94da05e0","8f62d16c18211ee53a15f200c24a7e4020a04843f62c27e3ae263c1f795b5e61","a32a6d8dbd4976e31474a405592bf6280b799597e6e946203a4ac12062a29f06","010ac477501ecb0ef5b4bd8b81d1ab09de7fd1c790153e1af59101f53492a07fcdd686259bc74c3828a07a2149b8d2f3601c67133bac4672912021cff27bf547","010ac477501ecb0ef5b4bd8b81d1ab09de7fd1c790153e1af59101f53492a07f0fd42c0e9336f9eed13e8b1813614f41134cfece7c64d98d41dbc4cbdb86e295",true +"b12d7d24c6f0b0afd29b43cef391e02b5182356366344f60c12f361c44eb0c1b","02f2c0830b948cfc2b7ac9aa7c6390955e0d6e20fd67930e5f6a87b8904ee57ae0","1a41e58047f2ecb0a60028d491355b2c2dca60b9894d624332f2c707220117e4","027b6ee8e1bd3272edcc9ba857f0a1d677a455e8cb18258866508a591d9f4fda39","d48ae86c6552cd7c5d536cd4bd28520ff89fc328816fe2d4ccbdedbfbbed10d4","7e6aba5e4f518dcd4d8e1ba1289e3d5b2fef7c5e7b4b94b0b4afe38557f07bd2","19d508f771a4db7e5a1ccce0d5565686bf9c9ff506650c48c6c057c7e725042fbe9c7badacfdca549b4c484e712567233ea4a17021ceba8ac9e804d753baf13a","19d508f771a4db7e5a1ccce0d5565686bf9c9ff506650c48c6c057c7e725042fd8de612df4f0b705414c7123025ac24f6c6f0229ab1c1ccdfcdacbde75bc091e",false +"d671ee3655eb17d4512d896d851953ec8bbf5b8cc7bacd005e5d3b6442d264b3","0269aafad3bb6e1d2694a2570d7e1513e0154b1f1c7ad2ae9fa7d926bbefae213a","5152217b5100c40ebf949d863f101ad72e1d51e24be6b7edc222c26933b0e20c","02acdd14736689da6d7cd8f200ab26759ebf9c65ef1207d17488c3b3b82fecf52c","28c2b4a6f7c5e7499beb923caf7d92646c0b11af4af6873f0d52c9c10e8150c4","a5fcda2417c758bac50a05e2e29cea8ec69a166e70496ad6cdddcef0c23bc5a2","ada74b4cf2f6f67c91c8f3011a612cb44f3b3760bb139d9d80fa5cd7657d53bfb7877e86424e3faa3f123162cd0b4479027be74d40a9569de0da29402e22e0da","ada74b4cf2f6f67c91c8f3011a612cb44f3b3760bb139d9d80fa5cd7657d53bf08d9a001934f03b8fea6cee90c1b5f5175ea5c48dd476e4fe32a8d1c919d81a5",false +"29d8887c2b4dc6ad8b4e96dacd1798ad7bc1f67e458386e2577c13fce3faf282","02842cb699537d1a0d46c5728a7341890c767b17b38cd49c758874dbd65ec698c3","332bc36dfc79a7369eaf37bf1637db0b740cdd1079e212d1480178f20a3d0176","02cea3bc0b5ec890822f52e8eba87690d1d92012f86622229fb5537db321a371ed","8bc125e47d1b0593b721656d01dd4f1f68ddfc3b1a2758197569404d0bd0836c","00f1be297d9ee2b7559d61b8dc8e9a781366bec75e126acc53b8bd0e3c84755e","c4dc81aae55b1e645bc1d45f5b30e18079f36d27449268919ccf889f89b8844a28f4c4d83ca844df41f97544dc1e8ce70db632b388a96a2708cd0994be7882f0","c4dc81aae55b1e645bc1d45f5b30e18079f36d27449268919ccf889f89b8844a5c2088463921ec15e0a8ad03f25667f281c30fc4028b7cf850ce8286c8b58466",false +"feda1cf986c989c081e1c6dc8b434715ee2b00f28a0823577a56c8a64e979736","02eaba7f222004910e7be62ed1c8a78e9dca7e6b31fdd5c5e4d0751d239bd63a20","06ab57ab98caad717e6a53baf5d1202ed25b258dec3a96a32200ac9eb78d4fa6","023238fe1444f7038dbc56fdf20246ff3c133a1053caa9aea700a63fe8eea172d9","79d4fb0e147bdb23b4b82d73fcb937d63bd61d94e6ad469c77a0678a3e5c15ed","66a8ee476af678dc112ff3aef4388e3f26b68db2cbe7c545d40baf1d8062447b","1e03ce47176eca24579c88907f17acdedea160054115f59a744822b1a06d68fb87219dacf6d9e6afe333a79c40892b8b1ba7c7151711d655f02110ab3bef2a4b","1e03ce47176eca24579c88907f17acdedea160054115f59a744822b1a06d68fb8dccf5588fa49421619dfb57365a4bb9ee02eca3034c6cf91221bd49f37c79f1",false +"723ec1fac7ab8087de8f3d6cbcd7fce6ad497a32683406031efbbb534af5e4f0","02ad3e90516e2aa3a4d848ca71848f8bef22b6449ca8ff719346717c3d08c022a0","7454898bbb9418ad0287f9861b0b9245d4ba592fa2d263eae113ca5afda3a6ac","02eb538ee72c2672479361070b692f2bd05536e4f535431fed7167262d05ee6393","12432a48bd8f9d319c2673fdd7e16ddf60d2300b984da1f24fa53f1a0149f4b0","1174a245a991e949616646358d4bd79f561289343a9929a1adbf245c8efdba51","fbb9d7d05da1d35097619164867b20267a69171b8e7d2c47ea11a7076abbd2cffb74559a030c3d7c804af55698f18f1bb30dc4b707b2dae19033cb57b2c3ec4d","fbb9d7d05da1d35097619164867b20267a69171b8e7d2c47ea11a7076abbd2cf6fc8df25bea0562982d2eedcb3fd2162cd1940fffb3c9e90b1753725e03151b8",false +"ce1ed0fc2587a09dbbb3edbeb1e7d2260e694e47219ff0b5803e3633fb6e3b8b","0297fce00d0ae1a7ca4a21a248bbb56d02236b4d9071589dd83a7fda160cab6a17","0df5e4cd6a732400b909810cb2423c81231b42c1b8de953aa0abc93acccf79ef","02a08986f8334a064dd81f79e0d46a451395f901401e0dc9c3dfbe21fd6fd803fa","31432c7343d699c883557306ff017beae7ce991c525edacee0fae6cd77693f27","55bd3426ff7f94c16a439d1d7a3ae8975e2eb660ddf8695c1b1cfc0bcc711b8e","41d93b7c672c3e43dd2c8f220acb5ac7dc05a787f8eb51ba74a75272b68bf3fe5b5d97b2d663ad2c36466345d7508bc815034f9f98accdaedff225301f038efe","41d93b7c672c3e43dd2c8f220acb5ac7dc05a787f8eb51ba74a75272b68bf3fe69537c8040d6d12cef4fe4528992c849381e9261518b62e9809dee6aebd308ed",false +"a99059144b0309a1ae1c742225b10fdbbf3cdd4e89536f91c227d58175220dd4","021c840d44397a1598ffc1b967acadab2a49308ce4b2749725a3f0af7887e63f82","82e0a36bfc1d3c1fa16272abbde846f6573c7b50c1f1d1dbc45bd6d3f9237da8","03e1bc5c665861bc71da5e12509b96bcbae9f8f3faad490155fc55e00fa5313712","fd8d2ec5dcacef0072f4666268d0877c5a46f5863b4f82a579b4ccfda67c4ced","6a4c6660d79d46131e1605a25e9b8b17a8d3646e53b4d42bc49fafd26e1bef3a","662d14456a5ab31a1012cecc068b141ca8d41757e73ec703aacc955bb54e4e433560e1951c0277557426a7310dde160b8c01bc083f424e7631b844dedd38280d","662d14456a5ab31a1012cecc068b141ca8d41757e73ec703aacc955bb54e4e43b2803e291fe53b35d2c434854ff5cf13ef741d9e2c991cd62d2ecc97b44aeba6",true +"e761546d8eef5cd892957115b0e896fb066d28b14179dff8bef75735ed968011","02a09f5a0da4c7c66d5d83b6427e64100762c342ebfd8825eb8050bc6db0ffc333","a739f4b46219bd5b6f2b8d0c945ec0beee7591d2dcdc32b44853d60a6cc19476","026167160fd64b079f5b47735c2c5e27817a8cb877e3c03c940b9808b0d87ccaf7","4ff6144e72807179659e93c8b786042a5fd2bfb08785b293eae5833cf5b490ed","5d815476dca33ef58022bae294b784d0757c80ed49dc20fc86bf7375f345c30d","6e2c6e1ebb9e22e6592bb2f0ca126b09f167d1c0c29d6f8296fe65db07a5b69ea6cdc7f44c2f71bc54be5b8051a6ced0f36c0db67169485500c67e45bd6c19f5","6e2c6e1ebb9e22e6592bb2f0ca126b09f167d1c0c29d6f8296fe65db07a5b69e4e07bca8ae492f17c3e9e88ce6058f912732c2a29efcdacd8947f5c359f76d2a",false +"1fe1a3cd19735058b4517c13f7d225a0dd0b7176e8b970f72337dfbe92ea1394","026c605773a227b87c6bffad7adad8b8699a2b3b84d61a874e12bc7ddaf4051daa","c013f96c065555a9185ed60e3a37edaa9b37c83a9a87ea164075822c2e962414","0362f76db1fb4049445b220cde65785502e22aff575baa8444bdff70320a7137a6","77e0910f8dd5b3a54c54b11106323848e26dcd142209f16206735665ae740651","0430912f3219a070d6f14d8ed4bea2c961ef002efe1815cd1fec456562fec2af","2cf0452d6c63c38f4396099c1fe24843757c5acc83f9ed105e93f2ae11b65d217dc9110ff8c01b5a5b9b5e6259836902baaa6a7bcfb9dd7571680cb5b9bfb8e2","2cf0452d6c63c38f4396099c1fe24843757c5acc83f9ed105e93f2ae11b65d21bdb517a3f26ac5b1433c88541f4b7b56da217f27e47a939af0c4e9165b5fd60f",true From 932687b1f887d05d1f15c53dea556233b06e3ee6 Mon Sep 17 00:00:00 2001 From: Sachin Meier Date: Tue, 31 Jan 2023 23:03:19 -0800 Subject: [PATCH 13/16] do not assume tweak is even in tests --- test/secp256k1/schnorr_adaptor_test_vectors.csv | 1 + test/secp256k1/schnorr_test.exs | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/test/secp256k1/schnorr_adaptor_test_vectors.csv b/test/secp256k1/schnorr_adaptor_test_vectors.csv index 2fae775..1d77803 100644 --- a/test/secp256k1/schnorr_adaptor_test_vectors.csv +++ b/test/secp256k1/schnorr_adaptor_test_vectors.csv @@ -1,3 +1,4 @@ +private_key_hex,public_key_sec,tweak_secret,tweak_point_sec,message_hash,aux_rand,untweaked_adaptor_signature,tweaked_signature,was_negated "01138d3785033d091a3fdb849d9a1291f5a6d1d24a81bdddf8881b10204e6136","02a13ad24a8a69cb200f25fbb73c2d3f403a0718ca4b02864b4c26b033cacfe2ff","665900ff1ca9e0bd80c12815122c6d928505e2b23e0d2121341fb1d9bf474f3c","03911b512d73c3602d8ae4455bee5cc82d442c00d9754b403f5b3eb83e98ed2ed9","a10616fb1ec1e3e212c30ccc0fbb5f8a3e37c9a821e35aed3e7431776ffd7c8b","eb41b70f96364551f2a8dea5177d8f7b0456dde9b21df232c9cfe1801b5d7646","99e9a06a6e865d6fa64aa4c9464e8215db7031d5a170433fe3b7d15ab5d10098fa3daffb44ed51687508d3e64825f8aaf7a4ae4ad6c57965d0caadc1e3f7b65d","99e9a06a6e865d6fa64aa4c9464e8215db7031d5a170433fe3b7d15ab5d100986096b0fa61973225f5c9fbfb5a52663ec1fbb4166589fa4b4518010ed308c458",false "20778aa439316b57aa4637c831f974a3b87b2c4e6eb95f073fda0cfa79bbdaa3","02bf1d005c2b11c3bfac8f5d518ad7b4796b6dcc37f418dcc9ba79d22abca18746","86e8589b1adfe4421f98dd88031345a3b3d0c5bc92fd50c4c50df38d64afd274","0255ac067d96a6c45deef413141ae55a7a00ca277b13bee4bac667fced556ead18","dc909e9617b5e440b3e0a2e85a7c82cc2e31e3fc650a6b119e60c65e98f0118f","20eee2ddffddd43f02d884b1844ecc6e387cc25300d0f835ff4973b2126a514b","22e48a3678437d52795bd83000ff0a2510db0cd0de65d3c8b25b222b0632bebbd6d19cef77faa6e6c60412b3782f8092745486b49476472fc1410da6c3510f00","22e48a3678437d52795bd83000ff0a2510db0cd0de65d3c8b25b222b0632bebb4fe944545d1ac2a4a66b352b751c3aeec083c0f80178f66afc331a195ea13c8c",true "7a34b0f93dbbd0c91b588a16fe4bd5d23f1903671f6de4403213cc3ce214ce38","027fb795e7e29f605bca05761496ef55de514d1d4cb67b1bee84597ded249ab024","6828ca09e3098a5a71b3be510794c1159c0eff6db384a830575c62a0006eaae0","033dd2db0ac9870447c63d4003eb183b0545fb86ccf929111666138c31bae62a28","be4a83480af33dd41908f009b2d9d4cb218d230acb7c1d48c40a97985e2bb2a4","4a1d8c39d92fa7405efe687837683cbc8e20f01621132161324e78d8b6c469f5","1021d59e8395ed425dc40625f2fea249549a86621a2edce57a114bbf8a1127622cea4b231d718eb55b2a253ce9dc23a1e76824d74ec8174951fed0fec49de82a","1021d59e8395ed425dc40625f2fea249549a86621a2edce57a114bbf8a112762c4c181193a68045ae97666ebe247628b060802504a8c0f54ba74cceb94657e8b",true diff --git a/test/secp256k1/schnorr_test.exs b/test/secp256k1/schnorr_test.exs index 228d9c1..fe4a8aa 100644 --- a/test/secp256k1/schnorr_test.exs +++ b/test/secp256k1/schnorr_test.exs @@ -165,7 +165,6 @@ defmodule Bitcoinex.Secp256k1.SchnorrTest do # tweak tweak_int = :rand.uniform(@n - 1) {:ok, tweak} = PrivateKey.new(tweak_int) - tweak = Secp256k1.force_even_y(tweak) tweak_point = PrivateKey.to_point(tweak) msg = :rand.uniform(@n - 1) |> :binary.encode_unsigned() From 289e49c94cf34d4ad31ac52cb6ff4b7f919307c5 Mon Sep 17 00:00:00 2001 From: Sachin Meier Date: Thu, 23 Feb 2023 23:05:15 -0800 Subject: [PATCH 14/16] lint --- lib/secp256k1/schnorr.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/secp256k1/schnorr.ex b/lib/secp256k1/schnorr.ex index 824d286..b09c4e8 100644 --- a/lib/secp256k1/schnorr.ex +++ b/lib/secp256k1/schnorr.ex @@ -161,7 +161,7 @@ defmodule Bitcoinex.Secp256k1.Schnorr do """ @spec encrypted_sign(PrivateKey.t(), non_neg_integer(), non_neg_integer(), Point.t()) :: {:ok, Signature.t(), boolean} - def encrypted_sign(sk = %PrivateKey{}, z, aux,tweak_point = %Point{}) do + def encrypted_sign(sk = %PrivateKey{}, z, aux, tweak_point = %Point{}) do z_bytes = Utils.int_to_big(z, 32) aux_bytes = Utils.int_to_big(aux, 32) d_point = PrivateKey.to_point(sk) From 9bd20fa800437d221dccf4d09699bc3c2347151c Mon Sep 17 00:00:00 2001 From: Sachin Meier Date: Thu, 23 Feb 2023 23:20:21 -0800 Subject: [PATCH 15/16] lint --- lib/secp256k1/schnorr.ex | 52 +++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/lib/secp256k1/schnorr.ex b/lib/secp256k1/schnorr.ex index b09c4e8..f6f49fa 100644 --- a/lib/secp256k1/schnorr.ex +++ b/lib/secp256k1/schnorr.ex @@ -35,23 +35,20 @@ defmodule Bitcoinex.Secp256k1.Schnorr do tagged_aux_hash = tagged_hash_aux(aux_bytes) t = Utils.xor_bytes(d_bytes, tagged_aux_hash) - {:ok, k0} = calculate_k(t, d_point, z_bytes) + case calculate_k(t, d_point, z_bytes) do + {:ok, k0} -> + r_point = PrivateKey.to_point(k0) - if k0.d == 0 do - {:error, "invalid aux randomness"} - else - r_point = PrivateKey.to_point(k0) + case Secp256k1.force_even_y(k0) do + {:error, msg} -> + {:error, msg} - case Secp256k1.force_even_y(k0) do - {:error, msg} -> - {:error, msg} + k -> + e = calculate_e(Point.x_bytes(r_point), Point.x_bytes(d_point), z_bytes) + sig_s = calculate_s(k, d, e) - k -> - e = calculate_e(Point.x_bytes(r_point), Point.x_bytes(d_point), z_bytes) - sig_s = calculate_s(k, d, e) - - {:ok, %Signature{r: r_point.x, s: sig_s}} - end + {:ok, %Signature{r: r_point.x, s: sig_s}} + end end end end @@ -171,19 +168,20 @@ defmodule Bitcoinex.Secp256k1.Schnorr do tagged_aux_hash = tagged_hash_aux(aux_bytes) t = Utils.xor_bytes(d_bytes, tagged_aux_hash) # TODO always add tweak_point to the nonce to commit to it as well - {:ok, k0} = calculate_k(t, d_point, z_bytes) - - r_point = PrivateKey.to_point(k0) - # ensure that tweak_point has even Y - tweaked_r_point = Math.add(r_point, tweak_point) - # ensure (R+T).y is even, if not, negate it, negate k, and set was_negated = true - {tweaked_r_point, was_negated} = make_point_even(tweaked_r_point) - k = conditional_negate(k0.d, was_negated) - - e = calculate_e(Point.x_bytes(tweaked_r_point), Point.x_bytes(d_point), z_bytes) - s = calculate_s(k, d, e) - # we return Signature{R+T,s}, not a valid signature since s is untweaked. - {:ok, %Signature{r: tweaked_r_point.x, s: s}, was_negated} + case calculate_k(t, d_point, z_bytes) do + {:ok, k0} -> + r_point = PrivateKey.to_point(k0) + # ensure that tweak_point has even Y + tweaked_r_point = Math.add(r_point, tweak_point) + # ensure (R+T).y is even, if not, negate it, negate k, and set was_negated = true + {tweaked_r_point, was_negated} = make_point_even(tweaked_r_point) + k = conditional_negate(k0.d, was_negated) + + e = calculate_e(Point.x_bytes(tweaked_r_point), Point.x_bytes(d_point), z_bytes) + s = calculate_s(k, d, e) + # we return Signature{R+T,s}, not a valid signature since s is untweaked. + {:ok, %Signature{r: tweaked_r_point.x, s: s}, was_negated} + end end @doc """ From 3a48dbdb653b03e57fad5120ff12e4eed94591e9 Mon Sep 17 00:00:00 2001 From: Sachin Meier Date: Thu, 23 Feb 2023 23:32:35 -0800 Subject: [PATCH 16/16] lint --- lib/secp256k1/schnorr.ex | 41 ++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/lib/secp256k1/schnorr.ex b/lib/secp256k1/schnorr.ex index f6f49fa..01391c9 100644 --- a/lib/secp256k1/schnorr.ex +++ b/lib/secp256k1/schnorr.ex @@ -163,24 +163,29 @@ defmodule Bitcoinex.Secp256k1.Schnorr do aux_bytes = Utils.int_to_big(aux, 32) d_point = PrivateKey.to_point(sk) - d = Secp256k1.force_even_y(sk) - d_bytes = Utils.int_to_big(d.d, 32) - tagged_aux_hash = tagged_hash_aux(aux_bytes) - t = Utils.xor_bytes(d_bytes, tagged_aux_hash) - # TODO always add tweak_point to the nonce to commit to it as well - case calculate_k(t, d_point, z_bytes) do - {:ok, k0} -> - r_point = PrivateKey.to_point(k0) - # ensure that tweak_point has even Y - tweaked_r_point = Math.add(r_point, tweak_point) - # ensure (R+T).y is even, if not, negate it, negate k, and set was_negated = true - {tweaked_r_point, was_negated} = make_point_even(tweaked_r_point) - k = conditional_negate(k0.d, was_negated) - - e = calculate_e(Point.x_bytes(tweaked_r_point), Point.x_bytes(d_point), z_bytes) - s = calculate_s(k, d, e) - # we return Signature{R+T,s}, not a valid signature since s is untweaked. - {:ok, %Signature{r: tweaked_r_point.x, s: s}, was_negated} + case Secp256k1.force_even_y(sk) do + {:error, msg} -> + {:error, msg} + + d -> + d_bytes = Utils.int_to_big(d.d, 32) + tagged_aux_hash = tagged_hash_aux(aux_bytes) + t = Utils.xor_bytes(d_bytes, tagged_aux_hash) + # TODO always add tweak_point to the nonce to commit to it as well + case calculate_k(t, d_point, z_bytes) do + {:ok, k0} -> + r_point = PrivateKey.to_point(k0) + # ensure that tweak_point has even Y + tweaked_r_point = Math.add(r_point, tweak_point) + # ensure (R+T).y is even, if not, negate it, negate k, and set was_negated = true + {tweaked_r_point, was_negated} = make_point_even(tweaked_r_point) + k = conditional_negate(k0.d, was_negated) + + e = calculate_e(Point.x_bytes(tweaked_r_point), Point.x_bytes(d_point), z_bytes) + s = calculate_s(k, d, e) + # we return Signature{R+T,s}, not a valid signature since s is untweaked. + {:ok, %Signature{r: tweaked_r_point.x, s: s}, was_negated} + end end end