Exemplo n.º 1
0
def generate_mlsag_full(message, pubs, in_sk, out_sk_mask, out_pk_commitments,
                        kLRki, index, txn_fee_key):
    cols = len(pubs)
    if cols == 0:
        raise ValueError("Empty pubs")
    rows = len(pubs[0])
    if rows == 0:
        raise ValueError("Empty pub row")
    for i in range(cols):
        if len(pubs[i]) != rows:
            raise ValueError("pub is not rectangular")

    if len(in_sk) != rows:
        raise ValueError("Bad inSk size")
    if len(out_sk_mask) != len(out_pk_commitments):
        raise ValueError("Bad outsk/putpk size")

    sk = _key_vector(rows + 1)
    M = _key_matrix(rows + 1, cols)
    for i in range(rows + 1):
        sk[i] = crypto.sc_0()

    for i in range(cols):
        M[i][rows] = crypto.identity()
        for j in range(rows):
            M[i][j] = crypto.decodepoint(pubs[i][j].dest)
            M[i][rows] = crypto.point_add(
                M[i][rows], crypto.decodepoint(pubs[i][j].commitment))

    sk[rows] = crypto.sc_0()
    for j in range(rows):
        sk[j] = in_sk[j].dest
        sk[rows] = crypto.sc_add(sk[rows],
                                 in_sk[j].mask)  # add masks in last row

    for i in range(cols):
        for j in range(len(out_pk_commitments)):
            M[i][rows] = crypto.point_sub(
                M[i][rows], crypto.decodepoint(
                    out_pk_commitments[j]))  # subtract output Ci's in last row

        # Subtract txn fee output in last row
        M[i][rows] = crypto.point_sub(M[i][rows], txn_fee_key)

    for j in range(len(out_pk_commitments)):
        sk[rows] = crypto.sc_sub(
            sk[rows], out_sk_mask[j])  # subtract output masks in last row

    return generate_mlsag(message, M, sk, kLRki, index, rows)
Exemplo n.º 2
0
def key_zero_vector(rows):
    """
    Empty key vector
    :param rows:
    :return:
    """
    vct = [None] * rows
    for i in range(rows):
        vct[i] = crypto.sc_0()
    return vct
Exemplo n.º 3
0
def check_ring_singature(prefix_hash, image, pubs, sig):
    """
    Checks ring signature generated with generate_ring_signature
    :param prefix_hash:
    :param image:
    :param pubs:
    :param sig:
    :return:
    """
    from apps.monero.xmr.common import memcpy

    image_unp = crypto.ge_frombytes_vartime(image)
    image_pre = crypto.ge_dsm_precomp(image_unp)

    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()
    for i in range(len(pubs)):
        if crypto.sc_check(sig[i][0]) != 0 or crypto.sc_check(sig[i][1]) != 0:
            return False

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

        tmp3 = crypto.hash_to_ec(crypto.encodepoint(pubs[i]))
        tmp2 = crypto.ge_double_scalarmult_precomp_vartime(
            sig[i][1], tmp3, sig[i][0], image_pre)
        crypto.encodepoint_into(tmp2, mvbuff[buff_off:buff_off + 32])
        buff_off += 32

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

    h = crypto.hash_to_scalar(buff)
    h = crypto.sc_sub(h, sum)
    return crypto.sc_isnonzero(h) == 0
