def _build_key(
    secret: bytes,
    discriminator: bytes,
    index: int | None = None,
    out: bytes | None = None,
) -> bytes:
    """
    Creates an unique-purpose key
    """

    key_buff = _BUILD_KEY_BUFFER
    utils.ensure(len(secret) == _SECRET_LENGTH, "Invalid key length")
    utils.ensure(len(discriminator) <= _DISCRIMINATOR_LENGTH, "Disc too long")

    offset = _SECRET_LENGTH
    utils.memcpy(key_buff, 0, secret, 0, _SECRET_LENGTH)

    for i in range(_SECRET_LENGTH, len(key_buff)):
        key_buff[i] = 0

    utils.memcpy(key_buff, offset, discriminator, 0, len(discriminator))
    offset += _DISCRIMINATOR_LENGTH  # fixed domain separator size

    if index is not None:
        # dump_uvarint_b_into, saving import
        shifted = True
        while shifted:
            shifted = index >> 7
            key_buff[offset] = (index & 0x7F) | (0x80 if shifted else 0x00)
            offset += 1
            index = shifted

    return crypto_helpers.keccak_2hash(key_buff, out)
Example #2
0
def _build_key(secret,
               discriminator=None,
               index: int = None,
               out: bytes = None) -> bytes:
    """
    Creates an unique-purpose key
    """
    key_buff = BUILD_KEY_BUFFER  # bytearray(32 + 12 + 4)  # key + disc + index
    utils.ensure(len(secret) == 32, "Invalid key length")
    utils.ensure(len(discriminator) <= 12, "Disc too long")

    offset = 32
    utils.memcpy(key_buff, 0, secret, 0, 32)

    for i in range(32, len(key_buff)):
        key_buff[i] = 0

    if discriminator is not None:
        utils.memcpy(key_buff, offset, discriminator, 0, len(discriminator))
        offset += len(discriminator)

    if index is not None:
        # dump_uvarint_b_into, saving import
        shifted = True
        while shifted:
            shifted = index >> 7
            key_buff[offset] = (index & 0x7F) | (0x80 if shifted else 0x00)
            offset += 1
            index = shifted

    return crypto.keccak_2hash(key_buff, out)
Example #3
0
async def send_cmd(cmd: Cmd, iface: io.HID) -> None:
    init_desc = frame_init()
    cont_desc = frame_cont()
    offset = 0
    seq = 0
    datalen = len(cmd.data)

    buf, frm = make_struct(init_desc)
    frm.cid = cmd.cid
    frm.cmd = cmd.cmd
    frm.bcnt = datalen

    offset += utils.memcpy(frm.data, 0, cmd.data, offset, datalen)
    iface.write(buf)

    if offset < datalen:
        frm = overlay_struct(buf, cont_desc)

    write = loop.wait(iface.iface_num() | io.POLL_WRITE)
    while offset < datalen:
        frm.seq = seq
        offset += utils.memcpy(frm.data, 0, cmd.data, offset, datalen)
        while True:
            await write
            if iface.write(buf) > 0:
                break
        seq += 1
Example #4
0
def msg_authenticate_sign(challenge: bytes, app_id: bytes, privkey: bytes) -> bytes:
    flags = bytes([_AUTH_FLAG_TUP])

    # get next counter
    ctr = storage.next_u2f_counter()
    ctrbuf = ustruct.pack(">L", ctr)

    # hash input data together with counter
    dig = hashlib.sha256()
    dig.update(app_id)  # uint8_t appId[32];
    dig.update(flags)  # uint8_t flags;
    dig.update(ctrbuf)  # uint8_t ctr[4];
    dig.update(challenge)  # uint8_t chal[32];
    dig = dig.digest()

    # sign the digest and convert to der
    sig = nist256p1.sign(privkey, dig, False)
    sig = der.encode_seq((sig[1:33], sig[33:]))

    # pack to a response
    buf, resp = make_struct(resp_cmd_authenticate(len(sig)))
    resp.flags = flags[0]
    resp.ctr = ctr
    utils.memcpy(resp.sig, 0, sig, 0, len(sig))
    resp.status = _SW_NO_ERROR

    return buf
