diff --git a/monero/address.py b/monero/address.py index 9528954..fc35c4b 100644 --- a/monero/address.py +++ b/monero/address.py @@ -95,14 +95,20 @@ class Address(BaseAddress): :rtype: bool """ - return ed25519.public_from_secret_hex(key) == self.view_key() + try: + return ed25519.public_from_secret_hex(key) == self.view_key() + except ValueError: + return False def check_private_spend_key(self, key): """Checks if private spend key matches this address. :rtype: bool """ - return ed25519.public_from_secret_hex(key) == self.spend_key() + try: + return ed25519.public_from_secret_hex(key) == self.spend_key() + except ValueError: + return False def with_payment_id(self, payment_id=0): """Integrates payment id into the address. diff --git a/monero/ed25519.py b/monero/ed25519.py index ebf1a8a..6c9bbe8 100644 --- a/monero/ed25519.py +++ b/monero/ed25519.py @@ -1,127 +1,36 @@ -# ed25519.py - Optimized version of the reference implementation of Ed25519 -# -# Written in 2011? by Daniel J. Bernstein -# 2013 by Donald Stufft -# 2013 by Alex Gaynor -# 2013 by Greg Price -# 2019 by Michal Salaban -# -# To the extent possible under law, the author(s) have dedicated all copyright -# and related and neighboring rights to this software to the public domain -# worldwide. This software is distributed without any warranty. -# -# You should have received a copy of the CC0 Public Domain Dedication along -# with this software. If not, see -# . - -""" -NB: This code is not safe for use with secret keys or secret data. -The only safe use of this code is for verifying signatures on public messages. - -Functions for computing the public key of a secret key and for signing -a message are included, namely publickey_unsafe and signature_unsafe, -for testing purposes only. - -The root of the problem is that Python's long-integer arithmetic is -not designed for use in cryptography. Specifically, it may take more -or less time to execute an operation depending on the values of the -inputs, and its memory access patterns may also depend on the inputs. -This opens it to timing and cache side-channel attacks which can -disclose data to an attacker. We rely on Python's long-integer -arithmetic, so we cannot handle secrets without risking their disclosure. -""" - import binascii import six import nacl.bindings -b = 256 -q = 2 ** 255 - 19 -l = 2 ** 252 + 27742317777372353535851937790883648493 - - def bit(h, i): return (six.indexbytes(h, i // 8) >> (i % 8)) & 1 def encodeint(y): - bits = [(y >> i) & 1 for i in range(b)] + bits = [(y >> i) & 1 for i in range(256)] return b"".join( - [ - six.int2byte(sum([bits[i * 8 + j] << j for j in range(8)])) - for i in range(b // 8) - ] + [six.int2byte(sum([bits[i * 8 + j] << j for j in range(8)])) for i in range(32)] ) def decodeint(s): - return sum(2 ** i * bit(s, i) for i in range(0, b)) + return sum(2 ** i * bit(s, i) for i in range(0, 256)) edwards_add = nacl.bindings.crypto_core_ed25519_add inv = nacl.bindings.crypto_core_ed25519_scalar_invert -public_from_secret = nacl.bindings.crypto_sign_ed25519_sk_to_pk -scalar_reduce = nacl.bindings.crypto_core_ed25519_scalar_reduce scalarmult_B = nacl.bindings.crypto_scalarmult_ed25519_base_noclamp +scalarmult = nacl.bindings.crypto_scalarmult_ed25519_noclamp -def scalarmult(P, e): - return nacl.bindings.crypto_scalarmult_ed25519_noclamp(e, P) - - -d = -121665 * decodeint(inv(encodeint(121666))) % q -I = pow(2, (q - 1) // 4, q) - - -def xrecover(y): - xx = (y * y - 1) * decodeint(inv(encodeint(d * y * y + 1))) - x = pow(xx, (q + 3) // 8, q) - - if (x * x - xx) % q != 0: - x = (x * I) % q - - if x % 2 != 0: - x = q - x - - return x - - -By = 4 * decodeint(inv(encodeint(5))) -Bx = xrecover(By) -B = (Bx % q, By % q, 1, (Bx * By) % q) -ident = (0, 1, 1, 0) - - -def encodepoint(P): - (x, y, z, t) = P - zi = inv(z) - x = (x * zi) % q - y = (y * zi) % q - bits = [(y >> i) & 1 for i in range(b - 1)] + [x & 1] - return b"".join( - [ - six.int2byte(sum([bits[i * 8 + j] << j for j in range(8)])) - for i in range(b // 8) - ] - ) - - -def decodepoint(s): - y = sum(2 ** i * bit(s, i) for i in range(0, b - 1)) - x = xrecover(y) - if x & 1 != bit(s, b - 1): - x = q - x - P = (x, y, 1, (x * y) % q) - return P - - -def pad_to_64B(v): - return nacl.bindings.utils.sodium_pad(v, 64) +def scalar_reduce(v): + return nacl.bindings.crypto_core_ed25519_scalar_reduce(v + 32 * b"\0") def public_from_secret_hex(hk): - return binascii.hexlify( - public_from_secret(pad_to_64B(binascii.unhexlify(hk))) - ).decode() + try: + return binascii.hexlify(scalarmult_B(binascii.unhexlify(hk))).decode() + except nacl.exceptions.RuntimeError: + raise ValueError("Invalid secret key") diff --git a/monero/seed.py b/monero/seed.py index e5080b8..ee0e360 100644 --- a/monero/seed.py +++ b/monero/seed.py @@ -117,9 +117,6 @@ class Seed(object): return True raise ValueError("Invalid checksum") - def _sc_reduce(self, input): - return hexlify(ed25519.scalar_reduce(ed25519.pad_to_64B(input))).decode() - def hex_seed(self): return self.hex @@ -128,7 +125,7 @@ class Seed(object): def secret_spend_key(self): a = self._hex_seed_keccak() if self.is_mymonero() else unhexlify(self.hex) - return self._sc_reduce(a) + return hexlify(ed25519.scalar_reduce(a)).decode() def secret_view_key(self): b = ( @@ -136,7 +133,7 @@ class Seed(object): if self.is_mymonero() else unhexlify(self.secret_spend_key()) ) - return self._sc_reduce(keccak_256(b).digest()) + return hexlify(ed25519.scalar_reduce(keccak_256(b).digest())).decode() def public_spend_key(self): if self._ed_pub_spend_key: diff --git a/monero/transaction/__init__.py b/monero/transaction/__init__.py index 474a44d..3671b2c 100644 --- a/monero/transaction/__init__.py +++ b/monero/transaction/__init__.py @@ -145,18 +145,13 @@ class Transaction(object): hsdata = b"".join( [ ed25519.scalarmult( - tx_key, ed25519.encodeint(ed25519.decodeint(svk) * 8) + ed25519.encodeint(ed25519.decodeint(svk) * 8), tx_key ), varint.encode(idx), ] ) Hs_ur = keccak_256(hsdata).digest() - - # sc_reduce32: - Hsint_ur = ed25519.decodeint(Hs_ur) - Hsint = Hsint_ur % ed25519.l - Hs = ed25519.encodeint(Hsint) - + Hs = ed25519.scalar_reduce(Hs_ur) k = ed25519.edwards_add( ed25519.scalarmult_B(Hs), psk, diff --git a/monero/wallet.py b/monero/wallet.py index bda66b3..a996165 100644 --- a/monero/wallet.py +++ b/monero/wallet.py @@ -231,9 +231,11 @@ class Wallet(object): ) m = keccak_256(hsdata).digest() # D = master_psk + m * B - D = ed25519.edwards_add(master_psk, ed25519.scalarmult_B(m)) + D = ed25519.edwards_add( + master_psk, ed25519.scalarmult_B(ed25519.scalar_reduce(m)) + ) # C = master_svk * D - C = ed25519.scalarmult(D, master_svk) + C = ed25519.scalarmult(master_svk, D) netbyte = bytearray( [const.SUBADDR_NETBYTES[const.NETS.index(master_address.net)]] ) diff --git a/tests/data/test_ed25519/scalar_reduce.json b/tests/data/test_ed25519/scalar_reduce.json deleted file mode 100644 index 2288411..0000000 --- a/tests/data/test_ed25519/scalar_reduce.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "2ec46011b23b0c00468946f1d9a64995bf0a89f9ee0bbf4f64058a3acd81a70e": { - "integer": 6628339198216413857236352180108225378148743212953972540346314253902012269614, - "modulo": 6628339198216413857236352180108225378148743212953972540346314253902012269614, - "result": "2ec46011b23b0c00468946f1d9a64995bf0a89f9ee0bbf4f64058a3acd81a70e" - }, - "25e0cf207358c6a1347dcba42119c0b695bb7823a0cc6ad856de6d372288d17f": { - "integer": 57813942366592237174432678471109775286345684008086513999063590953232999899173, - "modulo": 7154903325266401676620372529808815600345869492427160757049934385234820142250, - "result": "aa141796baa24539583306300b44a72495bb7823a0cc6ad856de6d372288d10f" - }, - "600d3c5022e1844dd2df02f178a074fc2e566793e99d9e1465926adcbfa9b508": { - "integer": 3939473675085984986992368553178981174068994160171572258582957763871603690848, - "modulo": 3939473675085984986992368553178981174068994160171572258582957763871603690848, - "result": "600d3c5022e1844dd2df02f178a074fc2e566793e99d9e1465926adcbfa9b508" - }, - "9531701ea6eafeac65a21e62e26e105cbb12b96d682bfc320b4f8ce935e2d323": { - "integer": 16205315679229594349692122491359510638187130104178740652140889924125210980757, - "modulo": 1731304524565069921745749365273522156472897385418925440136988047554302478779, - "result": "bb8984647124dafcb8682f1c257b5232bb12b96d682bfc320b4f8ce935e2d303" - }, - "a8e8a30d3638cc4d09d1fa9f4de12ac0096c69a77896774793627c0cc6a28703": { - "integer": 1596586321787417762488266696268089527089621022149808638093194755286593956008, - "modulo": 1596586321787417762488266696268089527089621022149808638093194755286593956008, - "result": "a8e8a30d3638cc4d09d1fa9f4de12ac0096c69a77896774793627c0cc6a28703" - }, - "9410478406b82c923cec509bf8e8bf857b8de3b45236c9842cb876212427f1d3": { - "integer": 95864091335870547385163600694014568338142560187395956431101203204900223651988, - "modulo": 1783018830551138603512175374455643207000047515457157553075841007189318389131, - "result": "8b4dcbcbafaf3d195af5bd54aa386d767a8de3b45236c9842cb876212427f103" - }, - "597703dd73d0da6b3996b83c3e1e2f602be4f0de453e15846171aa9076901603": { - "integer": 1396806229157654325406612422616318744215090798882912232993621199577392838489, - "modulo": 1396806229157654325406612422616318744215090798882912232993621199577392838489, - "result": "597703dd73d0da6b3996b83c3e1e2f602be4f0de453e15846171aa9076901603" - }, - "84dc0b095dfde492abee8087efbd2269b714fac032f1c3dff659ca26ab430f69": { - "integer": 47519818838455871225365520876286444378141740832067494560806350640166365551748, - "modulo": 4097785374462297941526401498028478932999042675788048924794645010453640045814, - "result": "f6e448dbbeaa7682a541b3b5b7e2e8ebb614fac032f1c3dff659ca26ab430f09" - }, - "193152abe15c5e0a0ff56e3020229398769cd7c6ca5a4e30e439d6702c4f320a": { - "integer": 4612017275153737097907034112534798197384252782681849631504289240246595498265, - "modulo": 4612017275153737097907034112534798197384252782681849631504289240246595498265, - "result": "193152abe15c5e0a0ff56e3020229398769cd7c6ca5a4e30e439d6702c4f320a" - }, - "cdb967c501195827d78a791e1173d4b8826a5ae73b0885984898c84b6c9dd80c": { - "integer": 5810479642822955659735279987239253355362863496018367030298598645652997519821, - "modulo": 5810479642822955659735279987239253355362863496018367030298598645652997519821, - "result": "cdb967c501195827d78a791e1173d4b8826a5ae73b0885984898c84b6c9dd80c" - }, - "29c8d9e91c1cb59e059bddd901e011db85f8d4f00f967226ffb5e185bd10e70d": { - "integer": 6288324241017125450135585489376912591668237464955021703284104294893526566953, - "modulo": 6288324241017125450135585489376912591668237464955021703284104294893526566953, - "result": "29c8d9e91c1cb59e059bddd901e011db85f8d4f00f967226ffb5e185bd10e70d" - }, - "f9c935bd64b45a34d537814006aa569341030c34f2d1efecc4eb26ea9f838b22": { - "integer": 15625137033897289677611928611975874263404959545762802384938837865992272857593, - "modulo": 1151125879232765249665555485889885781690726827002987172934935989421364355615, - "result": "1f224a0330ee358428fe91fa48b6986941030c34f2d1efecc4eb26ea9f838b02" - }, - "a047598095d2ada065af73758f7082900b9b0d721b5f99a541a78bd461ffc607": { - "integer": 3517788241725899011075135813684493241587423920731267486065832167600161376160, - "modulo": 3517788241725899011075135813684493241587423920731267486065832167600161376160, - "result": "a047598095d2ada065af73758f7082900b9b0d721b5f99a541a78bd461ffc607" - }, - "080c6135edf93233176d41c8535caef0f13d596dc5093b5a5afa4279339dbc00": { - "integer": 333252210082402815912805449335296231262512726833157454332209494259012799496, - "modulo": 333252210082402815912805449335296231262512726833157454332209494259012799496, - "result": "080c6135edf93233176d41c8535caef0f13d596dc5093b5a5afa4279339dbc00" - }, - "482700617ba810f94035d7f4d7ccc1a29878e165b4867872b705204c85406906": { - "integer": 2899841338757337401439250404000262340768069237371615667279145359232485828424, - "modulo": 2899841338757337401439250404000262340768069237371615667279145359232485828424, - "result": "482700617ba810f94035d7f4d7ccc1a29878e165b4867872b705204c85406906" - }, - "12b0ee7f6adbd85a7ee6c298527fd805cd25de7842aa0722abeb12697f1719d3": { - "integer": 95482344402833444566475004546782631202940389090036531688630083907873449881618, - "modulo": 1401271897514035784823579227223706071797876418097732810604721710162544618761, - "result": "09ed72c713d3e9e19bef2f5204cf85f6cb25de7842aa0722abeb12697f171903" - }, - "0e15b4e0f31c7023f59c07e50bb24676798a2d28b6072091e054238d84591483": { - "integer": 59288937934704856670126753414446243614245232688387735062206342841157186491662, - "modulo": 1392893316046758958341260910102289687388301813348474214190735334873552483750, - "result": "a67505f92004dd6242b64acd16e34ecf788a2d28b6072091e054238d84591403" - }, - "6661ba3dc3d75bf15a00890fa99454e38d0d0ed36e38b3c827c4c00370af1aff": { - "integer": 115386925237937118400643168199482218242653593459875731046215557413505242653030, - "modulo": 6831841577953185191045369753837304629796848069177116956186293339223428888195, - "result": "83f652cb370948c8cbcf06839df043aa8c0d0ed36e38b3c827c4c00370af1a0f" - }, - "60916cfcb10fa0b2b0648e36ecd7037f5c1972d36b2e6d56c2f4feca613a4200": { - "integer": 117014844056279701262384447223800575458979231802741572058244012970802450784, - "modulo": 117014844056279701262384447223800575458979231802741572058244012970802450784, - "result": "60916cfcb10fa0b2b0648e36ecd7037f5c1972d36b2e6d56c2f4feca613a4200" - }, - "2d05fa6dad90f748d7fb364e610b1703041fb501f1f80e0cb3b4b4638b5f7176": { - "integer": 53573229274981178047805367505496460267398966421504169209158271959949458933037, - "modulo": 2914190233655342549993061564195500581399151905844815967144615391951279176114, - "result": "b23941e3f4da76e0fab171d94a36fe70031fb501f1f80e0cb3b4b4638b5f7106" - }, - "6dc31f6ebcf834ab375a69006cb19c66fcccfa0732dfb3ea1b0662b455226b0d": { - "integer": 6069356637481544937951349412679035329690355688191943063509197966002018567021, - "modulo": 6069356637481544937951349412679035329690355688191943063509197966002018567021, - "result": "6dc31f6ebcf834ab375a69006cb19c66fcccfa0732dfb3ea1b0662b455226b0d" - }, - "8382125d125754da8ddeb8f89cbd5d9591fe31216a9cf6fb2d6b3c78698ce8ba": { - "integer": 84541067443461413931254155152843369596662288304236779390986978391229457400451, - "modulo": 4934006092806529577549102959370432947234008351057795724965518070089460639572, - "result": "5467825ef0148a11582115f80b01c9af90fe31216a9cf6fb2d6b3c78698ce80a" - }, - "5973d91299466a9a51ddfcd20d1710c776aa1399279b292b264ab6b7ab608105": { - "integer": 2490154711420054482973427955977087630958526490925203328128860118211022648153, - "modulo": 2490154711420054482973427955977087630958526490925203328128860118211022648153, - "result": "5973d91299466a9a51ddfcd20d1710c776aa1399279b292b264ab6b7ab608105" - }, - "3922528967d829c5334978295f4aaa4a24154334a3b43af1da05244ec4cf252d": { - "integer": 20420885481516297805400244677782227685119906340516184899355036869276044763705, - "modulo": 5946874326851773377453871551696239203405673621756369687351134992705136261727, - "result": "5f7a66cf32120515870f89e3a156ec2024154334a3b43af1da05244ec4cf250d" - } -} diff --git a/tests/test_ed25519.py b/tests/test_ed25519.py deleted file mode 100644 index 9f968f5..0000000 --- a/tests/test_ed25519.py +++ /dev/null @@ -1,31 +0,0 @@ -from binascii import hexlify, unhexlify -import json -import os -import unittest - -from monero import ed25519 - - -class TestEd25519(unittest.TestCase): - base_path = os.path.join(os.path.dirname(__file__), "data", "test_ed25519") - - def modulo_python(self, v): - return v % ed25519.l - - def test_scalar_reduce(self): - with open(os.path.join(self.base_path, "scalar_reduce.json"), "rb") as jsonfile: - suite = json.load(jsonfile) - for input_, data in suite.items(): - input_bin = ed25519.pad_to_64B(unhexlify(input_)) - integer = ed25519.decodeint(input_bin) - print(integer) - print(ed25519.l) - self.assertEqual(data["integer"], integer) - modulo_bin = ed25519.scalar_reduce(input_bin) - print(hexlify(modulo_bin).decode()) - print(hexlify(ed25519.encodeint(self.modulo_python(integer))).decode()) - print(ed25519.decodeint(modulo_bin)) - print(data["modulo"]) - self.assertEqual(data["modulo"], ed25519.decodeint(modulo_bin)) - modulo_hex = hexlify(modulo_bin).decode() - self.assertEqual(data["result"], modulo_hex)