Exemplo n.º 4
0
    def __init__(self, ctx):
        from apps.monero.xmr.keccak_hasher import KeccakXmrArchive
        from apps.monero.xmr.mlsag_hasher import PreMlsagHasher

        self.ctx = ctx
        """
        Account credentials
        type: AccountCreds
        - view private/public key
        - spend private/public key
        - and its corresponding address
        """
        self.creds: AccountCreds | None = None

        # HMAC/encryption keys used to protect offloaded data
        self.key_hmac: bytes | None = None
        self.key_enc: bytes | None = None
        """
        Transaction keys
        - also denoted as r/R
        - tx_priv is a random number
        - tx_pub is equal to `r*G` or `r*D` for subaddresses
        - for subaddresses the `r` is commonly denoted as `s`, however it is still just a random number
        - the keys are used to derive the one time address and its keys (P = H(A*r)*G + B)
        """
        self.tx_priv: Sc25519 = None
        self.tx_pub: Ge25519 = None
        """
        In some cases when subaddresses are used we need more tx_keys
        (explained in step 1).
        """
        self.need_additional_txkeys = False

        # Connected client version
        self.client_version = 0
        self.hard_fork = 12

        self.input_count = 0
        self.output_count = 0
        self.progress_total = 0
        self.progress_cur = 0

        self.output_change = None
        self.fee = 0
        self.tx_type = 0

        # wallet sub-address major index
        self.account_idx = 0

        # contains additional tx keys if need_additional_tx_keys is True
        self.additional_tx_private_keys: list[Sc25519] = []
        self.additional_tx_public_keys: list[bytes] = []

        # currently processed input/output index
        self.current_input_index = -1
        self.current_output_index = -1
        self.is_processing_offloaded = False

        # for pseudo_out recomputation from new mask
        self.input_last_amount = 0

        self.summary_inputs_money = 0
        self.summary_outs_money = 0

        # output commitments
        self.output_pk_commitments: list[bytes] = []

        self.output_amounts: list[int] = []
        # output *range proof* masks. HP10+ makes them deterministic.
        self.output_masks: list[Sc25519] = []

        # the range proofs are calculated in batches, this denotes the grouping
        self.rsig_grouping: list[int] = []
        # is range proof computing offloaded or not
        self.rsig_offload = False

        # sum of all inputs' pseudo out masks
        self.sumpouts_alphas: Sc25519 = crypto.sc_0()
        # sum of all output' pseudo out masks
        self.sumout: Sc25519 = crypto.sc_0()

        self.subaddresses: Subaddresses = {}

        # TX_EXTRA_NONCE extra field for tx.extra, due to sort_tx_extra()
        self.extra_nonce = None

        # contains an array where each item denotes the input's position
        # (inputs are sorted by key images)
        self.source_permutation: list[int] = []

        # Last key image seen. Used for input permutation correctness check
        self.last_ki: bytes | None = None

        # Encryption key to release to host after protocol ends without error
        self.opening_key: bytes | None = None

        # Step transition automaton
        self.last_step = self.STEP_INIT
        """
        Tx prefix hasher/hash. We use the hasher to incrementally hash and then
        store the final hash in tx_prefix_hash.
        See Monero-Trezor documentation section 3.3 for more details.
        """
        self.tx_prefix_hasher = KeccakXmrArchive()
        self.tx_prefix_hash: bytes | None = None
        """
        Full message hasher/hash that is to be signed using MLSAG.
        Contains tx_prefix_hash.
        See Monero-Trezor documentation section 3.3 for more details.
        """
        self.full_message_hasher = PreMlsagHasher()
        self.full_message: bytes | None = None
Exemplo n.º 5
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
Exemplo n.º 6
0
    def __init__(self, ctx):
        from apps.monero.xmr.keccak_hasher import KeccakXmrArchive
        from apps.monero.xmr.mlsag_hasher import PreMlsagHasher

        self.ctx = ctx
        """
        Account credentials
        type: AccountCreds
        - view private/public key
        - spend private/public key
        - and its corresponding address
        """
        self.creds = None

        # HMAC/encryption keys used to protect offloaded data
        self.key_hmac = None
        self.key_enc = None
        """
        Transaction keys
        - also denoted as r/R
        - tx_priv is a random number
        - tx_pub is equal to `r*G` or `r*D` for subaddresses
        - for subaddresses the `r` is commonly denoted as `s`, however it is still just a random number
        - the keys are used to derive the one time address and its keys (P = H(A*r)*G + B)
        """
        self.tx_priv = None
        self.tx_pub = None
        """
        In some cases when subaddresses are used we need more tx_keys
        (explained in step 1).
        """
        self.need_additional_txkeys = False

        # Ring Confidential Transaction type
        # allowed values: RctType.{Full, Simple}
        self.rct_type = None
        # Range Signature type (also called range proof)
        # allowed values: RsigType.{Borromean, Bulletproof}
        self.rsig_type = None

        self.input_count = 0
        self.output_count = 0
        self.output_change = None
        self.fee = 0

        # wallet sub-address major index
        self.account_idx = 0

        # contains additional tx keys if need_additional_tx_keys is True
        self.additional_tx_private_keys = []
        self.additional_tx_public_keys = []

        # currently processed input/output index
        self.current_input_index = -1
        self.current_output_index = -1

        self.summary_inputs_money = 0
        self.summary_outs_money = 0

        # output commitments
        self.output_pk_commitments = []
        # masks used in the output commitment
        self.output_sk_masks = []

        self.output_amounts = []
        # output *range proof* masks
        self.output_masks = []

        # the range proofs are calculated in batches, this denotes the grouping
        self.rsig_grouping = []
        # is range proof computing offloaded or not
        self.rsig_offload = False

        # sum of all inputs' pseudo out masks
        self.sumpouts_alphas = crypto.sc_0()
        # sum of all output' pseudo out masks
        self.sumout = crypto.sc_0()

        self.subaddresses = {}

        # simple stub containing items hashed into tx prefix
        self.tx = TprefixStub(vin=[], vout=[], extra=b"")
        # TX_EXTRA_NONCE extra field for tx.extra, due to sort_tx_extra()
        self.extra_nonce = None

        # contains an array where each item denotes the input's position
        # (inputs are sorted by key images)
        self.source_permutation = []
        """
        Tx prefix hasher/hash. We use the hasher to incrementally hash and then
        store the final hash in tx_prefix_hash.
        See Monero-Trezor documentation section 3.3 for more details.
        """
        self.tx_prefix_hasher = KeccakXmrArchive()
        self.tx_prefix_hash = None
        """
        Full message hasher/hash that is to be signed using MLSAG.
        Contains tx_prefix_hash.
        See Monero-Trezor documentation section 3.3 for more details.
        """
        self.full_message_hasher = PreMlsagHasher()
        self.full_message = None