Example #5
0
def send_cmd(cmd: Cmd, iface: io.HID) -> None:
    init_desc = frame_init()
    cont_desc = frame_cont()
    offset = 0
    seq = 0
    datalen = len(cmd.data)

    buf, frm = make_struct(init_desc)
    frm.cid = cmd.cid
    frm.cmd = cmd.cmd
    frm.bcnt = datalen

    offset += utils.memcpy(frm.data, 0, cmd.data, offset, datalen)
    iface.write(buf)
    # log.debug(__name__, 'send init %s', buf)

    if offset < datalen:
        frm = overlay_struct(buf, cont_desc)

    while offset < datalen:
        frm.seq = seq
        offset += utils.memcpy(frm.data, 0, cmd.data, offset, datalen)
        utime.sleep_ms(1)  # FIXME: async write
        iface.write(buf)
        # log.debug(__name__, 'send cont %s', buf)
        seq += 1
Example #6
0
async def read_cmd(iface: io.HID) -> Cmd:
    desc_init = frame_init()
    desc_cont = frame_cont()
    read = loop.wait(iface.iface_num() | io.POLL_READ)

    buf = await read

    ifrm = overlay_struct(buf, desc_init)
    bcnt = ifrm.bcnt
    data = ifrm.data
    datalen = len(data)
    seq = 0

    if ifrm.cmd & _TYPE_MASK == _TYPE_CONT:
        # unexpected cont packet, abort current msg
        if __debug__:
            log.warning(__name__, "_TYPE_CONT")
        return None

    if datalen < bcnt:
        databuf = bytearray(bcnt)
        utils.memcpy(databuf, 0, data, 0, bcnt)
        data = databuf
    else:
        data = data[:bcnt]

    while datalen < bcnt:
        buf = await read

        cfrm = overlay_struct(buf, desc_cont)

        if cfrm.seq == _CMD_INIT:
            # _CMD_INIT frame, cancels current channel
            ifrm = overlay_struct(buf, desc_init)
            data = ifrm.data[: ifrm.bcnt]
            break

        if cfrm.cid != ifrm.cid:
            # cont frame for a different channel, reply with BUSY and skip
            if __debug__:
                log.warning(__name__, "_ERR_CHANNEL_BUSY")
            await send_cmd(cmd_error(cfrm.cid, _ERR_CHANNEL_BUSY), iface)
            continue

        if cfrm.seq != seq:
            # cont frame for this channel, but incorrect seq number, abort
            # current msg
            if __debug__:
                log.warning(__name__, "_ERR_INVALID_SEQ")
            await send_cmd(cmd_error(cfrm.cid, _ERR_INVALID_SEQ), iface)
            return None

        datalen += utils.memcpy(data, datalen, cfrm.data, 0, bcnt - datalen)
        seq += 1

    return Cmd(ifrm.cid, ifrm.cmd, data)
Example #7
0
def msg_register_sign(challenge: bytes, app_id: bytes) -> bytes:

    from apps.common import seed

    # derivation path is m/U2F'/r'/r'/r'/r'/r'/r'/r'/r'
    keypath = [0x80000000 | random.uniform(0xf0000000) for _ in range(0, 8)]
    nodepath = [_U2F_KEY_PATH] + keypath

    # prepare signing key from random path, compute decompressed public key
    node = seed.get_root_without_passphrase('nist256p1')
    node.derive_path(nodepath)
    pubkey = nist256p1.publickey(node.private_key(), False)

    # first half of keyhandle is keypath
    keybuf = ustruct.pack('>8L', *keypath)

    # second half of keyhandle is a hmac of app_id and keypath
    keybase = hmac.Hmac(node.private_key(), app_id, hashlib.sha256)
    keybase.update(keybuf)
    keybase = keybase.digest()

    # hash the request data together with keyhandle and pubkey
    dig = hashlib.sha256()
    dig.update(b'\x00')  # uint8_t reserved;
    dig.update(app_id)  # uint8_t appId[32];
    dig.update(challenge)  # uint8_t chal[32];
    dig.update(keybuf)  # uint8_t keyHandle[64];
    dig.update(keybase)
    dig.update(pubkey)  # uint8_t pubKey[65];
    dig = dig.digest()

    # sign the digest and convert to der
    sig = nist256p1.sign(_U2F_ATT_PRIV_KEY, dig, False)
    sig = der.encode_seq((sig[1:33], sig[33:]))

    # pack to a response
    buf, resp = make_struct(
        resp_cmd_register(
            len(keybuf) + len(keybase), len(_U2F_ATT_CERT), len(sig)))
    resp.registerId = _U2F_REGISTER_ID
    utils.memcpy(resp.pubKey, 0, pubkey, 0, len(pubkey))
    resp.keyHandleLen = len(keybuf) + len(keybase)
    utils.memcpy(resp.keyHandle, 0, keybuf, 0, len(keybuf))
    utils.memcpy(resp.keyHandle, len(keybuf), keybase, 0, len(keybase))
    utils.memcpy(resp.cert, 0, _U2F_ATT_CERT, 0, len(_U2F_ATT_CERT))
    utils.memcpy(resp.sig, 0, sig, 0, len(sig))
    resp.status = _SW_NO_ERROR

    return buf
