# -*- coding: utf-8 -*-
"""
Module containing Python 2 compatibility utilities.
Provides type definitions, string/bytes handling, and encoding
utilities for Python 2 compatibility in cryptographic operations.
"""
import sys
import re
import binascii
import os
import hashlib
is_python2 = sys.version_info.major == 2
if sys.version_info.major == 2:
try:
unicode_type = unicode # type: ignore
long_type = long # type: ignore
except NameError:
unicode_type = str
long_type = int
string_types = (str, unicode_type)
string_or_bytes_types = string_types
int_types = (int, float, long_type)
# Base switching
code_strings = {
2: '01',
10: '0123456789',
16: '0123456789abcdef',
32: 'abcdefghijklmnopqrstuvwxyz234567',
58: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',
256: ''.join([chr(x) for x in range(256)])
}
def bin_dbl_sha256(s):
bytes_to_hash = from_string_to_bytes(s)
return hashlib.sha256(hashlib.sha256(bytes_to_hash).digest()).digest()
def lpad(msg, symbol, length):
if len(msg) >= length:
return msg
return symbol * (length - len(msg)) + msg
def get_code_string(base):
if base in code_strings:
return code_strings[base]
raise ValueError("Invalid base!")
def changebase(string, frm, to, minlen=0):
if frm == to:
return lpad(string, get_code_string(frm)[0], minlen)
return encode(decode(string, frm), to, minlen)
def bin_to_b58check(inp, magicbyte=0):
if magicbyte == 0:
inp = '\x00' + inp
while magicbyte > 0:
inp = chr(int(magicbyte % 256)) + inp
magicbyte //= 256
leadingzbytes = len(re.match('^\x00*', inp).group(0))
checksum = bin_dbl_sha256(inp)[:4]
return '1' * leadingzbytes + changebase(inp+checksum, 256, 58)
def bytes_to_hex_string(b):
return b.encode('hex')
def safe_from_hex(s):
return s.decode('hex')
def from_int_representation_to_bytes(a):
return str(a)
def from_int_to_byte(a):
return chr(a)
def from_byte_to_int(a):
return ord(a)
def from_bytes_to_string(s):
return s
def from_string_to_bytes(a):
return a
def safe_hexlify(a):
return binascii.hexlify(a)
def encode(val, base, minlen=0):
base, minlen = int(base), int(minlen)
code_string = get_code_string(base)
result = ""
while val > 0:
result = code_string[val % base] + result
val //= base
return code_string[0] * max(minlen - len(result), 0) + result
def decode(string, base):
base = int(base)
code_string = get_code_string(base)
result = 0
if base == 16:
string = string.lower()
while len(string) > 0:
result *= base
result += code_string.find(string[0])
string = string[1:]
return result
def random_string(x):
return os.urandom(x)
else:
int_types = (int, float)
string_types = (str,)
code_strings = {
2: '01',
10: '0123456789',
16: '0123456789abcdef',
32: 'abcdefghijklmnopqrstuvwxyz234567',
58: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',
256: bytes(range(256))
}
[docs]
def bin_dbl_sha256(s):
if isinstance(s, str):
s = s.encode('utf-8')
return hashlib.sha256(hashlib.sha256(s).digest()).digest()
[docs]
def get_code_string(base):
if base in code_strings:
return code_strings[base]
raise ValueError("Invalid base!")
[docs]
def lpad(msg, symbol, length):
if len(msg) >= length:
return msg
# Handle bytes vs str
if isinstance(msg, bytes):
if isinstance(symbol, int):
symbol = bytes([symbol])
elif isinstance(symbol, str):
symbol = symbol.encode('ascii')
return symbol * (length - len(msg)) + msg
[docs]
def encode(val, base, minlen=0):
base, minlen = int(base), int(minlen)
code_string = get_code_string(base)
is_bytes = isinstance(code_string, bytes)
result = b"" if is_bytes else ""
while val > 0:
digit = code_string[val % base]
if is_bytes:
digit_char = bytes([digit])
else:
digit_char = digit
result = digit_char + result
val //= base
padding = code_string[0]
if is_bytes:
padding = bytes([padding])
return padding * max(minlen - len(result), 0) + result
[docs]
def decode(string, base):
base = int(base)
if base != 256 and isinstance(string, bytes):
string = string.decode('utf-8')
code_string = get_code_string(base)
result = 0
if base == 16:
string = string.lower()
if base == 256 and isinstance(string, bytes):
return int.from_bytes(string, 'big')
while len(string) > 0:
result *= base
char = string[0]
if isinstance(code_string, bytes):
if isinstance(char, str):
char = ord(char)
idx = char
else:
idx = code_string.find(char)
result += idx
string = string[1:]
return result
[docs]
def changebase(string, frm, to, minlen=0):
if frm == to:
return lpad(string, get_code_string(frm)[0], minlen)
return encode(decode(string, frm), to, minlen)
[docs]
def bin_to_b58check(inp, magicbyte=0):
if isinstance(inp, str):
inp = inp.encode('latin-1')
if magicbyte == 0:
inp = b'\x00' + inp
while magicbyte > 0:
inp = bytes([int(magicbyte % 256)]) + inp
magicbyte //= 256
leadingzbytes = 0
for x in inp:
if x != 0:
break
leadingzbytes += 1
checksum = bin_dbl_sha256(inp)[:4]
payload = inp + checksum
val = int.from_bytes(payload, 'big')
result = encode(val, 58)
return '1' * leadingzbytes + result
[docs]
def bytes_to_hex_string(b):
if isinstance(b, str):
return b
return binascii.hexlify(b).decode('ascii')
[docs]
def safe_from_hex(s):
if isinstance(s, bytes):
s = s.decode('ascii')
return binascii.unhexlify(s)
[docs]
def from_int_representation_to_bytes(a):
return str(a).encode('ascii')
[docs]
def from_int_to_byte(a):
return bytes([a])
[docs]
def from_byte_to_int(a):
if isinstance(a, int):
return a
return a[0]
[docs]
def from_bytes_to_string(s):
if isinstance(s, bytes):
return s.decode('utf-8')
return s
[docs]
def from_string_to_bytes(a):
if isinstance(a, str):
return a.encode('utf-8')
return a
[docs]
def safe_hexlify(a):
return binascii.hexlify(a)
[docs]
def random_string(x):
return os.urandom(x)
[docs]
def from_jacobian(p):
"""Convert Jacobian coordinates to affine coordinates."""
# Import P from main to avoid circular dependency
from . import main
z = main.inv(p[2], main.P)
return ((p[0] * z**2) % main.P, (p[1] * z**3) % main.P)