Exemplo n.º 7
0
def prove_range_mem(amount, last_mask=None):
    """
    Memory optimized range proof.

    Gives C, and mask such that \sumCi = C
    c.f. http:#eprint.iacr.org/2015/1098 section 5.1

    Ci is a commitment to either 0 or 2^i, i=0,...,63
    thus this proves that "amount" is in [0, 2^ATOMS]
    mask is a such that C = aG + bH, and b = amount
    :param amount:
    :param last_mask: ai[ATOMS-1] will be computed as \sum_{i=0}^{ATOMS-2} a_i - last_mask
    :param use_asnl: use ASNL, used before Borromean
    :return: sumCi, mask, RangeSig.
        sumCi is Pedersen commitment on the amount value. sumCi = aG + amount*H
        mask is "a" from the Pedersent commitment above.
    """
    res = bytearray(32 * (64 + 64 + 64 + 1))
    mv = memoryview(res)
    gc.collect()

    def as0(mv, x, i):
        crypto.encodeint_into(x, mv[32 * i:])

    def as1(mv, x, i):
        crypto.encodeint_into(x, mv[32 * 64 + 32 * i:])

    def aci(mv, x, i):
        crypto.encodepoint_into(x, mv[32 * 64 * 2 + 32 + 32 * i:])

    n = 64
    bb = d2b(amount, n)  # gives binary form of bb in "digits" binary digits
    ai = key_zero_vector(n)
    a = crypto.sc_0()

    C = crypto.identity()
    alpha = key_zero_vector(n)
    c_H = crypto.gen_H()
    kck = crypto.get_keccak()  # ee computation

    # First pass, generates: ai, alpha, Ci, ee, s1
    for ii in range(n):
        ai[ii] = crypto.random_scalar()
        if last_mask is not None and ii == 64 - 1:
            ai[ii] = crypto.sc_sub(last_mask, a)

        a = crypto.sc_add(
            a, ai[ii]
        )  # creating the total mask since you have to pass this to receiver...

        alpha[ii] = crypto.random_scalar()
        L = crypto.scalarmult_base(alpha[ii])

        if bb[ii] == 0:
            Ctmp = crypto.scalarmult_base(ai[ii])
        else:
            Ctmp = crypto.point_add(crypto.scalarmult_base(ai[ii]), c_H)
        C = crypto.point_add(C, Ctmp)
        aci(mv, Ctmp, ii)

        if bb[ii] == 0:
            si = crypto.random_scalar()
            c = crypto.hash_to_scalar(crypto.encodepoint(L))
            L = crypto.add_keys2(si, c, crypto.point_sub(Ctmp, c_H))
            kck.update(crypto.encodepoint(L))
            as1(mv, si, ii)

        else:
            kck.update(crypto.encodepoint(L))

        c_H = crypto.point_double(c_H)

    # Compute ee, memory cleanup
    ee = crypto.sc_reduce32(crypto.decodeint(kck.digest()))
    crypto.encodeint_into(ee, mv[64 * 32 * 2:])
    del kck
    gc.collect()

    # Second phase computes: s0, s1
    c_H = crypto.gen_H()

    for jj in range(n):
        if not bb[jj]:
            s0 = crypto.sc_mulsub(ai[jj], ee, alpha[jj])

        else:
            s0 = crypto.random_scalar()
            Ctmp = crypto.decodepoint(
                mv[32 * 64 * 2 + 32 + 32 * jj:32 * 64 * 2 + 32 + 32 * jj + 32])
            LL = crypto.add_keys2(s0, ee, Ctmp)
            cc = crypto.hash_to_scalar(crypto.encodepoint(LL))
            si = crypto.sc_mulsub(ai[jj], cc, alpha[jj])
            as1(mv, si, jj)
        as0(mv, s0, jj)

        c_H = crypto.point_double(c_H)

    gc.collect()
    return C, a, res