Example #8
0
async def write_message(iface: WireInterface, mtype: int,
                        mdata: bytes) -> None:
    write = loop.wait(iface.iface_num() | io.POLL_WRITE)

    # gather data from msg
    msize = len(mdata)

    # prepare the report buffer with header data
    report = bytearray(_REP_LEN)
    repofs = _REP_INIT_DATA
    ustruct.pack_into(_REP_INIT, report, 0, _REP_MARKER, _REP_MAGIC,
                      _REP_MAGIC, mtype, msize)

    nwritten = 0
    while True:
        # copy as much as possible to the report buffer
        nwritten += utils.memcpy(report, repofs, mdata, nwritten)

        # write the report
        while True:
            await write
            n = iface.write(report)
            if n == len(report):
                break

        # if we have more data to write, use continuation reports for it
        if nwritten < msize:
            repofs = _REP_CONT_DATA
        else:
            break
def _serialize_ecdh(ecdh_info, v2=False):
    """
    Serializes ECDH according to the current format defined by the hard fork version
    or the signature format respectively.
    """
    if v2:
        # In HF10 the amount is serialized to 8B and mask is deterministic
        ecdh_info_bin = bytearray(8)
        ecdh_info_bin[:] = ecdh_info.amount[0:8]
        return ecdh_info_bin

    else:
        ecdh_info_bin = bytearray(64)
        utils.memcpy(ecdh_info_bin, 0, ecdh_info.mask, 0, 32)
        utils.memcpy(ecdh_info_bin, 32, ecdh_info.amount, 0, 32)
        return ecdh_info_bin
Example #10
0
    async def areadinto(self, buf):
        '''
        Read exactly `len(buf)` bytes into `buf`, waiting for additional
        reports, if needed.  Raises `EOFError` if end-of-message is encountered
        before the full read can be completed.
        '''
        if self.size < len(buf):
            raise EOFError

        read = loop.select(self.iface.iface_num() | io.POLL_READ)
        nread = 0
        while nread < len(buf):
            if self.ofs == len(self.data):
                # we are at the end of received data
                # wait for continuation report
                while True:
                    report = await read
                    marker = report[0]
                    if marker == _REP_MARKER:
                        break
                self.data = report[_REP_CONT_DATA:_REP_CONT_DATA + self.size]
                self.ofs = 0

            # copy as much as possible to target buffer
            nbytes = utils.memcpy(buf, nread, self.data, self.ofs, len(buf))
            nread += nbytes
            self.ofs += nbytes
            self.size -= nbytes

        return nread
def _dump_rsig_lr(buff: bytearray, offset: int,
                  rsig: Bulletproof | BulletproofPlus) -> int:
    buff[offset] = len(rsig.L)
    offset += 1

    for x in rsig.L:
        utils.memcpy(buff, offset, x, 0, 32)
        offset += 32

    buff[offset] = len(rsig.R)
    offset += 1

    for x in rsig.R:
        utils.memcpy(buff, offset, x, 0, 32)
        offset += 32
    return offset
