Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
    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()
Ejemplo n.º 4
0
Archivo: bitcoin.py Proyecto: 9cat/misc
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
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
    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)
Ejemplo n.º 7
0
    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)
Ejemplo n.º 8
0
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
Ejemplo n.º 9
0
    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)
Ejemplo n.º 10
0
    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)
Ejemplo n.º 11
0
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")
Ejemplo n.º 12
0
    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()
Ejemplo n.º 13
0
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

Ejemplo n.º 14
0
 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)
Ejemplo n.º 15
0
    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
Ejemplo n.º 16
0
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)
Ejemplo n.º 17
0
 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
Ejemplo n.º 18
0
    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()
Ejemplo n.º 19
0
    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()
Ejemplo n.º 20
0
 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 )
Ejemplo n.º 21
0
    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()
Ejemplo n.º 22
0
    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()
Ejemplo n.º 23
0
    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)