Skip to content
This repository has been archived by the owner on Nov 21, 2024. It is now read-only.

Commit

Permalink
style: improve bdhke readability and error handling (#31)
Browse files Browse the repository at this point in the history
  • Loading branch information
tdelabro authored Oct 21, 2024
1 parent ac4a435 commit 7c18f20
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 55 deletions.
88 changes: 41 additions & 47 deletions lib/cashubrew/NUTs/NUT-00/bdhke.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ defmodule Cashubrew.Nuts.Nut00.BDHKE do
alias Cashubrew.Crypto.Secp256k1Utils
alias ExSecp256k1

# Cashu parameters
@domain_separator "Secp256k1_HashToCurve_Cashu_"

# secp256k1 parameters
defp secp256k1_n do
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
Expand Down Expand Up @@ -63,23 +60,26 @@ defmodule Cashubrew.Nuts.Nut00.BDHKE do
/// never happen in practice).
"""
def hash_to_curve(message) do
:crypto.hash(:sha256, @domain_separator <> message)
:crypto.hash(:sha256, "Secp256k1_HashToCurve_Cashu_" <> message)
|> find_valid_point()
end

defp find_valid_point(msg_hash, counter \\ 0)

defp find_valid_point(msg_hash, counter) when counter < 65_536 do
defp find_valid_point(msg_hash, counter) when counter < 0x10000 do
to_hash = msg_hash <> <<counter::little-32>>
hash = :crypto.hash(:sha256, to_hash)

case load_public_key(<<2>> <> hash) do
{:ok, public_key} -> {:ok, public_key}
{:ok, public_key} -> public_key
_ -> find_valid_point(msg_hash, counter + 1)
end
end

defp find_valid_point(_, _), do: {:error, "No valid point found"}
# Not supposed to ever happen
defp find_valid_point(_, _) do
raise("No valid point found")
end

@doc """
Alice's step 1: Blind the message.
Expand All @@ -88,14 +88,15 @@ defmodule Cashubrew.Nuts.Nut00.BDHKE do
Alice sends to Bob: B_ = Y + rG with r being a random blinding factor (blinding).
This operation is called blinding.
"""
def step1_alice(secret_msg, blinding_factor \\ nil) do
{:ok, y} = hash_to_curve(secret_msg)
r = blinding_factor || generate_private_key() |> elem(1)
{:ok, r_pub} = ExSecp256k1.create_public_key(r)
{:ok, r_pub_compressed} = ExSecp256k1.public_key_compress(r_pub)
# B_ = Y + rG
{:ok, b_prime} = Secp256k1Utils.point_add(y, r_pub_compressed)
{:ok, {b_prime, r}}
def step1_alice(secret_msg, r) do
y = hash_to_curve(secret_msg)

with {:ok, r_pub} <- ExSecp256k1.create_public_key(r),
{:ok, r_pub_compressed} <- ExSecp256k1.public_key_compress(r_pub),
{:ok, b_prime} <- Secp256k1Utils.point_add(y, r_pub_compressed) do
# B_ = Y + rG
{:ok, {b_prime, r}}
end
end

@doc """
Expand All @@ -107,9 +108,6 @@ defmodule Cashubrew.Nuts.Nut00.BDHKE do
with {:ok, c_prime} <- Secp256k1Utils.point_mul(b_prime, a),
{:ok, {e, s}} <- step2_bob_dleq(b_prime, a) do
{:ok, {c_prime, e, s}}
else
error ->
error
end
end

Expand All @@ -129,14 +127,12 @@ defmodule Cashubrew.Nuts.Nut00.BDHKE do
Carol is the receiving user.
This operation is called verification.
"""
def verify(a, c, secret_msg) do
{:ok, y} = hash_to_curve(secret_msg)
def verify?(a, c, secret_msg) do
y = hash_to_curve(secret_msg)

with {:ok, a_y} <- Secp256k1Utils.point_mul(y, a),
true <- Secp256k1Utils.point_equal?(c, a_y) do
true
else
_ -> false
case Secp256k1Utils.point_mul(y, a) do
{:ok, a_y} -> Secp256k1Utils.point_equal?(c, a_y)
{:err, _} -> false
end
end

Expand All @@ -145,35 +141,34 @@ defmodule Cashubrew.Nuts.Nut00.BDHKE do
"""
def step2_bob_dleq(b_prime, a, p \\ nil) do
p = p || :crypto.strong_rand_bytes(32)
{:ok, r1} = ExSecp256k1.create_public_key(p)
{:ok, r1_compressed} = ExSecp256k1.public_key_compress(r1)
{:ok, r2} = Secp256k1Utils.point_mul(b_prime, p)
{:ok, a_pub} = ExSecp256k1.create_public_key(a)
{:ok, a_pub_compressed} = ExSecp256k1.public_key_compress(a_pub)
{:ok, c_prime} = Secp256k1Utils.point_mul(b_prime, a)

e = hash_e(r1_compressed, r2, a_pub_compressed, c_prime)
e_scalar = :binary.decode_unsigned(e)
a_times_e = Secp256k1Utils.mod_mul(:binary.decode_unsigned(a), e_scalar, secp256k1_n())
s = Secp256k1Utils.mod_add(:binary.decode_unsigned(p), a_times_e, secp256k1_n())
s_bin = :binary.encode_unsigned(s) |> Secp256k1Utils.pad_left(32)
{:ok, {e, s_bin}}
with {:ok, r1} <- ExSecp256k1.create_public_key(p),
{:ok, r1_compressed} <- ExSecp256k1.public_key_compress(r1),
{:ok, r2} <- Secp256k1Utils.point_mul(b_prime, p),
{:ok, a_pub} <- ExSecp256k1.create_public_key(a),
{:ok, a_pub_compressed} <- ExSecp256k1.public_key_compress(a_pub),
{:ok, c_prime} <- Secp256k1Utils.point_mul(b_prime, a) do
e = hash_e(r1_compressed, r2, a_pub_compressed, c_prime)
e_scalar = :binary.decode_unsigned(e)
a_times_e = Secp256k1Utils.mod_mul(:binary.decode_unsigned(a), e_scalar, secp256k1_n())
s = Secp256k1Utils.mod_add(:binary.decode_unsigned(p), a_times_e, secp256k1_n())
s_bin = :binary.encode_unsigned(s) |> Secp256k1Utils.pad_left(32)
{:ok, {e, s_bin}}
end
end

@doc """
Alice verifies DLEQ proof
"""
def alice_verify_dleq(b_prime, c_prime, e, s, a_pub) do
def alice_verify_dleq?(b_prime, c_prime, e, s, a_pub) do
with {:ok, s_g} <- ExSecp256k1.create_public_key(s),
{:ok, s_g_compressed} <- ExSecp256k1.public_key_compress(s_g),
{:ok, e_a} <- Secp256k1Utils.point_mul(a_pub, e),
{:ok, r1} <- Secp256k1Utils.point_sub(s_g_compressed, e_a),
{:ok, s_b_prime} <- Secp256k1Utils.point_mul(b_prime, s),
{:ok, e_c_prime} <- Secp256k1Utils.point_mul(c_prime, e),
{:ok, r2} <- Secp256k1Utils.point_sub(s_b_prime, e_c_prime),
computed_e <- hash_e(r1, r2, a_pub, c_prime),
true <- computed_e == e do
true
{:ok, r2} <- Secp256k1Utils.point_sub(s_b_prime, e_c_prime) do
hash_e(r1, r2, a_pub, c_prime) == e
else
_ -> false
end
Expand All @@ -182,16 +177,15 @@ defmodule Cashubrew.Nuts.Nut00.BDHKE do
@doc """
Carol verifies DLEQ proof
"""
def carol_verify_dleq(secret_msg, r, c, e, s, a_pub) do
{:ok, y} = hash_to_curve(secret_msg)
def carol_verify_dleq?(secret_msg, r, c, e, s, a_pub) do
y = hash_to_curve(secret_msg)

with {:ok, r_pub} <- ExSecp256k1.create_public_key(r),
{:ok, r_pub_compressed} <- ExSecp256k1.public_key_compress(r_pub),
{:ok, r_a_pub} <- Secp256k1Utils.point_mul(a_pub, r),
{:ok, c_prime} <- Secp256k1Utils.point_add(c, r_a_pub),
{:ok, b_prime} <- Secp256k1Utils.point_add(y, r_pub_compressed),
true <- alice_verify_dleq(b_prime, c_prime, e, s, a_pub) do
true
{:ok, b_prime} <- Secp256k1Utils.point_add(y, r_pub_compressed) do
alice_verify_dleq?(b_prime, c_prime, e, s, a_pub)
else
_ -> false
end
Expand Down
2 changes: 1 addition & 1 deletion lib/cashubrew/core/mint_verification.ex
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ defmodule Cashubrew.Mint.Verification.Inputs do

key = Mint.get_key_for_amount(repo, head.id, head.amount)

if !BDHKE.verify(key, head."C", head.secret) do
if !BDHKE.verify?(key, head."C", head.secret) do
raise "InvalidProof"
end

Expand Down
2 changes: 1 addition & 1 deletion lib/cashubrew/mix/tasks/bench.ex
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ defmodule Mix.Tasks.Bench do
{:ok, c} = BDHKE.step3_alice(c_prime, r, a_pub)

# CAROL VERIFY: Carol verifies the unblinded signature
carol_verification = BDHKE.verify(a, c, secret_msg)
carol_verification = BDHKE.verify?(a, c, secret_msg)

if carol_verification do
:ok
Expand Down
4 changes: 2 additions & 2 deletions test/bdhke_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ defmodule BDHKETest do
# ALICE VERIFY: Alice verifies the DLEQ proof
Logger.info("\n***********************************************************")
Logger.info("ALICE VERIFY: Alice verifies the DLEQ proof")
alice_verification = BDHKE.alice_verify_dleq(b_prime, c_prime, e, s, a_pub)
alice_verification = BDHKE.alice_verify_dleq?(b_prime, c_prime, e, s, a_pub)
assert alice_verification, "Alice's DLEQ verification failed"
Logger.info("Alice successfully verified the DLEQ proof")
Logger.info("***********************************************************\n")
Expand All @@ -69,7 +69,7 @@ defmodule BDHKETest do
# CAROL VERIFY: Carol verifies the unblinded signature
Logger.info("\n***********************************************************")
Logger.info("CAROL VERIFY: Carol verifies the unblinded signature")
carol_verification = BDHKE.carol_verify_dleq(secret_msg, r, c, e, s, a_pub)
carol_verification = BDHKE.carol_verify_dleq?(secret_msg, r, c, e, s, a_pub)
assert carol_verification, "Carol's DLEQ verification failed"
Logger.info("Carol successfully verified the unblinded signature")
Logger.info("***********************************************************\n")
Expand Down
8 changes: 4 additions & 4 deletions test/crypto_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ defmodule CryptoTest do
"02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2"
|> Base.decode16(case: :lower)

assert BDHKE.alice_verify_dleq(b_, c_, e, s, a_public_key)
assert BDHKE.alice_verify_dleq?(b_, c_, e, s, a_public_key)
end

test "Alice direct verify DLEQ" do
Expand All @@ -178,7 +178,7 @@ defmodule CryptoTest do
{:ok, {b_, _}} = BDHKE.step1_alice(secret_msg, blinding_factor)

{:ok, {c_, e, s}} = BDHKE.step2_bob(b_, a)
assert BDHKE.alice_verify_dleq(b_, c_, e, s, a_public_key)
assert BDHKE.alice_verify_dleq?(b_, c_, e, s, a_public_key)
end

test "Carol verify from Bob" do
Expand All @@ -197,11 +197,11 @@ defmodule CryptoTest do

{:ok, {b_, _}} = BDHKE.step1_alice(secret_msg, r)
{:ok, {c_, e, s}} = BDHKE.step2_bob(b_, a)
assert BDHKE.alice_verify_dleq(b_, c_, e, s, a_public_key)
assert BDHKE.alice_verify_dleq?(b_, c_, e, s, a_public_key)

{:ok, c} = BDHKE.step3_alice(c_, r, a_public_key)

# carol does not know B_ and C_, but she receives C and r from Alice
assert BDHKE.carol_verify_dleq(secret_msg = secret_msg, r, c, e, s, a_public_key)
assert BDHKE.carol_verify_dleq?(secret_msg = secret_msg, r, c, e, s, a_public_key)
end
end

0 comments on commit 7c18f20

Please sign in to comment.