Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wallet: Use multiplicative blinding #628

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 5 additions & 26 deletions cashu/core/crypto/b_dhke.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def step1_alice(
) -> tuple[PublicKey, PrivateKey]:
Y: PublicKey = hash_to_curve(secret_msg.encode("utf-8"))
r = blinding_factor or PrivateKey()
B_: PublicKey = Y + r.pubkey # type: ignore
B_: PublicKey = Y.mult(r) # type: ignore
return B_, r


Expand All @@ -101,8 +101,8 @@ def step2_bob(B_: PublicKey, a: PrivateKey) -> Tuple[PublicKey, PrivateKey, Priv
return C_, e, s


def step3_alice(C_: PublicKey, r: PrivateKey, A: PublicKey) -> PublicKey:
C: PublicKey = C_ - A.mult(r) # type: ignore
def step3_alice(C_: PublicKey, r: PrivateKey) -> PublicKey:
C: PublicKey = C_.mult_inverse(r) # type: ignore
return C


Expand Down Expand Up @@ -166,8 +166,8 @@ def carol_verify_dleq(
A: PublicKey,
) -> bool:
Y: PublicKey = hash_to_curve(secret_msg.encode("utf-8"))
C_: PublicKey = C + A.mult(r) # type: ignore
B_: PublicKey = Y + r.pubkey # type: ignore
C_: PublicKey = C.mult(r) # type: ignore
B_: PublicKey = Y.mult(r) # type: ignore
valid = alice_verify_dleq(B_, C_, e, s, A)
# BEGIN: BACKWARDS COMPATIBILITY < 0.15.1
if not valid:
Expand Down Expand Up @@ -222,24 +222,3 @@ def carol_verify_dleq_deprecated(
B_: PublicKey = Y + r.pubkey # type: ignore
valid = alice_verify_dleq(B_, C_, e, s, A)
return valid


# Below is a test of a simple positive and negative case

# # Alice's keys
# a = PrivateKey()
# A = a.pubkey
# secret_msg = "test"
# B_, r = step1_alice(secret_msg)
# C_ = step2_bob(B_, a)
# C = step3_alice(C_, r, A)
# print("C:{}, secret_msg:{}".format(C, secret_msg))
# assert verify(a, C, secret_msg)
# assert verify(a, C + C, secret_msg) == False # adding C twice shouldn't pass
# assert verify(a, A, secret_msg) == False # A shouldn't pass

# # Test operations
# b = PrivateKey()
# B = b.pubkey
# assert -A -A + A == -A # neg
# assert B.mult(a) == A.mult(b) # a*B = A*b
17 changes: 17 additions & 0 deletions cashu/core/crypto/secp.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from ecdsa.curves import SECP256k1
from secp256k1 import PrivateKey, PublicKey


Expand Down Expand Up @@ -31,6 +32,12 @@ def mult(self, privkey):
else:
raise TypeError("Can't multiply with non privatekey")

def mult_inverse(self, privkey):
privkey_int = pk_to_int(privkey)
privkey_inv_int = pow(privkey_int, -1, SECP256k1.order)
privkey_inv = int_to_pk(privkey_inv_int)
return self.mult(privkey_inv)

def __eq__(self, pubkey2):
if isinstance(pubkey2, PublicKey):
seq1 = self.to_data()
Expand All @@ -44,10 +51,20 @@ def to_data(self):
return [self.public_key.data[i] for i in range(64)]


def pk_to_int(priv: PrivateKey) -> int:
assert priv.private_key
return int.from_bytes(priv.private_key, byteorder="big")


def int_to_pk(k: int) -> PrivateKey:
return PrivateKey(k.to_bytes(32, byteorder="big"), raw=True)


# Horrible monkeypatching
PublicKey.__add__ = PublicKeyExt.__add__ # type: ignore
PublicKey.__neg__ = PublicKeyExt.__neg__ # type: ignore
PublicKey.__sub__ = PublicKeyExt.__sub__ # type: ignore
PublicKey.mult = PublicKeyExt.mult # type: ignore
PublicKey.mult_inverse = PublicKeyExt.mult_inverse # type: ignore
PublicKey.__eq__ = PublicKeyExt.__eq__ # type: ignore
PublicKey.to_data = PublicKeyExt.to_data # type: ignore
4 changes: 1 addition & 3 deletions cashu/wallet/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -866,9 +866,7 @@ async def _construct_proofs(
await self.load_mint_keysets()
assert promise.id in self.keysets, "Could not load keyset."
C_ = PublicKey(bytes.fromhex(promise.C_), raw=True)
C = b_dhke.step3_alice(
C_, r, self.keysets[promise.id].public_keys[promise.amount]
)
C = b_dhke.step3_alice(C_, r)

if not settings.wallet_use_deprecated_h2c:
B_, r = b_dhke.step1_alice(secret, r) # recompute B_ for dleq proofs
Expand Down
69 changes: 16 additions & 53 deletions tests/test_crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def test_step1():

assert (
B_.serialize().hex()
== "025cc16fe33b953e2ace39653efb3e7a7049711ae1d8a2f7a9108753f1cdea742b"
== "0215fdc277c704590f3c3bcc08cf9a8f748f46619b96268cece86442b6c3ac461b"
)
assert blinding_factor.private_key == bytes.fromhex(
"0000000000000000000000000000000000000000000000000000000000000001"
Expand All @@ -83,7 +83,7 @@ def test_step2():
C_, e, s = step2_bob(B_, a)
assert (
C_.serialize().hex()
== "025cc16fe33b953e2ace39653efb3e7a7049711ae1d8a2f7a9108753f1cdea742b"
== "0215fdc277c704590f3c3bcc08cf9a8f748f46619b96268cece86442b6c3ac461b"
)


Expand All @@ -102,18 +102,11 @@ def test_step3():
)
)

A = PublicKey(
pubkey=b"\x02"
+ bytes.fromhex(
"0000000000000000000000000000000000000000000000000000000000000001",
),
raw=True,
)
C = step3_alice(C_, r, A)
C = step3_alice(C_, r)

assert (
C.serialize().hex()
== "0271bf0d702dbad86cbe0af3ab2bfba70a0338f22728e412d88a830ed0580b9de4"
== "025cc16fe33b953e2ace39653efb3e7a7049711ae1d8a2f7a9108753f1cdea742b"
)


Expand Down Expand Up @@ -171,11 +164,11 @@ def test_dleq_step2_bob_dleq():
e, s = step2_bob_dleq(B_, a, p_bytes)
assert (
e.serialize()
== "a608ae30a54c6d878c706240ee35d4289b68cfe99454bbfa6578b503bce2dbe1"
== "600332a05c0722af1feb9ee95b2917eeafaa14cf17852f116f35e059b9c1ea0a"
)
assert (
s.serialize()
== "a608ae30a54c6d878c706240ee35d4289b68cfe99454bbfa6578b503bce2dbe2"
== "600332a05c0722af1feb9ee95b2917eeafaa14cf17852f116f35e059b9c1ea0b"
) # differs from e only in least significant byte because `a = 0x1`

# change `a`
Expand All @@ -188,11 +181,11 @@ def test_dleq_step2_bob_dleq():
e, s = step2_bob_dleq(B_, a, p_bytes)
assert (
e.serialize()
== "076cbdda4f368053c33056c438df014d1875eb3c8b28120bece74b6d0e6381bb"
== "345b33beec35d507701f513cf4cf60aea6a8c9f9c3b576a4fbb0f01f04888d90"
)
assert (
s.serialize()
== "b6d41ac1e12415862bf8cace95e5355e9262eab8a11d201dadd3b6e41584ea6e"
== "887e1d5d42b8a3f08679714e0731091912a66ee39b96e53f55de302a113656d4"
)


Expand Down Expand Up @@ -299,28 +292,28 @@ def test_dleq_carol_verify_from_bob():
B_, _ = step1_alice(secret_msg, r)
C_, e, s = step2_bob(B_, a)
assert alice_verify_dleq(B_, C_, e, s, A)
C = step3_alice(C_, r, A)
C = step3_alice(C_, r)
# carol does not know B_ and C_, but she receives C and r from Alice
assert carol_verify_dleq(secret_msg=secret_msg, C=C, r=r, e=e, s=s, A=A)


def test_dleq_carol_on_proof():
A = PublicKey(
bytes.fromhex(
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
"0381d73b92c7ed013476c3e4e64b4964e9c742c1ecc3375bb55c0520acc56e1233"
),
raw=True,
)
proof = Proof.parse_obj(
{
"id": "00ad268c4d1f5826",
"amount": 1,
"id": "00882760bfa2eb41",
"secret": "daf4dd00a2b68a0858a80450f52c8a7d2ccf87d375e43e216e0c571f089f63e9",
"C": "024369d2d22a80ecf78f3937da9d5f30c1b9f74f0c32684d583cca0fa6a61cdcfc",
"secret": "202caa260a09bdfb97de9d4c2f43fe4858a2a35a3e84b03471f28f92e5fadf97",
"C": "039eafe0acd4a39935bd878aed21259fa1019fb62f54bd5f160f33cb44898d82f1",
"dleq": {
"e": "b31e58ac6527f34975ffab13e70a48b6d2b0d35abc4b03f0151f09ee1a9763d4",
"s": "8fbae004c59e754d71df67e392b6ae4e29293113ddc2ec86592a0431d16306d8",
"r": "a6d13fcd7a18442e6076f5e1e7c887ad5de40a019824bdfa9fe740d302e8d861",
"e": "d9d1ee82155a5630c240607f3ab1d44fbfa5e605b44f0fc9faccfa636215d6bb",
"s": "46035f798873d26fdaa85b571147253706c176283495352fe879f5749e766bd5",
"r": "dda903ccb8fac8d031e7057b4270c802f1f6b181672bb3439a7bf2d7c3d516cd",
},
}
)
Expand Down Expand Up @@ -417,36 +410,6 @@ def test_step2_deprecated():
)


def test_step3_deprecated():
# C = C_ - A.mult(r)
# C_ from test_step2_deprecated
C_ = PublicKey(
bytes.fromhex(
"02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2"
),
raw=True,
)
r = PrivateKey(
privkey=bytes.fromhex(
"0000000000000000000000000000000000000000000000000000000000000001"
)
)

A = PublicKey(
pubkey=b"\x02"
+ bytes.fromhex(
"0000000000000000000000000000000000000000000000000000000000000001",
),
raw=True,
)
C = step3_alice(C_, r, A)

assert (
C.serialize().hex()
== "03c724d7e6a5443b39ac8acf11f40420adc4f99a02e7cc1b57703d9391f6d129cd"
)


def test_dleq_step2_bob_dleq_deprecated():
B_, _ = step1_alice_deprecated(
"test_message",
Expand Down
Loading