Example #12
0
    async def awrite(self, buf):
        '''
        Encode and write every byte from `buf`.  Does not need to be called in
        case message has zero length.  Raises `EOFError` if the length of `buf`
        exceeds the remaining message length.
        '''
        if self.size < len(buf):
            raise EOFError

        write = loop.select(self.iface.iface_num() | io.POLL_WRITE)
        nwritten = 0
        while nwritten < len(buf):
            # copy as much as possible to report buffer
            nbytes = utils.memcpy(self.data, self.ofs, buf, nwritten, len(buf))
            nwritten += nbytes
            self.ofs += nbytes
            self.size -= nbytes

            if self.ofs == _REP_LEN:
                # we are at the end of the report, flush it
                await write
                self.iface.write(self.data)
                self.ofs = _REP_CONT_DATA

        return nwritten
Example #13
0
    async def read_into(self, dst):
        '''
        Read exactly `len(dst)` bytes into writable buffer-like `dst`.

        Raises `EOFError` if the internal limit was reached or the
        backing IO strategy signalled an EOF.
        '''
        n = len(dst)

        if self._limit is not None:
            if self._limit < n:
                raise EOFError()
            self._limit -= n

        buf = self._buffer
        ofs = self._ofs
        i = 0
        while i < n:
            if ofs >= len(buf):
                buf = yield
                ofs = 0
            # memcpy caps on the buffer lengths, no need for exact byte count
            nb = memcpy(dst, i, buf, ofs, n)
            ofs += nb
            i += nb
        self._buffer = buf
        self._ofs = ofs
Example #14
0
async def read_message(iface: WireInterface,
                       buffer: utils.BufferType) -> Message:
    read = loop.wait(iface.iface_num() | io.POLL_READ)

    # wait for initial report
    report = await read
    if report[0] != _REP_MARKER:
        raise CodecError("Invalid magic")
    _, magic1, magic2, mtype, msize = ustruct.unpack(_REP_INIT, report)
    if magic1 != _REP_MAGIC or magic2 != _REP_MAGIC:
        raise CodecError("Invalid magic")

    read_and_throw_away = False

    with buffer_lock:
        if msize > len(buffer):
            # allocate a new buffer to fit the message
            try:
                mdata: utils.BufferType = bytearray(msize)
            except MemoryError:
                mdata = bytearray(_REP_LEN)
                read_and_throw_away = True
        else:
            # reuse a part of the supplied buffer
            mdata = memoryview(buffer)[:msize]

        # buffer the initial data
        nread = utils.memcpy(mdata, 0, report, _REP_INIT_DATA)

        while nread < msize:
            # wait for continuation report
            report = await read
            if report[0] != _REP_MARKER:
                raise CodecError("Invalid magic")

            # buffer the continuation data
            if read_and_throw_away:
                nread += len(report) - 1
            else:
                nread += utils.memcpy(mdata, nread, report, _REP_CONT_DATA)

    if read_and_throw_away:
        raise CodecError("Message too large")

    return Message(mtype, mdata)
def _set_tx_extra(state: State) -> bytes:
    """
    Sets tx public keys into transaction's extra.
    Extra field is supposed to be sorted (by sort_tx_extra() in the Monero)
    Tag ordering: TX_EXTRA_TAG_PUBKEY, TX_EXTRA_TAG_ADDITIONAL_PUBKEYS, TX_EXTRA_NONCE
    """
    from apps.monero.xmr.serialize import int_serialize

    # Extra buffer length computation
    # TX_EXTRA_TAG_PUBKEY (1B) | tx_pub_key (32B)
    extra_size = 33
    offset = 0
    num_keys = 0
    len_size = 0

    if state.need_additional_txkeys:
        num_keys = len(state.additional_tx_public_keys)
        len_size = int_serialize.uvarint_size(num_keys)

        # TX_EXTRA_TAG_ADDITIONAL_PUBKEYS (1B) | varint | keys
        extra_size += 1 + len_size + 32 * num_keys

    if state.extra_nonce:
        extra_size += len(state.extra_nonce)

    extra = bytearray(extra_size)
    extra[0] = 1  # TX_EXTRA_TAG_PUBKEY
    crypto.encodepoint_into(memoryview(extra)[1:], state.tx_pub)
    offset += 33

    if state.need_additional_txkeys:
        extra[offset] = 0x4  # TX_EXTRA_TAG_ADDITIONAL_PUBKEYS
        int_serialize.dump_uvarint_b_into(num_keys, extra, offset + 1)
        offset += 1 + len_size

        for idx in range(num_keys):
            extra[offset:offset + 32] = state.additional_tx_public_keys[idx]
            offset += 32

    if state.extra_nonce:
        utils.memcpy(extra, offset, state.extra_nonce, 0,
                     len(state.extra_nonce))
        state.extra_nonce = None

    return extra
