Source code for cryptnox_sdk_py.cryptos.py3specials

# -*- coding: utf-8 -*-
"""
Module containing Python 3 specific cryptographic implementations.

Provides Python 3 optimized cryptographic functions including
elliptic curve operations, ECDSA signing/verification, and
mathematical utilities for secp256k1 operations.
"""

import sys
import os
import binascii
import hashlib


if sys.version_info.major == 3:
    string_or_bytes_types = (str, bytes)
    int_types = (int, float)
    # Base switching
    code_strings = {
        2: '01',
        10: '0123456789',
        16: '0123456789abcdef',
        32: 'abcdefghijklmnopqrstuvwxyz234567',
        58: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',
        256: ''.join([chr(x) for x in range(256)])
    }

[docs] def bin_dbl_sha256(s): bytes_to_hash = from_string_to_bytes(s) return hashlib.sha256(hashlib.sha256(bytes_to_hash).digest()).digest()
[docs] def lpad(msg, symbol, length): if len(msg) >= length: return msg return symbol * (length - len(msg)) + msg
[docs] def get_code_string(base): if base in code_strings: return code_strings[base] raise ValueError("Invalid base!")
[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 magicbyte == 0: inp = from_int_to_byte(0) + inp while magicbyte > 0: inp = from_int_to_byte(magicbyte % 256) + inp magicbyte //= 256 leadingzbytes = 0 for x in inp: if x != 0: break leadingzbytes += 1 checksum = bin_dbl_sha256(inp)[:4] return '1' * leadingzbytes + changebase(inp+checksum, 256, 58)
[docs] def bytes_to_hex_string(b): if isinstance(b, str): return b return ''.join(f'{y:02x}' for y in b)
[docs] def safe_from_hex(s): return bytes.fromhex(s)
[docs] def from_int_representation_to_bytes(a): return bytes(str(a), 'utf-8')
[docs] def from_int_to_byte(a): return bytes([a])
[docs] def from_byte_to_int(a): return a
[docs] def from_string_to_bytes(a): return a if isinstance(a, bytes) else bytes(a, 'utf-8')
[docs] def safe_hexlify(a): return str(binascii.hexlify(a), 'utf-8')
[docs] def encode(val, base, minlen=0): base, minlen = int(base), int(minlen) code_string = get_code_string(base) result_bytes = bytes() while val > 0: curcode = code_string[val % base] result_bytes = bytes([ord(curcode)]) + result_bytes val //= base pad_size = minlen - len(result_bytes) padding_element = b'\x00' if base == 256 else b'1' \ if base == 58 else b'0' if pad_size > 0: result_bytes = padding_element*pad_size + result_bytes result_string = ''.join([chr(y) for y in result_bytes]) result = result_bytes if base == 256 else result_string return result
[docs] def decode(string, base): if base == 256 and isinstance(string, str): string = bytes(bytearray.fromhex(string)) base = int(base) code_string = get_code_string(base) result = 0 if base == 256: def extract(d, _cs): return d else: def extract(d, cs): return cs.find(d if isinstance(d, str) else chr(d)) if base == 16: string = string.lower() while len(string) > 0: result *= base result += extract(string[0], code_string) string = string[1:] return result
[docs] def random_string(x): return str(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)
[docs] def multiply(pubkey, privkey): """Multiply a public key by a scalar (private key).""" from . import main if isinstance(privkey, int): privkey_int = privkey else: privkey_int = main.decode_privkey(privkey) # Decode public key to get the point if isinstance(pubkey, tuple): point = pubkey else: point = main.decode_pubkey(pubkey) # Fast multiplication using double-and-add algorithm if privkey_int == 0: return (0, 0) if privkey_int == 1: return point # Convert to Jacobian coordinates for faster computation result = (0, 0, 1) # Point at infinity in Jacobian addend = main.to_jacobian(point) while privkey_int: if privkey_int & 1: result = main.jacobian_add(result, addend) addend = main.jacobian_double(addend) privkey_int >>= 1 # Convert back to affine coordinates return from_jacobian(result)
[docs] def ecdsa_raw_verify(msghash, sig, pub): """Verify ECDSA signature.""" from . import main from . import py2specials if isinstance(msghash, bytes): msghash = py2specials.decode(msghash, 256) _, r, s = sig # Decode public key if not isinstance(pub, tuple): pub = main.decode_pubkey(pub) w = main.inv(s, main.N) z = msghash u1 = (z * w) % main.N u2 = (r * w) % main.N # Calculate point: u1*G + u2*pub point1 = multiply(main.G, u1) point2 = multiply(pub, u2) point = main.fast_add(point1, point2) return point[0] == r
[docs] def ecdsa_recover(msg, sig): """Recover public key from ECDSA signature.""" from . import main from . import py2specials if isinstance(msg, str): msg = msg.encode('utf-8') msghash = main.electrum_sig_hash(msg) if isinstance(sig, str): v, r, _s = main.decode_sig(sig) else: v, r, _s = sig # Recovery parameter x = r # Calculate y from x beta = pow(int(x * x * x + main.A * x + main.B), int((main.P + 1) // 4), int(main.P)) y = beta if (v - 27) % 2 == beta % 2 else (main.P - beta) # Recovered point R R = (x, y) # Calculate public key: (r*R - z*G) / r e = py2specials.decode(msghash, 256) # Calculate -e*G minus_e = main.N - e eG = multiply(main.G, minus_e) # Calculate r*R rR = multiply(R, r) # Add the points numerator = main.fast_add(rR, eG) # Divide by r (multiply by r^-1) r_inv = main.inv(r, main.N) pubkey = multiply(numerator, r_inv) return pubkey