def test_Bool(self): u = types.Bool(True) self.assertEqual(py23_bytes(u), b"\x01") self.assertEqual(str(u), 'true') u = types.Bool(False) self.assertEqual(py23_bytes(u), b"\x00") self.assertEqual(str(u), 'false')
def test_varint32(self): u = types.Varint32(2**32 - 1) self.assertEqual(py23_bytes(u), b"\xff\xff\xff\xff\x0f") self.assertEqual(str(u), str(4294967295)) u = types.Id(2**32 - 1) self.assertEqual(py23_bytes(u), b"\xff\xff\xff\xff\x0f") self.assertEqual(str(u), str(4294967295))
def deriveDigest(self, chain): chain_params = self.getChainParams(chain) # Chain ID self.chainid = chain_params["chain_id"] # Do not serialize signatures sigs = self.data["signatures"] self.data["signatures"] = [] # Get message to sign # bytes(self) will give the wire formated data according to # GrapheneObject and the data given in __init__() self.message = OctetString(unhexlify(self.chainid)).dump() for name, value in list(self.data.items()): if name == "operations": for operation in value: if isinstance(value, string_types): b = py23_bytes(operation, 'utf-8') else: b = py23_bytes(operation) self.message += OctetString(b).dump() elif name != "signatures": if isinstance(value, string_types): b = py23_bytes(value, 'utf-8') else: b = py23_bytes(value) self.message += OctetString(b).dump() self.digest = hashlib.sha256(self.message).digest() # restore signatures self.data["signatures"] = sigs
def encode_memo(priv, pub, nonce, message, **kwargs): """ Encode a message with a shared secret between Alice and Bob :param PrivateKey priv: Private Key (of Alice) :param PublicKey pub: Public Key (of Bob) :param int nonce: Random nonce :param str message: Memo message :return: Encrypted message :rtype: hex """ shared_secret = get_shared_secret(priv, pub) aes, check = init_aes(shared_secret, nonce) " 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), "to": format(pub, prefix), "nonce": nonce, "check": check, "encrypted": cipher, "prefix": prefix } tx = Memo(**s) return "#" + base58encode(hexlify(py23_bytes(tx)).decode("ascii"))
def test_string(self): u = types.String("HelloFoobar") self.assertEqual(py23_bytes(u), b"\x0bHelloFoobar") self.assertEqual(str(u), "HelloFoobar") u = types.String("\x07\x08\x09\x0a\x0b\x0c\x0d\x0e") self.assertEqual(py23_bytes(u), b"\x14u0007b\t\nu000bf\ru000e") self.assertEqual(str(u), "\x07\x08\x09\x0a\x0b\x0c\x0d\x0e")
def test_bytes_int(self): """ In Py3, bytes(int) -> bytes object of size given by the parameter initialized with null """ self.assertEqual(py23_bytes(5), b'\x00\x00\x00\x00\x00') # Test using newint: self.assertEqual(py23_bytes(int(5)), b'\x00\x00\x00\x00\x00') self.assertTrue(isinstance(py23_bytes(int(5)), bytes_types))
def test_Optional(self): u = types.Optional(types.Uint16(10)) self.assertEqual(py23_bytes(u), b"\x01\n\x00") self.assertEqual(str(u), '10') self.assertFalse(u.isempty()) u = types.Optional(None) self.assertEqual(py23_bytes(u), b"\x00") self.assertEqual(str(u), 'None') self.assertTrue(u.isempty())
def test_isinstance_bytes_subclass(self): """ Issue #89 """ value = py23_bytes(b'abc') class Magic(): def __bytes__(self): return py23_bytes(b'abc') self.assertEqual(value, py23_bytes(Magic()))
def test_array(self): u = types.Array([types.Uint8(10) for x in range(2)] + [11]) self.assertEqual(py23_bytes(u), b'\x03\n\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') self.assertEqual(str(u), "[10, 10, 11]") u = types.Set([types.Uint16(10) for x in range(10)]) self.assertEqual(py23_bytes(u), b"\n\n\x00\n\x00\n\x00\n\x00\n\x00\n\x00\n\x00\n\x00\n\x00\n\x00") self.assertEqual(str(u), "[10, 10, 10, 10, 10, 10, 10, 10, 10, 10]") u = types.Array(["Foobar"]) # We do not support py23_bytes of Array containing String only! # self.assertEqual(py23_bytes(u), b'') self.assertEqual(str(u), '["Foobar"]')
def decode_memo_bts(priv, pub, nonce, message): """ Decode a message with a shared secret between Alice and Bob :param PrivateKey priv: Private Key (of Bob) :param PublicKey pub: Public Key (of Alice) :param int nonce: Nonce used for Encryption :param bytes message: Encrypted Memo message :return: Decrypted message :rtype: str :raise ValueError: if message cannot be decoded as valid UTF-8 string """ shared_secret = get_shared_secret(priv, pub) aes = init_aes_bts(shared_secret, nonce) " Encryption " raw = py23_bytes(message, "ascii") cleartext = aes.decrypt(unhexlify(raw)) " Checksum " checksum = cleartext[0:4] message = cleartext[4:] 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 test_Signature(self): u = types.Signature(b"\x00" * 33) self.assertEqual(py23_bytes(u), b"\x00" * 33) self.assertEqual( str(u), '"000000000000000000000000000000000000000000000000000000000000000000"' )
def test_bytes_encoding_arg_non_kwarg(self): """ As above, but with a positional argument """ u = u'Unicode string: \u5b54\u5b50' b = py23_bytes(u, 'utf-8') self.assertEqual(b, u.encode('utf-8'))
def test_Static_variant(self): class Tmp(types.Uint16): def json(self): return "Foobar" u = types.Static_variant(Tmp(10), 10) self.assertEqual(py23_bytes(u), b"\n\n\x00") self.assertEqual(str(u), '[10, "Foobar"]')
def doit(self, printWire=False, ops=None): if ops is None: ops = [Operation(self.op)] tx = Signed_Transaction(ref_block_num=self.ref_block_num, ref_block_prefix=self.ref_block_prefix, expiration=self.expiration, operations=ops) tx = tx.sign([self.wif], chain=self.prefix) tx.verify([PrivateKey(self.wif, prefix=u"STM").pubkey], self.prefix) txWire = hexlify(py23_bytes(tx)).decode("ascii")
def __bytes__(self): # padding # Workaround to allow transfers in HIVE if self.symbol == "HBD": self.symbol = "SBD" elif self.symbol == "HIVE": self.symbol = "STEEM" symbol = self.symbol + "\x00" * (7 - len(self.symbol)) return (struct.pack("<q", int(self.amount)) + struct.pack("<b", self.precision) + py23_bytes(symbol, "ascii"))
def test_pubkey(self): tx = Ledger_Transaction(ref_block_num=ref_block_num, ref_block_prefix=ref_block_prefix, expiration=expiration, operations=[]) apdu = tx.build_apdu_pubkey() self.assertEqual(( py23_bytes(apdu) ), b'\xd4\x02\x00\x01\x15\x05\x80\x00\x000\x80\x00\x00\r\x80\x00\x00\x00\x80\x00\x00\x00\x80\x00\x00\x00' )
def hash_op(event): """ This method generates a hash of blockchain operation. """ if isinstance(event, dict) and "type" in event and "value" in event: op_type = event["type"] if len(op_type) > 10 and op_type[len(op_type) - 10:] == "_operation": op_type = op_type[:-10] op = event["value"] event = [op_type, op] data = json.dumps(event, sort_keys=True) return hashlib.sha1(py23_bytes(data, 'utf-8')).hexdigest()
def test_bytes_encoding_arg(self): """ The bytes class has changed in Python 3 to accept an additional argument in the constructor: encoding. It would be nice to support this without breaking the isinstance(..., bytes) test below. """ u = u'Unicode string: \u5b54\u5b50' b = py23_bytes(u, encoding='utf-8') self.assertEqual(b, u.encode('utf-8'))
def __bytes__(self): if self.data is None: return py23_bytes() b = b"" output = b"" for name, value in list(self.data.items()): if name == "operations": for operation in value: if isinstance(value, string_types): b = py23_bytes(operation, 'utf-8') else: b = py23_bytes(operation) output += OctetString(b).dump() elif name != "signatures": if isinstance(value, string_types): b = py23_bytes(value, 'utf-8') else: b = py23_bytes(value) output += OctetString(b).dump() return output
def sign(self, path="48'/13'/0'/0'/0'", chain=u"STEEM"): from ledgerblue.comm import getDongle dongle = getDongle(True) apdu_list = self.build_apdu(path, chain) for apdu in apdu_list: result = dongle.exchange(py23_bytes(apdu)) dongle.close() sigs = [] signature = result sigs.append(Signature(signature)) self.data["signatures"] = Array(sigs) return self
def __bytes__(self): if self.data is None: return py23_bytes() b = b"" encoder = asn1.Encoder() encoder.start() for name, value in list(self.data.items()): if name == "operations": for operation in value: if isinstance(value, string_types): b = py23_bytes(operation, 'utf-8') else: b = py23_bytes(operation) encoder.write(b, asn1.Numbers.OctetString) elif name != "signatures": if isinstance(value, string_types): b = py23_bytes(value, 'utf-8') else: b = py23_bytes(value) encoder.write(b, asn1.Numbers.OctetString) return encoder.output()
def get_pubkey(self, path="48'/13'/0'/0'/0'", request_screen_approval=False, prefix="STM"): from ledgerblue.comm import getDongle dongle = getDongle(True) apdu = self.build_apdu_pubkey(path, request_screen_approval) result = dongle.exchange(py23_bytes(apdu)) dongle.close() offset = 1 + result[0] address = result[offset + 1:offset + 1 + result[offset]] # public_key = result[1: 1 + result[0]] return PublicKey(address.decode(), prefix=prefix)
def verify(self, pubkeys=[], chain=None, recover_parameter=False): """Returned pubkeys have to be checked if they are existing""" if not chain: raise chain_params = self.getChainParams(chain) self.deriveDigest(chain) signatures = self.data["signatures"].data pubKeysFound = [] for signature in signatures: if recover_parameter: p = verify_message(self.message, py23_bytes(signature)) else: p = None if p is None: for i in range(4): try: p = verify_message(self.message, py23_bytes(signature), recover_parameter=i) phex = hexlify(p).decode('ascii') pubKeysFound.append(phex) except Exception: p = None else: phex = hexlify(p).decode('ascii') pubKeysFound.append(phex) for pubkey in pubkeys: if not isinstance(pubkey, PublicKey): raise Exception("Pubkeys must be array of 'PublicKey'") k = pubkey.unCompressed()[2:] if k not in pubKeysFound and repr(pubkey) not in pubKeysFound: k = PublicKey(PublicKey(k).compressed()) f = format(k, chain_params["prefix"]) raise Exception("Signature for %s missing!" % f) return pubKeysFound
def prehash_message(self, timestamp, account, method, params, nonce): """ Prepare a hash for the Conveyor API request with SHA256 according to https://github.com/steemit/rpc-auth Hashing of `second` is then done inside `ecdsasig.sign_message()`. :param str timestamp: valid iso8601 datetime ending in "Z" :param str account: valid steem blockchain account name :param str method: Conveyor method name to be called :param bytes param: base64 encoded request parameters :param bytes nonce: random 8 bytes """ first = hashlib.sha256(py23_bytes(timestamp + account + method + params, self.ENCODING)) return self.K + first.digest() + nonce
def test_sign_message(self, module): if module == "cryptography": if not ecda.CRYPTOGRAPHY_AVAILABLE: return ecda.SECP256K1_MODULE = "cryptography" elif module == "secp256k1": if not ecda.SECP256K1_AVAILABLE: return ecda.SECP256K1_MODULE = "secp256k1" else: ecda.SECP256K1_MODULE = module pub_key = py23_bytes(repr(PrivateKey(wif).pubkey), "latin") signature = ecda.sign_message("Foobar", wif) pub_key_sig = ecda.verify_message("Foobar", signature) self.assertEqual(hexlify(pub_key_sig), pub_key)
def __init__(self, url="https://conveyor.steemit.com", steem_instance=None): """ Initialize a Conveyor instance :param str url: (optional) URL to the Conveyor API, defaults to https://conveyor.steemit.com :param beem.steem.Steem steem_instance: Steem instance """ self.url = url self.steem = steem_instance or shared_steem_instance() self.id = 0 self.ENCODING = 'utf-8' self.TIMEFORMAT = '%Y-%m-%dT%H:%M:%S.%f' self.K = hashlib.sha256(py23_bytes('steem_jsonrpc_auth', self.ENCODING)).digest()
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 id(self): """ The transaction id of this transaction """ # Store signatures temporarily since they are not part of # transaction id sigs = self.data["signatures"] self.data.pop("signatures", None) # Generage Hash of the seriliazed version h = hashlib.sha256(py23_bytes(self)).digest() # recover signatures self.data["signatures"] = sigs # Return properly truncated tx hash return hexlify(h[:20]).decode("ascii")
def get_tx_size(self, op): """Returns the tx size of an operation""" ops = [Operation(op)] prefix = u"STEEM" wif = "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3" ref_block_num = 34294 ref_block_prefix = 3707022213 expiration = "2016-04-06T08:29:27" tx = Signed_Transaction(ref_block_num=ref_block_num, ref_block_prefix=ref_block_prefix, expiration=expiration, operations=ops) tx = tx.sign([wif], chain=prefix) txWire = hexlify(py23_bytes(tx)).decode("ascii") tx_size = len(txWire) return tx_size
def deriveDigest(self, chain): chain_params = self.getChainParams(chain) # Chain ID self.chainid = chain_params["chain_id"] # Do not serialize signatures sigs = self.data["signatures"] self.data["signatures"] = [] # Get message to sign # bytes(self) will give the wire formated data according to # GrapheneObject and the data given in __init__() self.message = unhexlify(self.chainid) + py23_bytes(self) self.digest = hashlib.sha256(self.message).digest() # restore signatures self.data["signatures"] = sigs