diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0a9d63c85067adae72bb8ab27ffe0e9de3a3abc1..2ecd92896c33c3d8497b4a8a35302be40ed7c99e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,6 +10,8 @@ Changelog * Address class has now from_pubkey class method * Message class improved * beempy message can be used to sign and to verify a message +* decryption of long messages fixed +* varint decoding added to memo decryption 0.23.11 ------- diff --git a/beem/transactionbuilder.py b/beem/transactionbuilder.py index 4ed74f7922f0c907159bb6ae092414579e11b4d0..0f661b89054dd15fdf00e0b31d49772b053f8878 100644 --- a/beem/transactionbuilder.py +++ b/beem/transactionbuilder.py @@ -534,6 +534,8 @@ class TransactionBuilder(dict): self.ops = [] self.wifs = set() self.signing_accounts = [] + self.ref_block_num = None + self.ref_block_prefix = None # This makes sure that _is_constructed will return False afterwards self["expiration"] = None super(TransactionBuilder, self).__init__({}) diff --git a/beembase/memo.py b/beembase/memo.py index 09745c04c647c8222c973b1b8eb05253485c1c68..19b31a48af41716ee5c25140575b423a133d7caa 100644 --- a/beembase/memo.py +++ b/beembase/memo.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals from builtins import bytes, int, str from beemgraphenebase.py23 import py23_bytes, bytes_types from beemgraphenebase.base58 import base58encode, base58decode +from beemgraphenebase.types import varintdecode import sys import hashlib from binascii import hexlify, unhexlify @@ -24,44 +25,52 @@ default_prefix = "STM" def get_shared_secret(priv, pub): """ Derive the share secret between ``priv`` and ``pub`` - :param `Base58` priv: Private Key :param `Base58` pub: Public Key :return: Shared secret :rtype: hex - The shared secret is generated such that:: - Pub(Alice) * Priv(Bob) = Pub(Bob) * Priv(Alice) - """ pub_point = pub.point() priv_point = int(repr(priv), 16) res = pub_point * priv_point - res_hex = '%032x' % res.x() + res_hex = "%032x" % res.x() # Zero padding - res_hex = '0' * (64 - len(res_hex)) + res_hex + res_hex = "0" * (64 - len(res_hex)) + res_hex return res_hex -def init_aes_bts(shared_secret, nonce): +def init_aes(shared_secret, nonce): """ Initialize AES instance - :param hex shared_secret: Shared Secret to use as encryption key :param int nonce: Random nonce :return: AES instance :rtype: AES + """ + " Shared Secret " + ss = hashlib.sha512(unhexlify(shared_secret)).digest() + " Seed " + seed = py23_bytes(str(nonce), "ascii") + hexlify(ss) + seed_digest = hexlify(hashlib.sha512(seed).digest()).decode("ascii") + " AES " + key = unhexlify(seed_digest[0:64]) + iv = unhexlify(seed_digest[64:96]) + return AES.new(key, AES.MODE_CBC, iv) + +def init_aes_bts(shared_secret, nonce): + """ Initialize AES instance + :param hex shared_secret: Shared Secret to use as encryption key + :return: AES instance + :rtype: AES """ - # Shared Secret + " Shared Secret " ss = hashlib.sha512(unhexlify(shared_secret)).digest() - # Seed - seed = py23_bytes(str(nonce), 'ascii') + hexlify(ss) - seed_digest = hexlify(hashlib.sha512(seed).digest()).decode('ascii') - # Check'sum' - check = hashlib.sha256(unhexlify(seed_digest)).digest() - check = struct.unpack_from("<I", check[:4])[0] - # AES + " Seed " + seed = bytes(str(nonce), "ascii") + hexlify(ss) + seed_digest = hexlify(hashlib.sha512(seed).digest()).decode("ascii") + " AES " key = unhexlify(seed_digest[0:64]) iv = unhexlify(seed_digest[64:96]) return AES.new(key, AES.MODE_CBC, iv) @@ -69,11 +78,8 @@ def init_aes_bts(shared_secret, nonce): def init_aes(shared_secret, nonce): """ Initialize AES instance - :param hex shared_secret: Shared Secret to use as encryption key :param int nonce: Random nonce - :return: AES instance and checksum of the encryption key - :rtype: length 2 tuple """ shared_secret = hashlib.sha512(unhexlify(shared_secret)).hexdigest() # Seed @@ -90,13 +96,13 @@ def init_aes(shared_secret, nonce): def _pad(s, BS): - numBytes = (BS - len(s) % BS) - return s + numBytes * struct.pack('B', numBytes) + numBytes = BS - len(s) % BS + return s + numBytes * struct.pack("B", numBytes) def _unpad(s, BS): - count = int(struct.unpack('B', py23_bytes(s[-1], 'ascii'))[0]) - if py23_bytes(s[-count::], 'ascii') == count * struct.pack('B', count): + count = s[-1] + if s[-count::] == count * struct.pack("B", count): return s[:-count] return s @@ -114,17 +120,14 @@ def encode_memo_bts(priv, pub, nonce, message): """ shared_secret = get_shared_secret(priv, pub) aes = init_aes_bts(shared_secret, nonce) - # Checksum - raw = py23_bytes(message, 'utf8') + " Checksum " + raw = py23_bytes(message, "utf8") checksum = hashlib.sha256(raw).digest() - raw = (checksum[0:4] + raw) - # Padding - BS = 16 - # FIXME: this adds 16 bytes even if not required - if len(raw) % BS: - raw = _pad(raw, BS) - # Encryption - return hexlify(aes.encrypt(raw)).decode('ascii') + raw = checksum[0:4] + raw + " Padding " + raw = _pad(raw, 16) + " Encryption " + return hexlify(aes.encrypt(raw)).decode("ascii") def decode_memo_bts(priv, pub, nonce, message): @@ -142,15 +145,18 @@ def decode_memo_bts(priv, pub, nonce, message): """ shared_secret = get_shared_secret(priv, pub) aes = init_aes_bts(shared_secret, nonce) - # Encryption - raw = py23_bytes(message, 'ascii') + " Encryption " + raw = py23_bytes(message, "ascii") cleartext = aes.decrypt(unhexlify(raw)) - # TODO, verify checksum + " Checksum " + checksum = cleartext[0:4] message = cleartext[4:] - try: - return _unpad(message.decode('utf8'), 16) - except Exception: - raise ValueError(message) + message = _unpad(message, 16) + " Verify checksum " + check = hashlib.sha256(message).digest()[0:4] + if check != checksum: # pragma: no cover + raise ValueError("checksum verification failure") + return message.decode("utf8") def encode_memo(priv, pub, nonce, message, **kwargs): @@ -164,15 +170,12 @@ def encode_memo(priv, pub, nonce, message, **kwargs): :rtype: hex """ shared_secret = get_shared_secret(priv, pub) - aes, check = init_aes(shared_secret, nonce) - raw = py23_bytes(message, 'utf8') - # Padding - BS = 16 - if len(raw) % BS: - raw = _pad(raw, BS) - # Encryption - cipher = hexlify(aes.encrypt(raw)).decode('ascii') + " Padding " + raw = py23_bytes(message, "utf8") + raw = _pad(raw, 16) + " Encryption " + cipher = hexlify(aes.encrypt(raw)).decode("ascii") prefix = kwargs.pop("prefix", default_prefix) s = { "from": format(priv.pubkey, prefix), @@ -217,16 +220,15 @@ def decode_memo(priv, message): # Init encryption aes, checksum = init_aes(shared_secret, nonce) - # Check if not check == checksum: - raise AssertionError("Checksum failure") - + raise AssertionError("Checksum failure") # Encryption # remove the varint prefix (FIXME, long messages!) - message = cipher[2:] + numBytes = 16 - len(cipher) % 16 + n = 16 - numBytes + message = cipher[n:] message = aes.decrypt(unhexlify(py23_bytes(message, 'ascii'))) - try: - return _unpad(message.decode('utf8'), 16) - except: # noqa FIXME(sneak) - raise ValueError(message) + message = _unpad(message, 16) + n = varintdecode(message) + return '#' + message[len(message) - n:].decode("utf8") diff --git a/beemgraphenebase/signedtransactions.py b/beemgraphenebase/signedtransactions.py index 5e91fa0505ddf0ed222ec5be110b0e4b9d841a54..841a16e45ab75f784a125dedff7b42b0a8e3904f 100644 --- a/beemgraphenebase/signedtransactions.py +++ b/beemgraphenebase/signedtransactions.py @@ -45,8 +45,8 @@ class Signed_Transaction(GrapheneObject): """ Create a signed transaction and offer method to create the signature - :param num refNum: parameter ref_block_num (see :func:`beembase.transactions.getBlockParams`) - :param num refPrefix: parameter ref_block_prefix (see :func:`beembase.transactions.getBlockParams`) + :param num ref_block_num: reference block number + :param num ref_block_prefix: :param str expiration: expiration date :param array operations: array of operations """ diff --git a/tests/beembase/test_memo.py b/tests/beembase/test_memo.py index 6a4f66b5ed2f3dd385cd60947e1d0f3233d913a6..16d4ab7ece9a19c442b98a003fd32c8ad691f318 100644 --- a/tests/beembase/test_memo.py +++ b/tests/beembase/test_memo.py @@ -24,41 +24,78 @@ from beembase.memo import ( ) test_cases = [ - {'from': 'GPH7FPzbN7hnRk24T3Nh9MYM1xBaF5xyRYu8WtyTrtLoUG8cUtszM', - 'message_bts': '688fe6c97f78ad2d3c5a82d9aa61bc23', - 'message': '#FYu8pMPJxTv7q2geNLSQC8dm47uqdNtFLCoDY5yZWjAz2R4wNyHEwQ48hPWm9SuAZ6fCFmjQrFCBVQFSP7EkobrWWRGaeqH6msKkPjRsMd6UUaNva1nmtLc55RAzqPLht', - 'nonce': '16332877645293003478', - 'plain': u'I am this!', - 'to': 'GPH6HAMuJRkjGJkj6cZWBbTU13gkUhBep383prqRdExXsZsYTrWT5', - 'wif': '5Jpkeq1jiNE8Pe24GxFWTsyWbcP59Qq4cD7qg3Wgd6JFJqJkoG8'}, - {'from': 'GPH7FPzbN7hnRk24T3Nh9MYM1xBaF5xyRYu8WtyTrtLoUG8cUtszM', - 'message_bts': 'db7ab7dfefee3ffa2394ec438601ceff', - 'message': '#FYu8pMPJxTv7q2geNLSQC8dm47uqdNtFLCoDY5yZWjAz2R4wNyHEwQ48hPWm9SuAZ6fCFmjQrFCBVQFSP7EkobrWWRGaeqH6msKkPjRsMd6pNxowQQGhkWuR9z5W1aLau', - 'nonce': '16332877645293003478', - 'plain': u'Hello World', - 'to': 'GPH6HAMuJRkjGJkj6cZWBbTU13gkUhBep383prqRdExXsZsYTrWT5', - 'wif': '5Jpkeq1jiNE8Pe24GxFWTsyWbcP59Qq4cD7qg3Wgd6JFJqJkoG8'}, - {'from': 'GPH7FPzbN7hnRk24T3Nh9MYM1xBaF5xyRYu8WtyTrtLoUG8cUtszM', - 'message_bts': '01b6616cbd10bdd0743c82c2bd580651f3e852360a739e7d11c45f483871dc45', - 'message': '#FYu8pMPJxTv7q2geNLSQC8dm47uqdNtFLCoDY5yZWjAz2R4wNyHEwQ48hPWm9SuAZ6fCFmjQrFCBVQFSP7EkobrWWRGaeqH6msKkPjRsMd6iKUwipf3H34zh3CAZVHNDy', - 'nonce': '16332877645293003478', - 'plain': u'Daniel Larimer', - 'to': 'GPH6HAMuJRkjGJkj6cZWBbTU13gkUhBep383prqRdExXsZsYTrWT5', - 'wif': '5Jpkeq1jiNE8Pe24GxFWTsyWbcP59Qq4cD7qg3Wgd6JFJqJkoG8'}, - {'from': 'GPH7FPzbN7hnRk24T3Nh9MYM1xBaF5xyRYu8WtyTrtLoUG8cUtszM', - 'message_bts': '24702af49bc82e06eb74a4acd91b18c389b13a6c9850a0fd3f728f486fe6daf4', - 'message': '#FYu8pMPJxTv7q2geNLSQC8dm47uqdNtFLCoDY5yZWjAz2R4wNyHEwQ48hPWm9SuAZ6fCFmjQrFCBVQFSP7EkobrWWRGaeqH6msKkPjRsMd6QyDwh8a4rxrSLnY2H4ztCK', - 'nonce': '16332877645293003478', - 'plain': u'Thanks you, sir!', - 'to': 'GPH6HAMuJRkjGJkj6cZWBbTU13gkUhBep383prqRdExXsZsYTrWT5', - 'wif': '5Jpkeq1jiNE8Pe24GxFWTsyWbcP59Qq4cD7qg3Wgd6JFJqJkoG8'}, - {'from': 'GPH7FPzbN7hnRk24T3Nh9MYM1xBaF5xyRYu8WtyTrtLoUG8cUtszM', - 'message_bts': 'db059f7a0f9053b041cd95c373ed9dff3445491d03ef17c490870ebcfcc6ec61a53718ec6cc8f5d81da6fcaa77b40d19', - 'message': '#5Kh3GamVLQtmU7PRHr6gyvAXqKtcRUaDy7Yp4BWqFuNeRq88ioc6rTGMGc7bRC1PtUV2LAeqsiQtbuRgPFSppVXPccS5BSWfqSxMF7ytAbmafekm2DweU1F2nqYwFgWYVe8wsHQdZVpzCdm8BJUY4xBCEU2xrB8nX4559EKag5BuU', - 'nonce': '16332877645293003478', - 'plain': u'äöü߀@$²³', - 'to': 'GPH6HAMuJRkjGJkj6cZWBbTU13gkUhBep383prqRdExXsZsYTrWT5', - 'wif': '5Jpkeq1jiNE8Pe24GxFWTsyWbcP59Qq4cD7qg3Wgd6JFJqJkoG8'} + { + "from": "GPH7FPzbN7hnRk24T3Nh9MYM1xBaF5xyRYu8WtyTrtLoUG8cUtszM", + "message_bts": "688fe6c97f78ad2d3c5a82d9aa61bc23", + 'message': '#FYu8pMPJxTv7q2geNLSQC8dm47uqdNtFLCoDY5yZWjAz2R4wNyHEwQ48hPWm9SuAZ6fCFmjQrFCBVQFSP7EkobrWWRGaeqH6msKkPjRsMd6UUaNva1nmtLc55RAzqPLht', + "nonce": "16332877645293003478", + "plain": "I am this!", + "to": "GPH6HAMuJRkjGJkj6cZWBbTU13gkUhBep383prqRdExXsZsYTrWT5", + "wif": "5Jpkeq1jiNE8Pe24GxFWTsyWbcP59Qq4cD7qg3Wgd6JFJqJkoG8", + }, + { # no with integer nonce + "from": "GPH7FPzbN7hnRk24T3Nh9MYM1xBaF5xyRYu8WtyTrtLoUG8cUtszM", + "message_bts": "688fe6c97f78ad2d3c5a82d9aa61bc23", + 'message': '#FYu8pMPJxTv7q2geNLSQC8dm47uqdNtFLCoDY5yZWjAz2R4wNyHEwQ48hPWm9SuAZ6fCFmjQrFCBVQFSP7EkobrWWRGaeqH6msKkPjRsMd6UUaNva1nmtLc55RAzqPLht', + "nonce": 16332877645293003478, + "plain": "I am this!", + "to": "GPH6HAMuJRkjGJkj6cZWBbTU13gkUhBep383prqRdExXsZsYTrWT5", + "wif": "5Jpkeq1jiNE8Pe24GxFWTsyWbcP59Qq4cD7qg3Wgd6JFJqJkoG8", + }, + { + "from": "GPH7FPzbN7hnRk24T3Nh9MYM1xBaF5xyRYu8WtyTrtLoUG8cUtszM", + "message_bts": "db7ab7dfefee3ffa2394ec438601ceff", + 'message': '#FYu8pMPJxTv7q2geNLSQC8dm47uqdNtFLCoDY5yZWjAz2R4wNyHEwQ48hPWm9SuAZ6fCFmjQrFCBVQFSP7EkobrWWRGaeqH6msKkPjRsMd6pNxowQQGhkWuR9z5W1aLau', + "nonce": "16332877645293003478", + "plain": "Hello World", + "to": "GPH6HAMuJRkjGJkj6cZWBbTU13gkUhBep383prqRdExXsZsYTrWT5", + "wif": "5Jpkeq1jiNE8Pe24GxFWTsyWbcP59Qq4cD7qg3Wgd6JFJqJkoG8", + }, + { + "from": "GPH7FPzbN7hnRk24T3Nh9MYM1xBaF5xyRYu8WtyTrtLoUG8cUtszM", + "message_bts": "01b6616cbd10bdd0743c82c2bd580651f3e852360a739e7d11c45f483871dc45", + 'message': '#FYu8pMPJxTv7q2geNLSQC8dm47uqdNtFLCoDY5yZWjAz2R4wNyHEwQ48hPWm9SuAZ6fCFmjQrFCBVQFSP7EkobrWWRGaeqH6msKkPjRsMd6iKUwipf3H34zh3CAZVHNDy', + "nonce": "16332877645293003478", + "plain": "Daniel Larimer", + "to": "GPH6HAMuJRkjGJkj6cZWBbTU13gkUhBep383prqRdExXsZsYTrWT5", + "wif": "5Jpkeq1jiNE8Pe24GxFWTsyWbcP59Qq4cD7qg3Wgd6JFJqJkoG8", + }, + { + "from": "GPH7FPzbN7hnRk24T3Nh9MYM1xBaF5xyRYu8WtyTrtLoUG8cUtszM", + "message_bts": "24702af49bc82e06eb74a4acd91b18c389b13a6c9850a0fd3f728f486fe6daf4", + 'message': '#8vxJp5YDC1Mv7J8sShbhdyrDNyo2JFuUxMmkYvg3tREpXDxoAvZSxzJ8Yqhx6qCyKfpHVczST9ySdXQANy2XBdFpztTu29pUibJBUzoKWgKYQyn7ixqUKhkexUA9Vt7W4crzbvnHhoB9Xogj9xxyhiN', + "nonce": "16332877645293003478", + "plain": "Thanks you, sir!", + "to": "GPH6HAMuJRkjGJkj6cZWBbTU13gkUhBep383prqRdExXsZsYTrWT5", + "wif": "5Jpkeq1jiNE8Pe24GxFWTsyWbcP59Qq4cD7qg3Wgd6JFJqJkoG8", + }, + { + "from": "GPH7FPzbN7hnRk24T3Nh9MYM1xBaF5xyRYu8WtyTrtLoUG8cUtszM", + "message_bts": "1566da5b57e8e0fd9f530a352812a4197b8113df6495efdb246909c6ee1ffea6", + 'message': '#8vxJp5YDC1Mv7J8sShbhdyrDNyo2JFuUxMmkYvg3tREpXDxoAvZSxzJ8Yqhx6qCyKfpHVczST9ySdXQANy2XBdFpztTu29pUibJBUzoKWgKfW6Ew2qCiLrZFodoFmpQ77fg7HRGRRLN42jkJs3HxJf1', + "nonce": "16332877645293003478", + "plain": "äöü߀@$²³", + "to": "GPH6HAMuJRkjGJkj6cZWBbTU13gkUhBep383prqRdExXsZsYTrWT5", + "wif": "5Jpkeq1jiNE8Pe24GxFWTsyWbcP59Qq4cD7qg3Wgd6JFJqJkoG8", + }, + { + "from": "GPH6APYcWtrWXBhcrjPEhPz41bc98NxjnvufVVnRH1M8sjwtvFacz", + "message_bts": "40b7ed2efd5e23b97e3f3aec6319fda722194e08b4cee45b84566e2741916797", + "message": "#D2BAH3MLo3eMbJh9nR5jy53KXf22b55fQpNLXoGD4bqkE3EkiZirwL8GWsaFJ6g1RDzgRXiYXuNFwCyDddHzuL1Sxam5KCEMYZY4E5MmvMnv46ptN1Bur7Yuo7X6tfRtU", + "nonce": "10864609094208714729", + "plain": "1234567890\x02\x02", # final bytes LOOK LIKE padding + "to": "GPH7Ge953jTDzHKxFAzy19uhJtXxw8CbBM938hkSKWE3yXfRjLV57", + "wif": "5KR8jzysz2kbYy3TkL3x6NRxfNXwQUWyeVAF5ZagxdqKMawGgXG", + }, + { + "from": "GPH6APYcWtrWXBhcrjPEhPz41bc98NxjnvufVVnRH1M8sjwtvFacz", + "message_bts": "f43800298f9974c7b334bb1bf6224f236309520e99697f3980775231bfb4ef21", + "message": "#D2BAH3MLo3eMbJh9nR5jy53KXf22b55fQpNLXoGD4bqkE3EkiZirwL8GWsaFJ6g1RDzgRXiYXuNFwCyDddHzuL1SxTXsyHkXiqBXGwC9v8guy8xFQQ7w5dLFXVHHgmZSV", + "nonce": "8555724032490455626", + "plain": "abcdefghijÛ", # padding limit and last character is unicode + "to": "GPH7Ge953jTDzHKxFAzy19uhJtXxw8CbBM938hkSKWE3yXfRjLV57", + "wif": "5KR8jzysz2kbYy3TkL3x6NRxfNXwQUWyeVAF5ZagxdqKMawGgXG", + }, ] test_shared_secrets = [ @@ -75,35 +112,52 @@ test_shared_secrets = [ ] +not_enough_padding = [ + { + "from": "GPH6APYcWtrWXBhcrjPEhPz41bc98NxjnvufVVnRH1M8sjwtvFacz", + "message_bts": "0b93e05a3b017d00ee16dfea0c1a9d64", + "message": "#D2BAH3MLo3eMbJh9nR5jy53KXf22b55fQpNLXoGD4bqkE3EkiZirwL8GWsaFJ6g1RDzgRXiYXuNFwCyDddHzuL1SxQrkhcLU2k4tfkcKx1apw8mfzCCJ699LXJxnTgsZd", + "nonce": "7675159740645758991", + "plain": "abcdefghijÛ", + "to": "GPH7Ge953jTDzHKxFAzy19uhJtXxw8CbBM938hkSKWE3yXfRjLV57", + "wif": "5KR8jzysz2kbYy3TkL3x6NRxfNXwQUWyeVAF5ZagxdqKMawGgXG", + } +] + + class Testcases(unittest.TestCase): def test_padding(self): for l in range(0, 255): - s = bytes(l * chr(l), 'utf-8') - padded = _pad(s, 16).decode('utf-8') - self.assertEqual(s.decode('utf-8'), _unpad(padded, 16)) + s = bytes(l * chr(l), "utf-8") + padded = _pad(s, 16) + self.assertEqual(s, _unpad(padded, 16)) def test_decrypt_bts(self): for memo in test_cases: - dec = decode_memo_bts(PrivateKey(memo["wif"]), - PublicKey(memo["to"], prefix="GPH"), - memo["nonce"], - memo["message_bts"]) + dec = decode_memo_bts( + PrivateKey(memo["wif"]), + PublicKey(memo["to"], prefix="GPH"), + memo["nonce"], + memo["message_bts"], + ) self.assertEqual(memo["plain"], dec) def test_encrypt_bts(self): for memo in test_cases: - enc = encode_memo_bts(PrivateKey(memo["wif"]), - PublicKey(memo["to"], prefix="GPH"), - memo["nonce"], - memo["plain"]) + enc = encode_memo_bts( + PrivateKey(memo["wif"]), + PublicKey(memo["to"], prefix="GPH"), + memo["nonce"], + memo["plain"], + ) self.assertEqual(memo["message_bts"], enc) def test_decrypt(self): for memo in test_cases: dec = decode_memo(PrivateKey(memo["wif"]), memo["message"]) - self.assertEqual(memo["plain"], dec) + self.assertEqual(memo["plain"], dec[1:]) def test_encrypt(self): for memo in test_cases: @@ -114,7 +168,7 @@ class Testcases(unittest.TestCase): self.assertEqual(memo["message"], enc) def test_encrypt_decrypt(self): - base58 = u'#HU6pdQ4Hh8cFrDVooekRPVZu4BdrhAe9RxrWrei2CwfAApAPdM4PT5mSV9cV3tTuWKotYQF6suyM4JHFBZz4pcwyezPzuZ2na7uwhRcLqFotsqxWRBpaXkNks2QCnYLS8' + base58 = u'#HU6pdQ4Hh8cFrDVooekRPVZu4BdrhAe9RxrWrei2CwfAApAPdM4PT5mSV9cV3tTuWKotYQF6suyM4JHFBZz4pcwyezPzuZ2na7uwhRcLqFoxprno9kWoHiS766vPUKqGX' text = u'#爱' nonce = u'1462976530069648' wif = str(PasswordKey("", "", role="", prefix="STM").get_private_key()) @@ -123,7 +177,7 @@ class Testcases(unittest.TestCase): cypertext = encode_memo(private_key, public_key, nonce, - text, prefix="STM") + text[1:], prefix="STM") self.assertEqual(cypertext, base58) plaintext = decode_memo(private_key, cypertext) self.assertEqual(plaintext, text) @@ -147,5 +201,22 @@ class Testcases(unittest.TestCase): self.assertEqual( get_shared_secret(sender_private_key, receiver_public_key), - get_shared_secret(receiver_private_key, sender_public_key) + get_shared_secret(receiver_private_key, sender_public_key), + ) + + def test_decrypt_bugged_padding_bts(self): + for memo in not_enough_padding: + dec = decode_memo_bts( + PrivateKey(memo["wif"]), + PublicKey(memo["to"], prefix="GPH"), + memo["nonce"], + memo["message_bts"], + ) + self.assertEqual(memo["plain"], dec) + + def test_decrypt_bugged_padding(self): + for memo in not_enough_padding: + dec = decode_memo(PrivateKey(memo["wif"]), + memo["message"] ) + self.assertEqual(memo["plain"], dec[1:]) diff --git a/tests/beemgraphene/test_account.py b/tests/beemgraphene/test_account.py index fadc084b8ee1a3f52f1f8f4dc2b0ef082f23ca41..29881be03ce4ddc776a8b0d799f9d140595ba00d 100644 --- a/tests/beemgraphene/test_account.py +++ b/tests/beemgraphene/test_account.py @@ -7,7 +7,7 @@ from builtins import str import unittest from beemgraphenebase.base58 import Base58, base58encode from beemgraphenebase.bip32 import BIP32Key -from beemgraphenebase.account import BrainKey, Address, PublicKey, PrivateKey, PasswordKey, Mnemonic, MnemonicKey +from beemgraphenebase.account import BrainKey, Address, PublicKey, PrivateKey, PasswordKey, Mnemonic, MnemonicKey, BitcoinAddress from binascii import hexlify, unhexlify import sys import hashlib @@ -102,6 +102,26 @@ class Testcases(unittest.TestCase): "1Gu5191CVHmaoU3Zz3prept87jjnpFDrXL", ]) + def test_btcprivkeystr(self): + self.assertEqual([str(BitcoinAddress.from_pubkey(PrivateKey("5HvVz6XMx84aC5KaaBbwYrRLvWE46cH6zVnv4827SBPLorg76oq").pubkey)), + str(BitcoinAddress.from_pubkey(PrivateKey("5Jete5oFNjjk3aUMkKuxgAXsp7ZyhgJbYNiNjHLvq5xzXkiqw7R").pubkey, compressed=True)), + str(BitcoinAddress.from_pubkey(PrivateKey("5KDT58ksNsVKjYShG4Ls5ZtredybSxzmKec8juj7CojZj6LPRF7").pubkey, compressed=False)), + ], + ["1G7qw8FiVfHEFrSt3tDi6YgfAdrDrEM44Z", + "1E2jXCkSmLxirL31gHwi1UWTjUBxCgS7pq", + "1Gu5191CVHmaoU3Zz3prept87jjnpFDrXL", + ]) + + def test_gphprivkeystr(self): + self.assertEqual([str(Address.from_pubkey(PrivateKey("5HvVz6XMx84aC5KaaBbwYrRLvWE46cH6zVnv4827SBPLorg76oq").pubkey)), + str(Address.from_pubkey(PrivateKey("5Jete5oFNjjk3aUMkKuxgAXsp7ZyhgJbYNiNjHLvq5xzXkiqw7R").pubkey, compressed=True)), + str(Address.from_pubkey(PrivateKey("5KDT58ksNsVKjYShG4Ls5ZtredybSxzmKec8juj7CojZj6LPRF7").pubkey, compressed=False, prefix="BTS")), + ], + ["STMBXqRucGm7nRkk6jm7BNspTJTWRtNcx7k5", + "STM5tTDDR6M3mkcyVv16edsw8dGUyNQZrvKU", + "BTS4XPkBqYw882fH5aR5S8mMKXCaZ1yVA76f", + ]) + def test_PublicKey(self): self.assertEqual([str(PublicKey("BTS6UtYWWs3rkZGV8JA86qrgkG6tyFksgECefKE1MiH4HkLD8PFGL", prefix="BTS")), str(PublicKey("BTS8YAMLtNcnqGNd3fx28NP3WoyuqNtzxXpwXTkZjbfe9scBmSyGT", prefix="BTS")), @@ -166,6 +186,12 @@ class Testcases(unittest.TestCase): b = BrainKey() self.assertTrue(len(b.suggest()) > 0) + def test_child(self): + p = PrivateKey("5JWcdkhL3w4RkVPcZMdJsjos22yB5cSkPExerktvKnRNZR5gx1S") + p2 = p.child(b"Foobar") + self.assertIsInstance(p2, PrivateKey) + self.assertEqual(str(p2), "5JQ6AQmjpbEZjJBLnoa3BaWa9y3LDTUBeSDwEGQD2UjYkb1gY2x") + def test_BrainKey_sequences(self): b = BrainKey("COLORER BICORN KASBEKE FAERIE LOCHIA GOMUTI SOVKHOZ Y GERMAL AUNTIE PERFUMY TIME FEATURE GANGAN CELEMIN MATZO") keys = ["5Hsbn6kXio4bb7eW5bX7kTp2sdkmbzP8kGWoau46Cf7en7T1RRE", @@ -484,3 +510,32 @@ class Testcases(unittest.TestCase): mk.set_path_BIP48(role="memo") self.assertEqual(str(mk.get_private_key()), str(PrivateKey("L5GrFqdRsroM1Ym4aMdALQBL7xN9kNMru9JTgtwbHVZ4iGvx1184"))) self.assertEqual(str(mk.get_public_key()), str(PublicKey("02fa2cdf5a007b01b1911615a4fba9c2a864a1c1ed079d222e5d549d207412c601"))) + + def test_new_privatekey(self): + w = PrivateKey() + self.assertIsInstance(w, PrivateKey) + self.assertEqual(str(w)[0], "5") # is a wif key that starts with 5 + + def test_new_BrainKey(self): + w = BrainKey().get_private_key() + self.assertIsInstance(w, PrivateKey) + self.assertEqual(str(w)[0], "5") # is a wif key that starts with 5 + + def test_derive_private_key(self): + p = PrivateKey("5JWcdkhL3w4RkVPcZMdJsjos22yB5cSkPExerktvKnRNZR5gx1S") + p2 = p.derive_private_key(10) + self.assertEqual( + repr(p2), "2dc7cb99933132e25b37710f9ea806228b04a583da11a137ef97fd42c0007390" + ) + + def test_derive_child(self): + # NOTE: this key + offset pair is particularly nasty, as + # the resulting derived value is less then 64 bytes long. + # Thus, this test also tests for proper padding. + p = PrivateKey("5K6hMUtQB2xwjuz3SRR6uM5HNERWgBqcK7gPPZ31XtAyBNoATZd") + p2 = p.child( + b"\xaf\x8f: \xf6T?V\x0bM\xd8\x16 \xfd\xde\xe9\xb9\xac\x03\r\xba\xb2\x8d\x868-\xc2\x90\x80\xe8\x1b\xce" + ) + self.assertEqual( + repr(p2), "0c5fae344a513a4cfab312b24c08df2b2d6afa25c0ead0d3d1d0d3e76794109b" + )