Example #16
0
def cmd_init(req: Cmd) -> Cmd:
    if req.cid == 0:
        return cmd_error(req.cid, _ERR_INVALID_CID)
    elif req.cid == _CID_BROADCAST:
        # uint32_t except 0 and 0xffffffff
        resp_cid = random.uniform(0xFFFFFFFE) + 1
    else:
        resp_cid = req.cid

    buf, resp = make_struct(resp_cmd_init())
    utils.memcpy(resp.nonce, 0, req.data, 0, len(req.data))
    resp.cid = resp_cid
    resp.versionInterface = _U2FHID_IF_VERSION
    resp.versionMajor = 2
    resp.versionMinor = 0
    resp.versionBuild = 0
    resp.capFlags = _CAPFLAG_WINK

    return Cmd(req.cid, req.cmd, buf)
Example #17
0
async def all_inputs_set(state: State):
    state.mem_trace(0)

    await confirms.transaction_step(state.ctx, state.STEP_ALL_IN)

    from trezor.messages.MoneroTransactionAllInputsSetAck import (
        MoneroTransactionAllInputsSetAck, )
    from trezor.messages.MoneroTransactionRsigData import MoneroTransactionRsigData

    # Generate random commitment masks to be used in range proofs.
    # If SimpleRCT is used the sum of the masks must match the input masks sum.
    state.sumout = crypto.sc_init(0)
    for i in range(state.output_count):
        cur_mask = crypto.new_scalar()  # new mask for each output
        is_last = i + 1 == state.output_count
        if is_last and state.rct_type == RctType.Simple:
            # in SimpleRCT the last mask needs to be calculated as an offset of the sum
            crypto.sc_sub_into(cur_mask, state.sumpouts_alphas, state.sumout)
        else:
            crypto.random_scalar(cur_mask)

        crypto.sc_add_into(state.sumout, state.sumout, cur_mask)
        state.output_masks.append(cur_mask)

    if state.rct_type == RctType.Simple:
        utils.ensure(crypto.sc_eq(state.sumout, state.sumpouts_alphas),
                     "Invalid masks sum")  # sum check
    state.sumout = crypto.sc_init(0)

    rsig_data = MoneroTransactionRsigData()
    resp = MoneroTransactionAllInputsSetAck(rsig_data=rsig_data)

    # If range proofs are being offloaded, we send the masks to the host, which uses them
    # to create the range proof. If not, we do not send any and we use them in the following step.
    if state.rsig_offload:
        tmp_buff = bytearray(32)
        rsig_data.mask = bytearray(32 * state.output_count)
        for i in range(state.output_count):
            crypto.encodeint_into(tmp_buff, state.output_masks[i])
            utils.memcpy(rsig_data.mask, 32 * i, tmp_buff, 0, 32)

    return resp
Example #18
0
def _build_key(secret, discriminator=None, index: int = None) -> bytes:
    """
    Creates an unique-purpose key
    """
    key_buff = bytearray(32 + 12 + 4)  # key + disc + index
    offset = 32
    utils.memcpy(key_buff, 0, secret, 0, len(secret))

    if discriminator is not None:
        utils.memcpy(key_buff, offset, discriminator, 0, len(discriminator))
        offset += len(discriminator)

    if index is not None:
        # dump_uvarint_b_into, saving import
        shifted = True
        while shifted:
            shifted = index >> 7
            key_buff[offset] = (index & 0x7F) | (0x80 if shifted else 0x00)
            offset += 1
            index = shifted

    return crypto.keccak_2hash(key_buff)
