From b5c2ae49ddac6855b7f98de0c776d6b62e6a5f0c Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Tue, 26 Jun 2018 14:45:43 +0200 Subject: [PATCH] ripple: get address --- setup.cfg | 2 +- src/apps/ripple/__init__.py | 13 ++++++++ src/apps/ripple/base58_ripple.py | 33 +++++++++++++++++++++ src/apps/ripple/get_address.py | 20 +++++++++++++ src/apps/ripple/helpers.py | 25 ++++++++++++++++ src/main.py | 3 +- src/trezor/crypto/base58.py | 18 ++++++----- tests/test_apps.ripple.pubkey_to_address.py | 19 ++++++++++++ 8 files changed, 124 insertions(+), 9 deletions(-) create mode 100644 src/apps/ripple/__init__.py create mode 100644 src/apps/ripple/base58_ripple.py create mode 100644 src/apps/ripple/get_address.py create mode 100644 src/apps/ripple/helpers.py create mode 100644 tests/test_apps.ripple.pubkey_to_address.py diff --git a/setup.cfg b/setup.cfg index 60d212f45..fcf985ddd 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,4 +32,4 @@ known_standard_library = micropython,ubinascii,ustruct,uctypes,utime,utimeq,trez [tool:pytest] addopts = --pyargs trezorlib.tests.device_tests xfail_strict = true -run_xfail = +run_xfail = ripple diff --git a/src/apps/ripple/__init__.py b/src/apps/ripple/__init__.py new file mode 100644 index 000000000..c9ef9c2a2 --- /dev/null +++ b/src/apps/ripple/__init__.py @@ -0,0 +1,13 @@ +from trezor.wire import register, protobuf_workflow +from trezor.messages.wire_types import RippleGetAddress +from .get_address import get_address +from .serializer import * + + +def dispatch_RippleGetAddress(*args, **kwargs): + from .get_address import get_address + return get_address(*args, **kwargs) + + +def boot(): + register(RippleGetAddress, protobuf_workflow, dispatch_RippleGetAddress) diff --git a/src/apps/ripple/base58_ripple.py b/src/apps/ripple/base58_ripple.py new file mode 100644 index 000000000..2ead40890 --- /dev/null +++ b/src/apps/ripple/base58_ripple.py @@ -0,0 +1,33 @@ +from trezor.crypto import base58 + +# Ripple uses different 58 character alphabet than traditional base58 +_ripple_alphabet = "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz" + + +def encode(data: bytes) -> str: + """ + Convert bytes to base58 encoded string. + """ + return base58.encode(data, alphabet=_ripple_alphabet) + + +def decode(string: str) -> bytes: + """ + Convert base58 encoded string to bytes. + """ + return base58.decode(string, alphabet=_ripple_alphabet) + + +def encode_check(data: bytes, digestfunc=base58.sha256d_32) -> str: + """ + Convert bytes to base58 encoded string, append checksum. + """ + return encode(data + digestfunc(data)) + + +def decode_check(string: str, digestfunc=base58.sha256d_32) -> bytes: + """ + Convert base58 encoded string to bytes and verify checksum. + """ + data = decode(string) + return base58.verify_checksum(data, digestfunc) diff --git a/src/apps/ripple/get_address.py b/src/apps/ripple/get_address.py new file mode 100644 index 000000000..8f66f8c9d --- /dev/null +++ b/src/apps/ripple/get_address.py @@ -0,0 +1,20 @@ +from apps.common import seed +from apps.common.display_address import show_address, show_qr +from trezor.messages.RippleAddress import RippleAddress +from trezor.messages.RippleGetAddress import RippleGetAddress +from . import helpers + + +async def get_address(ctx, msg: RippleGetAddress): + node = await seed.derive_node(ctx, msg.address_n) + pubkey = node.public_key() + address = helpers.address_from_public_key(pubkey) + + if msg.show_display: + while True: + if await show_address(ctx, address): + break + if await show_qr(ctx, address.upper()): + break + + return RippleAddress(address=address) diff --git a/src/apps/ripple/helpers.py b/src/apps/ripple/helpers.py new file mode 100644 index 000000000..a7249acd0 --- /dev/null +++ b/src/apps/ripple/helpers.py @@ -0,0 +1,25 @@ +from trezor.crypto.hashlib import ripemd160, sha256 + +from . import base58_ripple + + +def address_from_public_key(pubkey: bytes): + """Extracts public key from an address + + Ripple address is in format: + <1-byte ripple flag> <20-bytes account id> <4-bytes dSHA-256 checksum> + + - 1-byte flag is 0x00 which is 'r' (Ripple uses its own base58 alphabet) + - 20-bytes account id is a ripemd160(sha256(pubkey)) + - checksum is first 4 bytes of double sha256(data) + + see https://developers.ripple.com/accounts.html#address-encoding + """ + """Returns the Ripple address created using base58""" + h = sha256(pubkey).digest() + h = ripemd160(h).digest() + + address = bytearray() + address.append(0x00) # 'r' + address.extend(h) + return base58_ripple.encode_check(bytes(address)) diff --git a/src/main.py b/src/main.py index 76e6aab41..24af03958 100644 --- a/src/main.py +++ b/src/main.py @@ -16,9 +16,9 @@ import apps.ethereum import apps.lisk import apps.nem import apps.stellar +import apps.ripple import apps.cardano - if __debug__: import apps.debug else: @@ -32,6 +32,7 @@ apps.ethereum.boot() apps.lisk.boot() apps.nem.boot() apps.stellar.boot() +apps.ripple.boot() apps.cardano.boot() if __debug__: apps.debug.boot() diff --git a/src/trezor/crypto/base58.py b/src/trezor/crypto/base58.py index 9edadd8d3..10bc0147f 100644 --- a/src/trezor/crypto/base58.py +++ b/src/trezor/crypto/base58.py @@ -17,7 +17,7 @@ _alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" -def encode(data: bytes) -> str: +def encode(data: bytes, alphabet=_alphabet) -> str: """ Convert bytes to base58 encoded string. """ @@ -33,22 +33,22 @@ def encode(data: bytes) -> str: result = "" while acc > 0: acc, mod = divmod(acc, 58) - result += _alphabet[mod] + result += alphabet[mod] - return "".join((c for c in reversed(result + _alphabet[0] * (origlen - newlen)))) + return "".join((c for c in reversed(result + alphabet[0] * (origlen - newlen)))) -def decode(string: str) -> bytes: +def decode(string: str, alphabet=_alphabet) -> bytes: """ Convert base58 encoded string to bytes. """ origlen = len(string) - string = string.lstrip(_alphabet[0]) + string = string.lstrip(alphabet[0]) newlen = len(string) p, acc = 1, 0 for c in reversed(string): - acc += p * _alphabet.index(c) + acc += p * alphabet.index(c) p *= 58 result = [] @@ -83,8 +83,12 @@ def decode_check(string: str, digestfunc=sha256d_32) -> bytes: Convert base58 encoded string to bytes and verify checksum. """ result = decode(string) + return verify_checksum(result, digestfunc) + + +def verify_checksum(data: bytes, digestfunc) -> bytes: digestlen = len(digestfunc(b"")) - result, check = result[:-digestlen], result[-digestlen:] + result, check = data[:-digestlen], data[-digestlen:] if check != digestfunc(result): raise ValueError("Invalid checksum") diff --git a/tests/test_apps.ripple.pubkey_to_address.py b/tests/test_apps.ripple.pubkey_to_address.py new file mode 100644 index 000000000..7ad5a5101 --- /dev/null +++ b/tests/test_apps.ripple.pubkey_to_address.py @@ -0,0 +1,19 @@ +from common import * +from apps.ripple.helpers import address_from_public_key + + +class TestStellarPubkeyToAddress(unittest.TestCase): + + def test_pubkey_to_address(self): + addr = address_from_public_key(unhexlify('ed9434799226374926eda3b54b1b461b4abf7237962eae18528fea67595397fa32')) + self.assertEqual(addr, 'rDTXLQ7ZKZVKz33zJbHjgVShjsBnqMBhmN') + + addr = address_from_public_key(unhexlify('03e2b079e9b09ae8916da8f5ee40cbda9578dbe7c820553fe4d5f872eec7b1fdd4')) + self.assertEqual(addr, 'rhq549rEtUrJowuxQC2WsHNGLjAjBQdAe8') + + addr = address_from_public_key(unhexlify('0282ee731039929e97db6aec242002e9aa62cd62b989136df231f4bb9b8b7c7eb2')) + self.assertEqual(addr, 'rKzE5DTyF9G6z7k7j27T2xEas2eMo85kmw') + + +if __name__ == '__main__': + unittest.main()