def _CKD_priv(k, c, s, is_prime): order = generator_secp256k1.order() keypair = EC_KEY(k) cK = GetPubKey(keypair.pubkey,True) data = chr(0) + k + s if is_prime else cK + s I = hmac.new(c, data, hashlib.sha512).digest() k_n = number_to_string( (string_to_number(I[0:32]) + string_to_number(k)) % order , order ) c_n = I[32:] return k_n, c_n
def _CKD_pub(cK, c, s): order = generator_secp256k1.order() I = hmac.new(c, cK + s, hashlib.sha512).digest() curve = SECP256k1 pubkey_point = string_to_number(I[0:32])*curve.generator + ser_to_point(cK) public_key = ecdsa.VerifyingKey.from_public_point( pubkey_point, curve = SECP256k1 ) c_n = I[32:] cK_n = GetPubKey(public_key.pubkey,True) return cK_n, c_n
def digibox_sign(self, tx): try: change_keypath = None for i, txout in enumerate(tx.outputs): addr = tx.outputs[i][1] if self.is_change(addr): change_keypath = self.address_id(addr) require_pass = True; for i, txin in enumerate(tx.inputs): signatures = filter(None, txin['signatures']) num = txin['num_sig'] if len(signatures) == num: # Continue if this txin is complete. continue for x_pubkey in txin['x_pubkeys']: print_error("Creating signature for", x_pubkey) ii = tx.inputs[i]['x_pubkeys'].index(x_pubkey) keypath = self.address_id(tx.inputs[i]['address']) if True: for_sig = tx.tx_for_sig(i) msg = '{"sign": {"type":"transaction", "data":"%s", "keypath":"%s", "change_keypath":"%s"} }' % \ (for_sig, keypath, change_keypath) else: for_sig = Hash(tx.tx_for_sig(i).decode('hex')) for_sig = for_sig.encode('hex') msg = '{"sign": {"type":"hash", "data":"%s", "keypath":"%s"} }' % \ (for_sig, keypath) reply = self.commander(msg, require_pass) if reply==None: raise Exception("Could not sign transaction.") if 'sign' in reply: require_pass = False print_error("Adding signature for", x_pubkey) item = reply['sign'] tx.inputs[i]['x_pubkeys'][ii] = item['pubkey'] tx.inputs[i]['pubkeys'][ii] = item['pubkey'] r = int(item['sig'][:64], 16) s = int(item['sig'][64:], 16) sig = sigencode_der(r, s, generator_secp256k1.order()) tx.inputs[i]['signatures'][ii] = sig.encode('hex') else: raise Exception("Could not sign transaction.") except Exception as e: raise Exception(e) else: print_error("is_complete", tx.is_complete()) tx.raw = tx.serialize()
def CKD(k, c, n): import hmac from ecdsa.util import string_to_number, number_to_string order = generator_secp256k1.order() keypair = EC_KEY(string_to_number(k)) K = GetPubKey(keypair.pubkey,True) if n & BIP32_PRIME: data = chr(0) + k + rev_hex(int_to_hex(n,4)).decode('hex') I = hmac.new(c, data, hashlib.sha512).digest() else: I = hmac.new(c, K + rev_hex(int_to_hex(n,4)).decode('hex'), hashlib.sha512).digest() k_n = number_to_string( (string_to_number(I[0:32]) + string_to_number(k)) % order , order ) c_n = I[32:] return k_n, c_n
def CKD(k, c, n): import hmac from ecdsa.util import string_to_number, number_to_string order = generator_secp256k1.order() keypair = EC_KEY(k) K = GetPubKey(keypair.pubkey,True) if n & BIP32_PRIME: # We want to make a "secret" address that can't be determined from K data = chr(0) + k + rev_hex(int_to_hex(n,4)).decode('hex') I = hmac.new(c, data, hashlib.sha512).digest() else: # We want a "non-secret" address that can be determined from K I = hmac.new(c, K + rev_hex(int_to_hex(n,4)).decode('hex'), hashlib.sha512).digest() k_n = number_to_string( (string_to_number(I[0:32]) + string_to_number(k)) % order , order ) c_n = I[32:] return k_n, c_n
def encrypt_message(self, message, pubkey): pk = ser_to_point(pubkey) if not ecdsa.ecdsa.point_is_valid(generator_secp256k1, pk.x(), pk.y()): raise Exception('invalid pubkey') ephemeral_exponent = number_to_string(ecdsa.util.randrange(pow(2,256)), generator_secp256k1.order()) ephemeral = EC_KEY(ephemeral_exponent) ecdh_key = point_to_ser(pk * ephemeral.privkey.secret_multiplier) key = hashlib.sha512(ecdh_key).digest() iv, key_e, key_m = key[0:16], key[16:32], key[32:] ciphertext = aes_encrypt_with_iv(key_e, iv, message) ephemeral_pubkey = ephemeral.get_public_key(compressed=True).decode('hex') encrypted = 'BIE1' + ephemeral_pubkey + ciphertext mac = hmac.new(key_m, encrypted, hashlib.sha256).digest() return base64.b64encode(encrypted + mac)
def encrypt_message(self, message, pubkey, magic=b'BIE1'): assert_bytes(message) pk = ser_to_point(pubkey) if not ecdsa.ecdsa.point_is_valid(generator_secp256k1, pk.x(), pk.y()): raise Exception('invalid pubkey') ephemeral_exponent = number_to_string( ecdsa.util.randrange(pow(2, 256)), generator_secp256k1.order()) ephemeral = EC_KEY(ephemeral_exponent) ecdh_key = point_to_ser(pk * ephemeral.privkey.secret_multiplier) key = hashlib.sha512(ecdh_key).digest() iv, key_e, key_m = key[0:16], key[16:32], key[32:] ciphertext = aes_encrypt_with_iv(key_e, iv, message) ephemeral_pubkey = bfh(ephemeral.get_public_key(compressed=True)) encrypted = magic + ephemeral_pubkey + ciphertext mac = hmac.new(key_m, encrypted, hashlib.sha256).digest() return base64.b64encode(encrypted + mac)
def CKD_prime(K, c, n): import hmac from ecdsa.util import string_to_number, number_to_string order = generator_secp256k1.order() if n & BIP32_PRIME: raise K_public_key = ecdsa.VerifyingKey.from_string( K, curve = SECP256k1 ) K_compressed = GetPubKey(K_public_key.pubkey,True) I = hmac.new(c, K_compressed + rev_hex(int_to_hex(n,4)).decode('hex'), hashlib.sha512).digest() curve = SECP256k1 pubkey_point = string_to_number(I[0:32])*curve.generator + K_public_key.pubkey.point public_key = ecdsa.VerifyingKey.from_public_point( pubkey_point, curve = SECP256k1 ) K_n = public_key.to_string() K_n_compressed = GetPubKey(public_key.pubkey,True) c_n = I[32:] return K_n, K_n_compressed, c_n
def encrypt_message(self, message, pubkey): pk = ser_to_point(pubkey) if not ecdsa.ecdsa.point_is_valid(generator_secp256k1, pk.x(), pk.y()): raise Exception("invalid pubkey") ephemeral_exponent = number_to_string(ecdsa.util.randrange(pow(2, 256)), generator_secp256k1.order()) ephemeral = EC_KEY(ephemeral_exponent) ecdh_key = (pk * ephemeral.privkey.secret_multiplier).x() ecdh_key = ("%064x" % ecdh_key).decode("hex") key = hashlib.sha512(ecdh_key).digest() key_e, key_m = key[:32], key[32:] iv_ciphertext = aes.encryptData(key_e, message) ephemeral_pubkey = ephemeral.get_public_key(compressed=True).decode("hex") encrypted = "BIE1" + ephemeral_pubkey + iv_ciphertext mac = hmac.new(key_m, encrypted, hashlib.sha256).digest() return base64.b64encode(encrypted + mac)
def encrypt_message(self, message, pubkey): pk = ser_to_point(pubkey) if not ecdsa.ecdsa.point_is_valid(generator_secp256k1, pk.x(), pk.y()): raise Exception('invalid pubkey') ephemeral_exponent = number_to_string( ecdsa.util.randrange(pow(2, 256)), generator_secp256k1.order()) ephemeral = EC_KEY(ephemeral_exponent) ecdh_key = (pk * ephemeral.privkey.secret_multiplier).x() ecdh_key = ('%064x' % ecdh_key).decode('hex') key = hashlib.sha512(ecdh_key).digest() key_e, key_m = key[:32], key[32:] iv_ciphertext = aes.encryptData(key_e, message) ephemeral_pubkey = ephemeral.get_public_key( compressed=True).decode('hex') encrypted = 'BIE1' + ephemeral_pubkey + iv_ciphertext mac = hmac.new(key_m, encrypted, hashlib.sha256).digest() return base64.b64encode(encrypted + mac)
def sign_message(secret, message, compressed=True): private_key = SigningKey.from_secret_exponent(secret, curve=secp256k1) public_key = private_key.get_verifying_key() msg_hash = double_sha256(message) k = rfc6979.generate_k(generator_secp256k1, secret, sha256, msg_hash) % generator_secp256k1.order() signature = private_key.sign_digest( msg_hash, sigencode=util.sigencode_string_canonize, k=k) address = public_key_to_bc_address(encode_point(public_key, compressed)) assert public_key.verify_digest(signature, msg_hash, sigdecode=util.sigdecode_string) for i in range(4): nV = 27 + i if compressed: nV += 4 sig = base64.b64encode(chr(nV) + signature) try: if verify_message(address, sig, message): return sig except: continue else: raise BaseException("error: cannot sign message")
def sign_transaction(self, tx, password, *, use_cache=False): if tx.is_complete(): return try: p2pkhTransaction = True derivations = self.get_tx_derivations(tx) inputhasharray = [] hasharray = [] pubkeyarray = [] # Build hasharray from inputs for i, txin in enumerate(tx.inputs()): if txin['type'] == 'coinbase': self.give_error("Coinbase not supported") # should never happen if txin['type'] != 'p2pkh': p2pkhTransaction = False for x_pubkey in txin['x_pubkeys']: if x_pubkey in derivations: index = derivations.get(x_pubkey) inputPath = "%s/%d/%d" % (self.get_derivation(), index[0], index[1]) inputHash = Hash(binascii.unhexlify(tx.serialize_preimage(i))) hasharray_i = {'hash': to_hexstr(inputHash), 'keypath': inputPath} hasharray.append(hasharray_i) inputhasharray.append(inputHash) break else: self.give_error("No matching x_key for sign_transaction") # should never happen # Build pubkeyarray from outputs for _type, address, amount in tx.outputs(): info = tx.output_info.get(address) if info is not None: index, xpubs, m, script_type = info changePath = self.get_derivation() + "/%d/%d" % index changePubkey = self.derive_pubkey(index[0], index[1]) pubkeyarray_i = {'pubkey': changePubkey, 'keypath': changePath} pubkeyarray.append(pubkeyarray_i) # Special serialization of the unsigned transaction for # the mobile verification app. # At the moment, verification only works for p2pkh transactions. if p2pkhTransaction: class CustomTXSerialization(Transaction): @classmethod def input_script(self, txin, estimate_size=False, sign_schnorr=False): if txin['type'] == 'p2pkh': return Transaction.get_preimage_script(txin) if txin['type'] == 'p2sh': # Multisig verification has partial support, but is disabled. This is the # expected serialization though, so we leave it here until we activate it. return '00' + push_script(Transaction.get_preimage_script(txin)) raise Exception("unsupported type %s" % txin['type']) tx_dbb_serialized = CustomTXSerialization(tx.serialize()).serialize() else: # We only need this for the signing echo / verification. tx_dbb_serialized = None # Build sign command dbb_signatures = [] steps = math.ceil(1.0 * len(hasharray) / self.maxInputs) for step in range(int(steps)): hashes = hasharray[step * self.maxInputs : (step + 1) * self.maxInputs] msg = { "sign": { "data": hashes, "checkpub": pubkeyarray, }, } if tx_dbb_serialized is not None: msg["sign"]["meta"] = to_hexstr(Hash(tx_dbb_serialized)) msg = json.dumps(msg).encode('ascii') dbb_client = self.plugin.get_client(self) if not dbb_client.is_paired(): raise Exception("Could not sign transaction.") reply = dbb_client.hid_send_encrypt(msg) if 'error' in reply: raise Exception(reply['error']['message']) if 'echo' not in reply: raise Exception("Could not sign transaction.") if self.plugin.is_mobile_paired() and tx_dbb_serialized is not None: reply['tx'] = tx_dbb_serialized self.plugin.comserver_post_notification(reply) if steps > 1: self.handler.show_message(_("Signing large transaction. Please be patient ...") + "\n\n" + _("To continue, touch the Digital Bitbox's blinking light for 3 seconds.") + " " + _("(Touch {} of {})").format((step + 1), steps) + "\n\n" + _("To cancel, briefly touch the blinking light or wait for the timeout.") + "\n\n") else: self.handler.show_message(_("Signing transaction...") + "\n\n" + _("To continue, touch the Digital Bitbox's blinking light for 3 seconds.") + "\n\n" + _("To cancel, briefly touch the blinking light or wait for the timeout.")) # Send twice, first returns an echo for smart verification reply = dbb_client.hid_send_encrypt(msg) self.handler.finished() if 'error' in reply: if reply["error"].get('code') in (600, 601): # aborted via LED short touch or timeout raise UserCancelled() raise Exception(reply['error']['message']) if 'sign' not in reply: raise Exception("Could not sign transaction.") dbb_signatures.extend(reply['sign']) # Fill signatures if len(dbb_signatures) != len(tx.inputs()): raise Exception("Incorrect number of transactions signed.") # Should never occur for i, txin in enumerate(tx.inputs()): num = txin['num_sig'] for pubkey in txin['pubkeys']: signatures = list(filter(None, txin['signatures'])) if len(signatures) == num: break # txin is complete ii = txin['pubkeys'].index(pubkey) signed = dbb_signatures[i] if 'recid' in signed: # firmware > v2.1.1 recid = int(signed['recid'], 16) s = binascii.unhexlify(signed['sig']) h = inputhasharray[i] pk = MyVerifyingKey.from_signature(s, recid, h, curve = SECP256k1) pk = to_hexstr(point_to_ser(pk.pubkey.point, True)) elif 'pubkey' in signed: # firmware <= v2.1.1 pk = signed['pubkey'] if pk != pubkey: continue sig_r = int(signed['sig'][:64], 16) sig_s = int(signed['sig'][64:], 16) sig = sigencode_der(sig_r, sig_s, generator_secp256k1.order()) txin['signatures'][ii] = to_hexstr(sig) + '41' tx._inputs[i] = txin except UserCancelled: raise except BaseException as e: self.give_error(e, True) else: print_error("Transaction is_complete", tx.is_complete()) tx.raw = tx.serialize()
def is_infinity(P): return P == INFINITY # parse256(p): interprets a 32-byte sequence as a 256-bit number, most # significant byte first. def parse256(p): assert (len(p) == 32) return int.from_bytes(p, byteorder='big') def iH(x): return x + (1 << 31) n = generator_secp256k1.order() rformat = re.compile(r"^[0-9]+'?$") def ckd_pub(K_par, c_par, i): if i >= 1 << 31: raise BIP32Error("the child is a hardended key") I = hmac.digest(c_par, serP(K_par) + ser32(i), 'sha512') I_L, I_R = I[:32], I[32:] K_i = point(parse256(I_L)) + K_par c_i = I_R if parse256(I_L) >= n or is_infinity(K_i): raise BIP32Error("invalid i") return K_i, c_i
def get_private_key_from_stretched_exponent(self, for_change, n, secexp): order = generator_secp256k1.order() secexp = (secexp + self.get_sequence(for_change, n)) % order pk = number_to_string(secexp, generator_secp256k1.order()) compressed = False return SecretToASecret(pk, compressed)
Secp256k1PrivateKey = Secp256k1PrivateKeyCoincurve _CURVE_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 _GENERATOR = Secp256k1Point.FromCoordinates( 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8) # Use classes from ecdsa version else: from ecdsa.ecdsa import generator_secp256k1 from bip_utils.ecc.secp256k1.secp256k1_keys_ecdsa import ( Secp256k1PointEcdsa, Secp256k1PublicKeyEcdsa, Secp256k1PrivateKeyEcdsa) Secp256k1Point = Secp256k1PointEcdsa Secp256k1PublicKey = Secp256k1PublicKeyEcdsa Secp256k1PrivateKey = Secp256k1PrivateKeyEcdsa _CURVE_ORDER = generator_secp256k1.order() _GENERATOR = Secp256k1Point(generator_secp256k1) class Secp256k1Const: """Class container for Secp256k1 constants.""" # Curve name NAME: str = "Secp256k1" # Curve order CURVE_ORDER: int = _CURVE_ORDER # Curve generator point GENERATOR: IPoint = _GENERATOR
def derive_child(parent_priv, parent_pub, parent_chain_code, hardened=False, depth=0, index=0): if index < 0: raise ValueError("Child index should be greater than or equal to 0.") if depth < 0: raise ValueError("Depth should be greater than or equal to 0.") if not hardened: # concatenate parent public key || child index -> normal derivation parent_index = parent_pub + int.to_bytes( index, length=4, byteorder="big") else: # concatenate parent private key || child index -> hardened derivation parent_index = b"\x00" + parent_priv + int.to_bytes( index, length=4, byteorder="big") # HMAC that shit data = hmac.new(parent_chain_code, parent_index, digestmod=hashlib.sha512).digest() # set child chain code to IR child_chain_code = data[32:] il = data[:32] # build child private key -> parent private key + IL (scalar addition) (mod n) child_private_key = int.from_bytes(parent_priv, byteorder="big") + \ int.from_bytes(il, byteorder="big") child_private_key = child_private_key % generator_secp256k1.order() child_private_key = int.to_bytes(child_private_key, length=32, byteorder="big") # build child public key from SECP256k1 curve # compress the pubkey to 33 bytes # y point is discarded and replaced with 0x02 for evenness or 0x03 for oddness -> # the header byte privkey_obj = ecdsa.SigningKey.from_string(child_private_key, curve=ecdsa.SECP256k1) pubkey_obj = privkey_obj.get_verifying_key().to_string("compressed") child_public_key = pubkey_obj ##################################################################################### # Extend child private key extended_priv = MAINNET_XPRV # add version - 4 bytes extended_priv += int.to_bytes(depth, length=1, byteorder="big") # add depth - 1 byte extended_priv += int.to_bytes(index, length=4, byteorder="big") # add index - 4 bytes # add first 4 bytes (parent fingerprint) from hash160 hash (ripemd160(sha256(xpriv))) sha = hashlib.sha256(b"\x00" + parent_priv).digest() hash160 = hashlib.new("ripemd160") hash160.update(sha) hash160 = hash160.digest() extended_priv += hash160[:4] extended_priv += child_chain_code extended_priv += b"\x00" + child_private_key hashed_xpriv = hashlib.sha256(extended_priv).digest() hashed_xpriv = hashlib.sha256(hashed_xpriv).digest() extended_priv += hashed_xpriv[:4] ####################################################################################### # Extend child public key extended_pub = MAINNET_XPUB # add version - 4 bytes extended_pub += int.to_bytes(depth, length=1, byteorder="big") # add depth - 1 byte extended_pub += int.to_bytes(index, length=4, byteorder="big") # add index - 4 bytes # add first 4 bytes (parent fingerprint) from hash160 hash (ripemd160(sha256(xpub))) sha = hashlib.sha256(parent_pub).digest() hash160 = hashlib.new("ripemd160") hash160.update(sha) hash160 = hash160.digest() extended_pub += hash160[:4] extended_pub += child_chain_code extended_pub += child_public_key hashed_xpub = hashlib.sha256(extended_pub).digest() hashed_xpub = hashlib.sha256(hashed_xpub).digest() extended_pub += hashed_xpub[:4] return (b58encode(extended_priv), b58encode(extended_pub)),\ (child_private_key, child_public_key, child_chain_code)
def get_private_key_from_stretched_exponent(self, for_change, n, secexp): order = generator_secp256k1.order() secexp = (secexp + self.get_sequence(self.mpk, for_change, n)) % order pk = number_to_string(secexp, generator_secp256k1.order()) return pk
def sign_transaction(self, tx, password): if tx.is_complete(): return try: p2shTransaction = False derivations = self.get_tx_derivations(tx) inputhasharray = [] hasharray = [] pubkeyarray = [] # Build hasharray from inputs for i, txin in enumerate(tx.inputs()): if txin['type'] == 'coinbase': self.give_error("Coinbase not supported") # should never happen if txin['type'] in ['p2sh']: p2shTransaction = True for x_pubkey in txin['x_pubkeys']: if x_pubkey in derivations: index = derivations.get(x_pubkey) inputPath = "%s/%d/%d" % (self.get_derivation(), index[0], index[1]) inputHash = Hash(binascii.unhexlify(tx.serialize_preimage(i))) hasharray_i = {'hash': to_hexstr(inputHash), 'keypath': inputPath} hasharray.append(hasharray_i) inputhasharray.append(inputHash) break else: self.give_error("No matching x_key for sign_transaction") # should never happen # Sanity check if p2shTransaction: for txinput in tx.inputs(): if txinput['type'] != 'p2sh': self.give_error("P2SH / regular input mixed in same transaction not supported") # should never happen # Build pubkeyarray from outputs for _type, address, amount in tx.outputs(): assert _type == TYPE_ADDRESS info = tx.output_info.get(address) if info is not None: index, xpubs, m = info changePath = self.get_derivation() + "/%d/%d" % index changePubkey = self.derive_pubkey(index[0], index[1]) pubkeyarray_i = {'pubkey': changePubkey, 'keypath': changePath} pubkeyarray.append(pubkeyarray_i) # Special serialization of the unsigned transaction for # the mobile verification app. class CustomTXSerialization(Transaction): @classmethod def input_script(self, txin, estimate_size=False): if txin['type'] == 'p2pkh': return Transaction.get_preimage_script(txin) if txin['type'] == 'p2sh': return '00' + push_script(Transaction.get_preimage_script(txin)) raise Exception("unsupported type %s" % txin['type']) tx_dbb_serialized = CustomTXSerialization(tx.serialize()).serialize() # Build sign command dbb_signatures = [] steps = math.ceil(1.0 * len(hasharray) / self.maxInputs) for step in range(int(steps)): hashes = hasharray[step * self.maxInputs : (step + 1) * self.maxInputs] msg = ('{"sign": {"meta":"%s", "data":%s, "checkpub":%s} }' % \ (to_hexstr(Hash(tx_dbb_serialized)), json.dumps(hashes), json.dumps(pubkeyarray))).encode('utf8') dbb_client = self.plugin.get_client(self) if not dbb_client.is_paired(): raise Exception("Could not sign transaction.") reply = dbb_client.hid_send_encrypt(msg) if 'error' in reply: raise Exception(reply['error']['message']) if 'echo' not in reply: raise Exception("Could not sign transaction.") # multisig verification not working correctly yet if self.plugin.is_mobile_paired() and not p2shTransaction: reply['tx'] = tx_dbb_serialized self.plugin.comserver_post_notification(reply) if steps > 1: self.handler.show_message(_("Signing large transaction. Please be patient ...\r\n\r\n" \ "To continue, touch the Digital Bitbox's blinking light for 3 seconds. " \ "(Touch " + str(step + 1) + " of " + str(int(steps)) + ")\r\n\r\n" \ "To cancel, briefly touch the blinking light or wait for the timeout.\r\n\r\n")) else: self.handler.show_message(_("Signing transaction ...\r\n\r\n" \ "To continue, touch the Digital Bitbox's blinking light for 3 seconds.\r\n\r\n" \ "To cancel, briefly touch the blinking light or wait for the timeout.")) # Send twice, first returns an echo for smart verification reply = dbb_client.hid_send_encrypt(msg) self.handler.clear_dialog() if 'error' in reply: raise Exception(reply['error']['message']) if 'sign' not in reply: raise Exception("Could not sign transaction.") dbb_signatures.extend(reply['sign']) # Fill signatures if len(dbb_signatures) != len(tx.inputs()): raise Exception("Incorrect number of transactions signed.") # Should never occur for i, txin in enumerate(tx.inputs()): num = txin['num_sig'] for pubkey in txin['pubkeys']: signatures = list(filter(None, txin['signatures'])) if len(signatures) == num: break # txin is complete ii = txin['pubkeys'].index(pubkey) signed = dbb_signatures[i] if 'recid' in signed: # firmware > v2.1.1 recid = int(signed['recid'], 16) s = binascii.unhexlify(signed['sig']) h = inputhasharray[i] pk = MyVerifyingKey.from_signature(s, recid, h, curve = SECP256k1) pk = to_hexstr(point_to_ser(pk.pubkey.point, True)) elif 'pubkey' in signed: # firmware <= v2.1.1 pk = signed['pubkey'] if pk != pubkey: continue sig_r = int(signed['sig'][:64], 16) sig_s = int(signed['sig'][64:], 16) sig = sigencode_der(sig_r, sig_s, generator_secp256k1.order()) txin['signatures'][ii] = to_hexstr(sig) + '01' tx._inputs[i] = txin except BaseException as e: self.give_error(e, True) else: print_error("Transaction is_complete", tx.is_complete()) tx.raw = tx.serialize()
def sign_transaction(self, tx, password): if tx.is_complete(): return try: p2shTransaction = False derivations = self.get_tx_derivations(tx) hasharray = [] pubkeyarray = [] # Build hasharray from inputs for i, txin in enumerate(tx.inputs()): if txin.get('is_coinbase'): self.give_error( "Coinbase not supported") # should never happen if len(txin['pubkeys']) > 1: p2shTransaction = True for x_pubkey in txin['x_pubkeys']: if x_pubkey in derivations: index = derivations.get(x_pubkey) inputPath = "%s/%d/%d" % (self.get_derivation(), index[0], index[1]) inputHash = Hash( tx.tx_for_sig(i).decode('hex')).encode('hex') hasharray_i = {'hash': inputHash, 'keypath': inputPath} hasharray.append(hasharray_i) break else: self.give_error("No matching x_key for sign_transaction" ) # should never happen # Sanity check if p2shTransaction: for txinput in tx.inputs(): if len(txinput['pubkeys']) < 2: self.give_error( "P2SH / regular input mixed in same transaction not supported" ) # should never happen # Build pubkeyarray from outputs (unused because echo for smart verification not implemented) if not p2shTransaction: for _type, address, amount in tx.outputs(): assert _type == TYPE_ADDRESS info = tx.output_info.get(address) if info is not None: index, xpubs, m = info changePath = self.get_derivation() + "/%d/%d" % index changePubkey = self.derive_pubkey(index[0], index[1]) pubkeyarray_i = { 'pubkey': changePubkey, 'keypath': changePath } pubkeyarray.append(pubkeyarray_i) # Build sign command msg = '{"sign": {"meta":"%s", "data":%s, "checkpub":%s} }' % \ (Hash(tx.serialize()).encode('hex'), json.dumps(hasharray), json.dumps(pubkeyarray)) dbb_client = self.plugin.get_client(self) if not dbb_client.is_paired(): raise Exception("Could not sign transaction.") reply = dbb_client.hid_send_encrypt(msg) self.handler.show_message(_("Signing transaction ...\r\n\r\n" \ "To continue, touch the Digital Bitbox's blinking light for 3 seconds.\r\n\r\n" \ "To cancel, briefly touch the blinking light or wait for the timeout.")) reply = dbb_client.hid_send_encrypt( msg ) # Send twice, first returns an echo for smart verification (not implemented) self.handler.clear_dialog() if 'error' in reply: raise Exception(reply['error']['message']) if 'sign' not in reply: raise Exception("Could not sign transaction.") if len(reply['sign']) <> len(tx.inputs()): raise Exception("Incorrect number of transactions signed." ) # Should never occur # Fill signatures for i, txin in enumerate(tx.inputs()): num = txin['num_sig'] for pubkey in txin['pubkeys']: signatures = filter(None, txin['signatures']) if len(signatures) == num: break # txin is complete ii = txin['pubkeys'].index(pubkey) signed = reply['sign'][i] assert signed['pubkey'] == pubkey sig_r = int(signed['sig'][:64], 16) sig_s = int(signed['sig'][64:], 16) sig = sigencode_der(sig_r, sig_s, generator_secp256k1.order()) txin['signatures'][ii] = sig.encode('hex') tx._inputs[i] = txin except BaseException as e: self.give_error(e, True) else: print_error("Transaction is_complete", tx.is_complete()) tx.raw = tx.serialize()
def get_private_key_from_stretched_exponent(self, for_change, n, secexp): order = generator_secp256k1.order() secexp = ( secexp + self.get_sequence(for_change, n) ) % order pk = number_to_string( secexp, generator_secp256k1.order() ) compressed = False return SecretToASecret( pk, compressed )
def sign_transaction(self, tx, password): if tx.is_complete(): return try: p2shTransaction = False derivations = self.get_tx_derivations(tx) inputhasharray = [] hasharray = [] pubkeyarray = [] # Build hasharray from inputs for i, txin in enumerate(tx.inputs()): if txin['type'] == 'coinbase': self.give_error( "Coinbase not supported") # should never happen if txin['type'] in ['p2sh']: p2shTransaction = True for x_pubkey in txin['x_pubkeys']: if x_pubkey in derivations: index = derivations.get(x_pubkey) inputPath = "%s/%d/%d" % (self.get_derivation(), index[0], index[1]) inputHash = Hash( tx.serialize_preimage(i).decode('hex')) hasharray_i = { 'hash': inputHash.encode('hex'), 'keypath': inputPath } hasharray.append(hasharray_i) inputhasharray.append(inputHash) break else: self.give_error("No matching x_key for sign_transaction" ) # should never happen # Sanity check if p2shTransaction: for txinput in tx.inputs(): if txinput['type'] != 'p2sh': self.give_error( "P2SH / regular input mixed in same transaction not supported" ) # should never happen # Build pubkeyarray from outputs (unused because echo for smart verification not implemented) if not p2shTransaction: for _type, address, amount in tx.outputs(): assert _type == TYPE_ADDRESS info = tx.output_info.get(address) if info is not None: index, xpubs, m = info changePath = self.get_derivation() + "/%d/%d" % index changePubkey = self.derive_pubkey(index[0], index[1]) pubkeyarray_i = { 'pubkey': changePubkey, 'keypath': changePath } pubkeyarray.append(pubkeyarray_i) # Build sign command dbb_signatures = [] steps = math.ceil(1.0 * len(hasharray) / self.maxInputs) for step in range(int(steps)): hashes = hasharray[step * self.maxInputs:(step + 1) * self.maxInputs] msg = '{"sign": {"meta":"%s", "data":%s, "checkpub":%s} }' % \ (Hash(tx.serialize()).encode('hex'), json.dumps(hashes), json.dumps(pubkeyarray)) dbb_client = self.plugin.get_client(self) if not dbb_client.is_paired(): raise Exception("Could not sign transaction.") reply = dbb_client.hid_send_encrypt(msg) if 'error' in reply: raise Exception(reply['error']['message']) if 'echo' not in reply: raise Exception("Could not sign transaction.") if steps > 1: self.handler.show_message(_("Signing large transaction. Please be patient ...\r\n\r\n" \ "To continue, touch the Digital Bitbox's blinking light for 3 seconds. " \ "(Touch " + str(step + 1) + " of " + str(int(steps)) + ")\r\n\r\n" \ "To cancel, briefly touch the blinking light or wait for the timeout.\r\n\r\n")) else: self.handler.show_message(_("Signing transaction ...\r\n\r\n" \ "To continue, touch the Digital Bitbox's blinking light for 3 seconds.\r\n\r\n" \ "To cancel, briefly touch the blinking light or wait for the timeout.")) reply = dbb_client.hid_send_encrypt( msg ) # Send twice, first returns an echo for smart verification (not implemented) self.handler.clear_dialog() if 'error' in reply: raise Exception(reply['error']['message']) if 'sign' not in reply: raise Exception("Could not sign transaction.") dbb_signatures.extend(reply['sign']) # Fill signatures if len(dbb_signatures) <> len(tx.inputs()): raise Exception("Incorrect number of transactions signed." ) # Should never occur for i, txin in enumerate(tx.inputs()): num = txin['num_sig'] for pubkey in txin['pubkeys']: signatures = filter(None, txin['signatures']) if len(signatures) == num: break # txin is complete ii = txin['pubkeys'].index(pubkey) signed = dbb_signatures[i] if 'recid' in signed: # firmware > v2.1.1 recid = int(signed['recid'], 16) s = signed['sig'].decode('hex') h = inputhasharray[i] pk = MyVerifyingKey.from_signature(s, recid, h, curve=SECP256k1) pk = point_to_ser(pk.pubkey.point, True).encode('hex') elif 'pubkey' in signed: # firmware <= v2.1.1 pk = signed['pubkey'] if pk != pubkey: continue sig_r = int(signed['sig'][:64], 16) sig_s = int(signed['sig'][64:], 16) sig = sigencode_der(sig_r, sig_s, generator_secp256k1.order()) txin['signatures'][ii] = sig.encode('hex') + '01' tx._inputs[i] = txin except BaseException as e: self.give_error(e, True) else: print_error("Transaction is_complete", tx.is_complete()) tx.raw = tx.serialize()
def sign_transaction(self, tx, password): if tx.is_complete(): return try: p2shTransaction = False derivations = self.get_tx_derivations(tx) hasharray = [] pubkeyarray = [] # Build hasharray from inputs for i, txin in enumerate(tx.inputs()): if txin['type'] == 'coinbase': self.give_error("Coinbase not supported") # should never happen if txin['type'] in ['p2sh']: p2shTransaction = True for x_pubkey in txin['x_pubkeys']: if x_pubkey in derivations: index = derivations.get(x_pubkey) inputPath = "%s/%d/%d" % (self.get_derivation(), index[0], index[1]) inputHash = Hash(tx.serialize_preimage(i).decode('hex')).encode('hex') hasharray_i = {'hash': inputHash, 'keypath': inputPath} hasharray.append(hasharray_i) break else: self.give_error("No matching x_key for sign_transaction") # should never happen # Sanity check if p2shTransaction: for txinput in tx.inputs(): if txinput['type'] != 'p2sh': self.give_error("P2SH / regular input mixed in same transaction not supported") # should never happen # Build pubkeyarray from outputs (unused because echo for smart verification not implemented) if not p2shTransaction: for _type, address, amount in tx.outputs(): assert _type == TYPE_ADDRESS info = tx.output_info.get(address) if info is not None: index, xpubs, m = info changePath = self.get_derivation() + "/%d/%d" % index changePubkey = self.derive_pubkey(index[0], index[1]) pubkeyarray_i = {'pubkey': changePubkey, 'keypath': changePath} pubkeyarray.append(pubkeyarray_i) # Build sign command dbb_signatures = [] steps = math.ceil(1.0 * len(hasharray) / self.maxInputs) for step in range(int(steps)): hashes = hasharray[step * self.maxInputs : (step + 1) * self.maxInputs] msg = '{"sign": {"meta":"%s", "data":%s, "checkpub":%s} }' % \ (Hash(tx.serialize()).encode('hex'), json.dumps(hashes), json.dumps(pubkeyarray)) dbb_client = self.plugin.get_client(self) if not dbb_client.is_paired(): raise Exception("Could not sign transaction.") reply = dbb_client.hid_send_encrypt(msg) if 'error' in reply: raise Exception(reply['error']['message']) if 'echo' not in reply: raise Exception("Could not sign transaction.") if steps > 1: self.handler.show_message(_("Signing large transaction. Please be patient ...\r\n\r\n" \ "To continue, touch the Digital Bitbox's blinking light for 3 seconds. " \ "(Touch " + str(step + 1) + " of " + str(int(steps)) + ")\r\n\r\n" \ "To cancel, briefly touch the blinking light or wait for the timeout.\r\n\r\n")) else: self.handler.show_message(_("Signing transaction ...\r\n\r\n" \ "To continue, touch the Digital Bitbox's blinking light for 3 seconds.\r\n\r\n" \ "To cancel, briefly touch the blinking light or wait for the timeout.")) reply = dbb_client.hid_send_encrypt(msg) # Send twice, first returns an echo for smart verification (not implemented) self.handler.clear_dialog() if 'error' in reply: raise Exception(reply['error']['message']) if 'sign' not in reply: raise Exception("Could not sign transaction.") dbb_signatures.extend(reply['sign']) # Fill signatures if len(dbb_signatures) <> len(tx.inputs()): raise Exception("Incorrect number of transactions signed.") # Should never occur for i, txin in enumerate(tx.inputs()): num = txin['num_sig'] for pubkey in txin['pubkeys']: signatures = filter(None, txin['signatures']) if len(signatures) == num: break # txin is complete ii = txin['pubkeys'].index(pubkey) signed = dbb_signatures[i] if signed['pubkey'] != pubkey: continue sig_r = int(signed['sig'][:64], 16) sig_s = int(signed['sig'][64:], 16) sig = sigencode_der(sig_r, sig_s, generator_secp256k1.order()) txin['signatures'][ii] = sig.encode('hex') tx._inputs[i] = txin except BaseException as e: self.give_error(e, True) else: print_error("Transaction is_complete", tx.is_complete()) tx.raw = tx.serialize()
def test_secp256k1(self): # Curve self.assertEqual(Secp256k1.Name(), "Secp256k1") self.assertEqual(Secp256k1.Order(), generator_secp256k1.order()) self.assertEqual(Secp256k1.Generator().X(), generator_secp256k1.x()) self.assertEqual(Secp256k1.Generator().Y(), generator_secp256k1.y()) self.assertTrue(Secp256k1.PointClass() is Secp256k1Point) self.assertTrue(Secp256k1.PublicKeyClass() is Secp256k1PublicKey) self.assertTrue(Secp256k1.PrivateKeyClass() is Secp256k1PrivateKey) # # Public key # self.assertRaises(TypeError, Secp256k1PublicKey, 0) self.assertEqual(Secp256k1PublicKey.CurveType(), EllipticCurveTypes.SECP256K1) self.assertEqual(Secp256k1PublicKey.CompressedLength(), 33) self.assertEqual(Secp256k1PublicKey.UncompressedLength(), 65) # From compressed pub_key = Secp256k1PublicKey.FromBytes( TEST_SECP256K1_COMPR_PUB_KEY_BYTES) self.assertEqual(pub_key.RawCompressed().ToBytes(), TEST_SECP256K1_COMPR_PUB_KEY_BYTES) self.assertEqual(pub_key.RawUncompressed().ToBytes(), TEST_SECP256K1_UNCOMPR_PUB_KEY_BYTES) self.assertTrue( isinstance( pub_key.UnderlyingObject(), coincurve.PublicKey if EccConf.USE_COINCURVE else ecdsa.VerifyingKey)) self.assertTrue(isinstance(pub_key.Point(), Secp256k1Point)) # From uncompressed pub_key = Secp256k1PublicKey.FromBytes( TEST_SECP256K1_UNCOMPR_PUB_KEY_BYTES) self.assertEqual(pub_key.RawCompressed().ToBytes(), TEST_SECP256K1_COMPR_PUB_KEY_BYTES) self.assertEqual(pub_key.RawUncompressed().ToBytes(), TEST_SECP256K1_UNCOMPR_PUB_KEY_BYTES) self.assertTrue( isinstance( pub_key.UnderlyingObject(), coincurve.PublicKey if EccConf.USE_COINCURVE else ecdsa.VerifyingKey)) self.assertTrue(isinstance(pub_key.Point(), Secp256k1Point)) # From point pub_key = Secp256k1PublicKey.FromPoint( Secp256k1Point.FromCoordinates(TEST_SECP256K1_POINT["x"], TEST_SECP256K1_POINT["y"])) self.assertEqual(pub_key.RawCompressed().ToBytes(), TEST_SECP256K1_COMPR_PUB_KEY_BYTES) self.assertEqual(pub_key.RawUncompressed().ToBytes(), TEST_SECP256K1_UNCOMPR_PUB_KEY_BYTES) # # Private key # self.assertRaises(TypeError, Secp256k1PrivateKey, 0) self.assertEqual(Secp256k1PrivateKey.CurveType(), EllipticCurveTypes.SECP256K1) self.assertEqual(Secp256k1PrivateKey.Length(), 32) priv_key = Secp256k1PrivateKey.FromBytes(TEST_SECP256K1_PRIV_KEY_BYTES) self.assertEqual(priv_key.Raw().ToBytes(), TEST_SECP256K1_PRIV_KEY_BYTES) self.assertTrue( isinstance( priv_key.UnderlyingObject(), coincurve.PrivateKey if EccConf.USE_COINCURVE else ecdsa.SigningKey)) self.assertTrue(isinstance(priv_key.PublicKey(), Secp256k1PublicKey)) self.assertEqual(priv_key.PublicKey().RawCompressed().ToBytes(), TEST_SECP256K1_COMPR_PUB_KEY_BYTES) # # Point # self.assertRaises(TypeError, Secp256k1Point, 0) point = pub_key.Point() self.assertTrue( isinstance( point.UnderlyingObject(), coincurve.PublicKey if EccConf.USE_COINCURVE else ellipticcurve.PointJacobi)) self.assertEqual(point.X(), TEST_SECP256K1_POINT["x"]) self.assertEqual(point.Y(), TEST_SECP256K1_POINT["y"]) self.assertEqual(point.Raw().ToBytes(), TEST_SECP256K1_POINT_DEC_BYTES) self.assertEqual(point.RawDecoded().ToBytes(), TEST_SECP256K1_POINT_DEC_BYTES) self.assertEqual(point.RawEncoded().ToBytes(), TEST_SECP256K1_POINT_ENC_BYTES) # Addition point_add = point + point self.assertEqual(point_add.X(), TEST_SECP256K1_POINT_ADD["x"]) self.assertEqual(point_add.Y(), TEST_SECP256K1_POINT_ADD["y"]) # Multiplication point_mul = point * 2 self.assertEqual(point_mul.X(), TEST_SECP256K1_POINT_MUL["x"]) self.assertEqual(point_mul.Y(), TEST_SECP256K1_POINT_MUL["y"]) # Reverse multiplication point_mul = 2 * point self.assertEqual(point_mul.X(), TEST_SECP256K1_POINT_MUL["x"]) self.assertEqual(point_mul.Y(), TEST_SECP256K1_POINT_MUL["y"]) # From bytes point = Secp256k1Point.FromBytes(TEST_SECP256K1_POINT_DEC_BYTES) self.assertEqual(point.X(), TEST_SECP256K1_POINT["x"]) self.assertEqual(point.Y(), TEST_SECP256K1_POINT["y"]) self.assertEqual(point.Raw().ToBytes(), TEST_SECP256K1_POINT_DEC_BYTES) point = Secp256k1Point.FromBytes(TEST_SECP256K1_POINT_ENC_BYTES) self.assertEqual(point.X(), TEST_SECP256K1_POINT["x"]) self.assertEqual(point.Y(), TEST_SECP256K1_POINT["y"]) self.assertEqual(point.Raw().ToBytes(), TEST_SECP256K1_POINT_DEC_BYTES)