Example #19
0
def _get_ecdh_info_and_out_pk(state: State, tx_out_key, amount, mask,
                              amount_key):
    """
    Calculates the Pedersen commitment C = aG + bH and returns it as CtKey.
    Also encodes the two items - `mask` and `amount` - into ecdh info,
    so the recipient is able to reconstruct the commitment.
    """
    out_pk_dest = crypto.encodepoint(tx_out_key)
    out_pk_commitment = crypto.encodepoint(crypto.gen_commitment(mask, amount))

    state.sumout = crypto.sc_add(state.sumout, mask)
    state.output_sk_masks.append(mask)

    # masking of mask and amount
    ecdh_info = _ecdh_encode(mask, amount, crypto.encodeint(amount_key))

    # Manual ECDH info serialization
    ecdh_info_bin = bytearray(64)
    utils.memcpy(ecdh_info_bin, 0, ecdh_info.mask, 0, 32)
    utils.memcpy(ecdh_info_bin, 32, ecdh_info.amount, 0, 32)
    gc.collect()

    return out_pk_dest, out_pk_commitment, ecdh_info_bin
def _dump_rsig_bp_plus(rsig: BulletproofPlus) -> bytes:
    if len(rsig.L) > 127:
        raise ValueError("Too large")

    # Manual serialization as the generic purpose serialize.dump_msg_gc
    # is more memory intensive which is not desired in the range proof section.

    # BP: "V", "A", "A1", "B", "r1", "s1", "d1", "V", "L", "R"
    # Commitment vector V is not serialized
    # Vector size under 127 thus varint occupies 1 B
    buff_size = 32 * (6 + 2 * (len(rsig.L))) + 2
    buff = bytearray(buff_size)

    utils.memcpy(buff, 0, rsig.A, 0, 32)
    utils.memcpy(buff, 32, rsig.A1, 0, 32)
    utils.memcpy(buff, 32 * 2, rsig.B, 0, 32)
    utils.memcpy(buff, 32 * 3, rsig.r1, 0, 32)
    utils.memcpy(buff, 32 * 4, rsig.s1, 0, 32)
    utils.memcpy(buff, 32 * 5, rsig.d1, 0, 32)

    _dump_rsig_lr(buff, 32 * 6, rsig)
    return buff
def _dump_rsig_bp(rsig: Bulletproof) -> bytes:
    if len(rsig.L) > 127:
        raise ValueError("Too large")

    # Manual serialization as the generic purpose serialize.dump_msg_gc
    # is more memory intensive which is not desired in the range proof section.

    # BP: V, A, S, T1, T2, taux, mu, L, R, a, b, t
    # Commitment vector V is not serialized
    # Vector size under 127 thus varint occupies 1 B
    buff_size = 32 * (9 + 2 * (len(rsig.L))) + 2
    buff = bytearray(buff_size)

    utils.memcpy(buff, 0, rsig.A, 0, 32)
    utils.memcpy(buff, 32, rsig.S, 0, 32)
    utils.memcpy(buff, 32 * 2, rsig.T1, 0, 32)
    utils.memcpy(buff, 32 * 3, rsig.T2, 0, 32)
    utils.memcpy(buff, 32 * 4, rsig.taux, 0, 32)
    utils.memcpy(buff, 32 * 5, rsig.mu, 0, 32)

    offset = _dump_rsig_lr(buff, 32 * 6, rsig)

    utils.memcpy(buff, offset, rsig.a, 0, 32)
    offset += 32
    utils.memcpy(buff, offset, rsig.b, 0, 32)
    offset += 32
    utils.memcpy(buff, offset, rsig.t, 0, 32)
    return buff