Exemplo n.º 8
0
def generate_ring_signature(prefix_hash,
                            image,
                            pubs,
                            sec,
                            sec_idx,
                            test=False):
    """
    Generates ring signature with key image.
    void crypto_ops::generate_ring_signature()

    :param prefix_hash:
    :param image:
    :param pubs:
    :param sec:
    :param sec_idx:
    :param test:
    :return:
    """
    from apps.monero.xmr.common import memcpy

    if test:
        from apps.monero.xmr import monero

        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.ge_frombytes_vartime_check(k)

    image_unp = crypto.ge_frombytes_vartime(image)
    image_pre = crypto.ge_dsm_precomp(image_unp)

    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 i 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(tmp3, mvbuff[buff_off:buff_off + 32])
            buff_off += 32

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

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

            tmp3 = crypto.hash_to_ec(crypto.encodepoint(tmp3))
            tmp2 = crypto.ge_double_scalarmult_precomp_vartime(
                sig[i][1], tmp3, sig[i][0], image_pre)
            crypto.encodepoint_into(tmp2, mvbuff[buff_off:buff_off + 32])
            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
Exemplo n.º 9
0
    def verify_clsag(self, msg, ss, sc1, sI, sD, pubs, C_offset):
        n = len(pubs)
        c = crypto.new_scalar()
        D_8 = crypto.new_point()
        tmp_bf = bytearray(32)
        C_offset_bf = crypto.encodepoint(C_offset)

        crypto.sc_copy(c, sc1)
        crypto.point_mul8_into(D_8, sD)

        hsh_P = crypto.get_keccak()  # domain, I, D, P, C, C_offset
        hsh_C = crypto.get_keccak()  # domain, I, D, P, C, C_offset
        hsh_P.update(mlsag._HASH_KEY_CLSAG_AGG_0)
        hsh_C.update(mlsag._HASH_KEY_CLSAG_AGG_1)

        def hsh_PC(x):
            hsh_P.update(x)
            hsh_C.update(x)

        for x in pubs:
            hsh_PC(x.dest)

        for x in pubs:
            hsh_PC(x.commitment)

        hsh_PC(crypto.encodepoint_into(tmp_bf, sI))
        hsh_PC(crypto.encodepoint_into(tmp_bf, sD))
        hsh_PC(C_offset_bf)
        mu_P = crypto.decodeint(hsh_P.digest())
        mu_C = crypto.decodeint(hsh_C.digest())

        c_to_hash = crypto.get_keccak(
        )  # domain, P, C, C_offset, message, L, R
        c_to_hash.update(mlsag._HASH_KEY_CLSAG_ROUND)
        for i in range(len(pubs)):
            c_to_hash.update(pubs[i].dest)
        for i in range(len(pubs)):
            c_to_hash.update(pubs[i].commitment)
        c_to_hash.update(C_offset_bf)
        c_to_hash.update(msg)

        c_p = crypto.new_scalar()
        c_c = crypto.new_scalar()
        L = crypto.new_point()
        R = crypto.new_point()
        tmp_pt = crypto.new_point()
        i = 0
        while i < n:
            crypto.sc_mul_into(c_p, mu_P, c)
            crypto.sc_mul_into(c_c, mu_C, c)

            C_P = crypto.point_sub(
                crypto.decodepoint_into(tmp_pt, pubs[i].commitment), C_offset)
            crypto.add_keys2_into(
                L, ss[i], c_p, crypto.decodepoint_into(tmp_pt, pubs[i].dest))
            crypto.point_add_into(L, L,
                                  crypto.scalarmult_into(tmp_pt, C_P, c_c))

            HP = crypto.hash_to_point(pubs[i].dest)
            crypto.add_keys3_into(R, ss[i], HP, c_p, sI)
            crypto.point_add_into(R, R,
                                  crypto.scalarmult_into(tmp_pt, D_8, c_c))

            chasher = c_to_hash.copy()
            chasher.update(crypto.encodepoint_into(tmp_bf, L))
            chasher.update(crypto.encodepoint_into(tmp_bf, R))
            crypto.decodeint_into(c, chasher.digest())
            i += 1
        res = crypto.sc_sub(c, sc1)
        if not crypto.sc_eq(res, crypto.sc_0()):
            raise ValueError("Signature error")