Source code for pyswitcheo.crypto_utils
# !/usr/bin/env python
# -*- coding: utf-8 -*-
"""Crypto related wrapper functions."""
import re
import string
import logging
import base58
import hashlib
import binascii
from neocore.KeyPair import KeyPair
logger = logging.getLogger(__name__)
__HEX_STRING_REGEX_OBJ = re.compile(r"^([0-9A-Fa-f]{2})*$/")
MAX_TRANSACTION_ATTRIBUTE_SIZE = 65535
[docs]def is_hex(input_hex):
"""Check if the passed string is a hex string.
Empty string is always treated as hex.
"""
if input_hex == "":
return True
return all(c in set(string.hexdigits) for c in input_hex)
[docs]def ensure_hex(input_hex):
"""Check if the passed string is a hex string else raise exception.
Empty string is always treated as hex.
"""
if not is_hex(input_hex):
raise Exception(
"Expected a hexstring but got {input_hex}.".format(input_hex=input_hex)
)
[docs]def reverse_hex(input_hex):
"""Reverse a HEX string, treating 2 chars as a byte.
Expected results look like this:
reverse_hex('abcdef') = 'efcdab'
Args:
input_hex (str): Input string in hex which needs to be converted.
Returns:
Hex string reversed.
"""
return "".join([input_hex[x: x + 2] for x in range(0, len(input_hex), 2)][::-1])
[docs]def num_to_hex_string(num, size=1, little_endian=False):
"""Convert a given number to hex string.
Converts a number to a big endian hexstring of a suitable size, optionally little endian
Args:
num (int) : Input int for which we need to get the hex string
size (int) : The required size in bytes, eg 1 for Uint8, 2 for Uint16. Defaults to 1.
Returns:
(str)
"""
if num < 0:
raise Exception("num should be unsigned (>= 0)")
if size % 1 != 0:
raise TypeError("size must be a whole integer")
size = size * 2
hexstring = hex(num)[2:]
hexstr_len = len(hexstring)
output = (
hexstring if hexstr_len % size == 0 else ("0" * (size) + hexstring)[hexstr_len:]
)
if little_endian:
return reverse_hex(output)
return output
[docs]def num_to_var_int(num):
"""Convert a number to a variable length Int. Used for array length header.
Detailed explanation
Args:
num (int) : A number
Returns:
(str) hexstring of the variable Int
"""
if num < 0xfd:
return num_to_hex_string(num)
elif num <= 0xffff:
# uint16
return "fd" + num_to_hex_string(num, 2, True)
elif num <= 0xffffffff:
# uint32
return "fe" + num_to_hex_string(num, 3, True)
else:
# uint64
return "ff" + num_to_hex_string(num, 8, True)
[docs]def encode_msg(msg):
"""Convert a given msg to its hex representation.
This is generally used when we send signed payload to the Switcheo api.
Args:
msg (str) : Input message which needs to be encoded.
Returns:
encoded message (str)
"""
if isinstance(msg, str):
msg = msg.encode("UTF-8")
encoded_msg = binascii.hexlify(msg)
length_hex = hex(int(len(encoded_msg) / 2))[2:]
logger.debug("Length of hex message {0}".format(length_hex))
encoded_msg = "010001f0{hex_len}{enc_msg}0000".format(
hex_len=length_hex, enc_msg=encoded_msg.decode()
)
logger.debug("Final message to sign : {0}".format(encoded_msg))
return encoded_msg
[docs]def get_private_key_from_wif(wif):
"""Fetch the private key from a wif represented in string format.
Args:
wif (str) : wif from which we need to extract the private key
Returns:
private key in bytearray format
"""
pk = KeyPair.PrivateKeyFromWIF(wif)
return KeyPair(pk).PrivateKey
[docs]def get_wif_from_private_key(priv_key):
"""Convert the given privatekey to a wif format.
Args:
priv_key (str) : private key in its hex string format.
Returns:
WIF format
"""
# Step 1: let's add 80 in front of it and 01 in the end
extended_key = "80" + priv_key + "01"
# Step 2: first SHA-256
first_sha256 = hashlib.sha256(binascii.unhexlify(extended_key)).hexdigest()
# Step 3: second SHA-256
second_sha256 = hashlib.sha256(binascii.unhexlify(first_sha256)).hexdigest()
# Step 4-5: add checksum to end of extended key
final_key = extended_key + second_sha256[:8]
# Step 6: finally the Wallet Import Format is the base 58 encode of final_key
return base58.b58encode(binascii.unhexlify(final_key))
[docs]def get_script_hash_from_wif(wif):
"""Fetch the script hash of the public key from a wif represented in string format.
Args:
wif (str) : wif from which we need to extract the public key script hash
Returns:
public key script hash in string format
"""
pk = KeyPair.PrivateKeyFromWIF(wif)
keypair = KeyPair(pk)
logger.debug("Public Address is {}".format(keypair.GetAddress()))
return get_script_hash_from_address(keypair.GetAddress())
[docs]def get_script_hash_from_address(address):
"""Convert a given address to script hash.
This code has been taken from:
https://github.com/CityOfZion/neo-python-core/blob/fcb0837e8f69e6f4dc01f2861b856affd2213446/neocore/bin/cli.py#L23
"""
data = bytes(base58.b58decode(address))
# Make sure signature byte is correct. In python 3, data[0] is bytes, and in 2 it's str.
# We use this isinstance checke to make it work with both Python 2 and 3.
is_correct_signature = data[0] != 0x17 if isinstance(data[0], bytes) else b"\x17"
if not is_correct_signature:
raise Exception("Invalid address: wrong signature byte")
# Make sure the checksum is correct
if data[-4:] != hashlib.sha256(hashlib.sha256(data[:-4]).digest()).digest()[:4]:
raise Exception("Invalid address: invalid checksum")
# Return only the scripthash bytes, reverse it and return hex of it.
return data[1:-4][::-1].hex()