async def set_output(
    state: State,
    dst_entr: MoneroTransactionDestinationEntry,
    dst_entr_hmac: bytes,
    rsig_data: MoneroTransactionRsigData,
    is_offloaded_bp=False,
) -> MoneroTransactionSetOutputAck:
    state.mem_trace(0, True)
    mods = utils.unimport_begin()

    # Progress update only for master message (skip for offloaded BP msg)
    if not is_offloaded_bp:
        await layout.transaction_step(state, state.STEP_OUT,
                                      state.current_output_index + 1)

    state.mem_trace(1, True)

    dst_entr = _validate(state, dst_entr, dst_entr_hmac, is_offloaded_bp)
    state.mem_trace(2, True)

    if not state.is_processing_offloaded:
        # First output - we include the size of the container into the tx prefix hasher
        if state.current_output_index == 0:
            state.tx_prefix_hasher.uvarint(state.output_count)

        state.mem_trace(4, True)
        state.output_amounts.append(dst_entr.amount)
        state.summary_outs_money += dst_entr.amount

    utils.unimport_end(mods)
    state.mem_trace(5, True)

    # Compute tx keys and masks if applicable
    tx_out_key, amount_key, derivation = _compute_tx_keys(state, dst_entr)
    utils.unimport_end(mods)
    state.mem_trace(6, True)

    # Range proof first, memory intensive (fragmentation)
    rsig_data_new, mask = _range_proof(state, rsig_data)
    utils.unimport_end(mods)
    state.mem_trace(7, True)

    # If det masks & offloading, return as we are handling offloaded BP.
    if state.is_processing_offloaded:
        from trezor.messages import MoneroTransactionSetOutputAck

        return MoneroTransactionSetOutputAck()

    # Tx header prefix hashing, hmac dst_entr
    tx_out_bin, hmac_vouti = _set_out_tx_out(
        state,
        dst_entr,
        tx_out_key,
        derivation,
    )
    del (derivation, )
    state.mem_trace(11, True)

    out_pk_dest, out_pk_commitment, ecdh_info_bin = _get_ecdh_info_and_out_pk(
        state=state,
        tx_out_key=tx_out_key,
        amount=dst_entr.amount,
        mask=mask,
        amount_key=amount_key,
    )
    del (dst_entr, mask, amount_key, tx_out_key)
    state.mem_trace(12, True)

    # Incremental hashing of the ECDH info.
    # RctSigBase allows to hash only one of the (ecdh, out_pk) as they are serialized
    # as whole vectors. We choose to hash ECDH first, because it saves state space.
    state.full_message_hasher.set_ecdh(ecdh_info_bin)
    state.mem_trace(13, True)

    # output_pk_commitment is stored to the state as it is used during the signature and hashed to the
    # RctSigBase later. No need to store amount, it was already stored.
    state.output_pk_commitments.append(out_pk_commitment)
    state.last_step = state.STEP_OUT
    state.mem_trace(14, True)

    from trezor.messages import MoneroTransactionSetOutputAck

    out_pk_bin = bytearray(64)
    utils.memcpy(out_pk_bin, 0, out_pk_dest, 0, 32)
    utils.memcpy(out_pk_bin, 32, out_pk_commitment, 0, 32)

    return MoneroTransactionSetOutputAck(
        tx_out=tx_out_bin,
        vouti_hmac=hmac_vouti,
        rsig_data=rsig_data_new,
        out_pk=out_pk_bin,
        ecdh_info=ecdh_info_bin,
    )
Example #23
0
def generate_ring_signature(
    prefix_hash: bytes,
    image: Ge25519,
    pubs: list[Ge25519],
    sec: Sc25519,
    sec_idx: int,
    test: bool = False,
) -> Sig:
    """
    Generates ring signature with key image.
    void crypto_ops::generate_ring_signature()
    """
    from trezor.utils import memcpy

    if test:
        t = crypto.scalarmult_base(sec)
        if not crypto.point_eq(t, pubs[sec_idx]):
            raise ValueError("Invalid sec key")

        k_i = monero.generate_key_image(crypto.encodepoint(pubs[sec_idx]), sec)
        if not crypto.point_eq(k_i, image):
            raise ValueError("Key image invalid")
        for k in pubs:
            crypto.check_ed25519point(k)

    buff_off = len(prefix_hash)
    buff = bytearray(buff_off + 2 * 32 * len(pubs))
    memcpy(buff, 0, prefix_hash, 0, buff_off)
    mvbuff = memoryview(buff)

    sum = crypto.sc_0()
    k = crypto.sc_0()
    sig = []

    for _ in range(len(pubs)):
        sig.append([crypto.sc_0(), crypto.sc_0()])  # c, r

    for i in range(len(pubs)):
        if i == sec_idx:
            k = crypto.random_scalar()
            tmp3 = crypto.scalarmult_base(k)
            crypto.encodepoint_into(mvbuff[buff_off:buff_off + 32], tmp3)
            buff_off += 32

            tmp3 = crypto.hash_to_point(crypto.encodepoint(pubs[i]))
            tmp2 = crypto.scalarmult(tmp3, k)
            crypto.encodepoint_into(mvbuff[buff_off:buff_off + 32], tmp2)
            buff_off += 32

        else:
            sig[i] = [crypto.random_scalar(), crypto.random_scalar()]
            tmp3 = pubs[i]
            tmp2 = crypto.ge25519_double_scalarmult_base_vartime(
                sig[i][0], tmp3, sig[i][1])
            crypto.encodepoint_into(mvbuff[buff_off:buff_off + 32], tmp2)
            buff_off += 32

            tmp3 = crypto.hash_to_point(crypto.encodepoint(tmp3))
            tmp2 = crypto.ge25519_double_scalarmult_vartime2(
                sig[i][1], tmp3, sig[i][0], image)
            crypto.encodepoint_into(mvbuff[buff_off:buff_off + 32], tmp2)
            buff_off += 32

            sum = crypto.sc_add(sum, sig[i][0])

    h = crypto.hash_to_scalar(buff)
    sig[sec_idx][0] = crypto.sc_sub(h, sum)
    sig[sec_idx][1] = crypto.sc_mulsub(sig[sec_idx][0], sec, k)
    return sig
Example #24
0
async def set_output(state: State, dst_entr, dst_entr_hmac, rsig_data):
    state.mem_trace(0, True)
    mods = utils.unimport_begin()

    await confirms.transaction_step(state.ctx, state.STEP_OUT,
                                    state.current_output_index + 1,
                                    state.output_count)
    state.mem_trace(1)

    state.current_output_index += 1
    state.mem_trace(2, True)
    await _validate(state, dst_entr, dst_entr_hmac)

    # First output - we include the size of the container into the tx prefix hasher
    if state.current_output_index == 0:
        state.tx_prefix_hasher.uvarint(state.output_count)
    state.mem_trace(4, True)

    state.output_amounts.append(dst_entr.amount)
    state.summary_outs_money += dst_entr.amount
    utils.unimport_end(mods)
    state.mem_trace(5, True)

    # Range proof first, memory intensive
    rsig, mask = _range_proof(state, dst_entr.amount, rsig_data)
    utils.unimport_end(mods)
    state.mem_trace(6, True)

    # additional tx key if applicable
    additional_txkey_priv = _set_out_additional_keys(state, dst_entr)
    # derivation = a*R or r*A or s*C
    derivation = _set_out_derivation(state, dst_entr, additional_txkey_priv)
    # amount key = H_s(derivation || i)
    amount_key = crypto.derivation_to_scalar(derivation,
                                             state.current_output_index)
    # one-time destination address P = H_s(derivation || i)*G + B
    tx_out_key = crypto.derive_public_key(
        derivation,
        state.current_output_index,
        crypto.decodepoint(dst_entr.addr.spend_public_key),
    )
    del (derivation, additional_txkey_priv)
    state.mem_trace(7, True)

    # Tx header prefix hashing, hmac dst_entr
    tx_out_bin, hmac_vouti = await _set_out_tx_out(state, dst_entr, tx_out_key)
    state.mem_trace(11, True)

    out_pk_dest, out_pk_commitment, ecdh_info_bin = _get_ecdh_info_and_out_pk(
        state=state,
        tx_out_key=tx_out_key,
        amount=dst_entr.amount,
        mask=mask,
        amount_key=amount_key,
    )
    del (dst_entr, mask, amount_key, tx_out_key)
    state.mem_trace(12, True)

    # Incremental hashing of the ECDH info.
    # RctSigBase allows to hash only one of the (ecdh, out_pk) as they are serialized
    # as whole vectors. We choose to hash ECDH first, because it saves state space.
    state.full_message_hasher.set_ecdh(ecdh_info_bin)
    state.mem_trace(13, True)

    # output_pk_commitment is stored to the state as it is used during the signature and hashed to the
    # RctSigBase later. No need to store amount, it was already stored.
    state.output_pk_commitments.append(out_pk_commitment)
    state.mem_trace(14, True)

    from trezor.messages.MoneroTransactionSetOutputAck import (
        MoneroTransactionSetOutputAck, )

    out_pk_bin = bytearray(64)
    utils.memcpy(out_pk_bin, 0, out_pk_dest, 0, 32)
    utils.memcpy(out_pk_bin, 32, out_pk_commitment, 0, 32)

    return MoneroTransactionSetOutputAck(
        tx_out=tx_out_bin,
        vouti_hmac=hmac_vouti,
        rsig_data=_return_rsig_data(rsig),
        out_pk=out_pk_bin,
        ecdh_info=ecdh_info_bin,
    )