Esempio n. 1
0
    def unblind_int(self, v, k, AKout):
        AKout = crypto.hash_to_scalar(AKout)
        k = crypto.sc_sub(crypto.decodeint(k), AKout)

        AKout = crypto.hash_to_scalar(crypto.encodeint(AKout))
        v = crypto.sc_sub(crypto.decodeint(v), AKout)

        return v, k, AKout
Esempio n. 2
0
def check_ring_singature(prefix_hash, image, pubs, sig):
    """
    Checks ring signature generated with generate_ring_signature
    """
    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(mvbuff[buff_off:buff_off + 32], tmp2)
        buff_off += 32

        tmp3 = crypto.hash_to_point(crypto.encodepoint(pubs[i]))
        tmp2 = crypto.ge_double_scalarmult_precomp_vartime(
            sig[i][1], tmp3, sig[i][0], image_pre)
        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)
    h = crypto.sc_sub(h, sum)
    return crypto.sc_isnonzero(h) == 0
Esempio 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:
    """
    image_unp = crypto.ge_frombytes_vartime(image)
    image_pre = crypto.ge_dsm_precomp(image_unp)

    buff = b"" + prefix_hash
    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])
        buff += crypto.encodepoint(tmp2)
        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)
        buff += crypto.encodepoint(tmp2)
        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
Esempio n. 4
0
    def poc2(self):
        print('[+] PoC Ledger-app-Monero 1.4.2 spend key extraction, v2')
        self.reset()
        self.set_mode()
        self.open_tx()

        # 1. get A, find x, s.t.: [8*a*x*G]_pt == [8*a*x*G]_sc
        A = self.scalarmult(self.fake_a)
        Apt = crypto.decodepoint(A)
        x, A8x = self.find_confusion(Apt)
        Gx = crypto.encodepoint(crypto.scalarmult_base(crypto.sc_init(x)))

        print('  1. Confusion found, x: %d' % (x, ))
        print('     8xA:  %s' % binascii.hexlify(A8x).decode('ascii'))
        print('       A:  %s' % binascii.hexlify(A).decode('ascii'))
        print('      xG:  %s' % binascii.hexlify(Gx).decode('ascii'))

        # 2. gen_deriv (8*a*x*G) = enc(8x*A) = enc(P); we know {P, enc(P)};
        # It holds that P=8xA is also a valid scalar value, from the step above.
        P = self.gen_derivation(Gx, self.fake_a)
        print('  2.   P:  %s' % (self.fmtkey(P).decode('ascii'), ))

        # 3. get_secret_key: s1 = Hs(P||0) + s
        sp = self.derive_secret_key(P, 0, self.fake_b)
        print('  3.   sp: %s' % (self.fmtkey(sp).decode('ascii'), ))

        # 4. mlsag_hash(p2=1, opt=0x80) = c
        c = self.mlsag_hash()
        print('  4.   c:  %s' % (binascii.hexlify(c).decode('ascii'), ))

        # 5. mlsag_sign(s1, enc(P)), r1 = enc(s1 - Pc) = enc(Hs(P||0) + s - Pc);
        # We have R = Hs(P||0) + s - Pc -> R - Hs(P||0) + Pc = s
        r = self.mlsag_sign_s(P, sp)
        print('  5.   r:  %s' % (binascii.hexlify(r[0]).decode('ascii'), ))

        # Extract the spend key
        hs0 = crypto.hash_to_scalar(bytearray(A8x) + bytearray(1))
        rsc = crypto.decodeint(r[0])
        rsc = crypto.sc_sub(rsc, hs0)
        bsc = crypto.sc_add(
            rsc, crypto.sc_mul(crypto.decodeint(c), crypto.decodeint(A8x)))
        b = crypto.encodeint(bsc)
        print('  5.   b:  %s' % binascii.hexlify(b).decode('ascii'))

        B = crypto.scalarmult_base(bsc)
        print('  5.   B:  %s' %
              binascii.hexlify(crypto.encodepoint(B)).decode('ascii'))

        # 6. Verify
        BB = self.scalarmult(self.fake_b)
        print('  6.   bG: %s\n' % binascii.hexlify(BB).decode('ascii'))

        if BB == crypto.encodepoint(B):
            print('[+] PoC successful')
        else:
            print('[-] PoC not working')

        print('\nCommands: ')
        for x in self.commands:
            print('  %s' % x)
Esempio n. 5
0
    def extra_poc(self, zero, hs0, B):
        #  --- Extra ---
        # Extract view key and address reconstruction.
        # Not needed for the PoC
        print('\nExtracting view-key...')
        encr = self.derive_secret_key(zero, 0, self.fake_a)
        r = self.mlsag_sign_s(zero, encr)
        rsc = crypto.decodeint(r[0])
        asc = crypto.sc_sub(rsc, hs0)
        a = crypto.encodeint(asc)
        print('  a:  %s' % binascii.hexlify(a).decode('ascii'))

        A = crypto.scalarmult_base(asc)
        print('  A:  %s' %
              binascii.hexlify(crypto.encodepoint(A)).decode('ascii'))

        AA = self.scalarmult(self.fake_a)
        print('  aG: %s' % binascii.hexlify(AA).decode('ascii'))

        main_addr = addr.encode_addr(
            xmr_net.net_version(xmr_net.NetworkTypes.MAINNET),
            crypto.encodepoint(B),
            crypto.encodepoint(A),
        )

        test_addr = addr.encode_addr(
            xmr_net.net_version(xmr_net.NetworkTypes.TESTNET),
            crypto.encodepoint(B),
            crypto.encodepoint(A),
        )

        print('Mainnet address: %s' % main_addr.decode('ascii'))
        print('Testnet address: %s' % test_addr.decode('ascii'))
Esempio n. 6
0
def ecdh_decode(masked, receiver_sk=None, derivation=None):
    """
    Elliptic Curve Diffie-Helman: encodes and decodes the amount b and mask a
    where C= aG + bH
    """
    rv = xmrtypes.EcdhTuple()

    if derivation is None:
        derivation = crypto.scalarmult(masked.senderPk, receiver_sk)

    sharedSec1 = crypto.hash_to_scalar(derivation)
    sharedSec2 = crypto.hash_to_scalar(crypto.encodeint(sharedSec1))

    rv.mask = crypto.sc_sub(masked.mask, sharedSec1)
    rv.amount = crypto.sc_sub(masked.amount, sharedSec2)
    return rv
Esempio n. 7
0
def prove_rct_mg_simple(message, pubs, in_sk, a, cout, kLRki, mscout, index):
    """
    Simple version for when we assume only
        post rct inputs
        here pubs is a vector of (P, C) length mixin

    :param message:
    :param pubs: vector of CtKeys, public, point values, encoded form. (dest, mask) = (P, C)
    :param in_sk: CtKey, private. (spending private key, input commitment mask (original))
    :param a: mask from the pseudo_output commitment (alpha)
    :param cout: point, decoded. Pseudo output public key.
    :param kLRki:
    :param mscout: lambda accepting c
    :param index:
    :return:
    """
    rows = 1
    cols = len(pubs)
    if cols == 0:
        raise ValueError("Empty pubs")
    if (not kLRki or not mscout) and (kLRki and mscout):
        raise ValueError("Only one of kLRki/mscout is present")

    sk = key_vector(rows + 1)
    M = key_matrix(rows + 1, cols)

    sk[0] = in_sk.dest
    sk[1] = crypto.sc_sub(in_sk.mask, a)

    for i in range(cols):
        M[i][0] = crypto.decodepoint(pubs[i].dest)
        M[i][1] = crypto.point_sub(crypto.decodepoint(pubs[i].commitment),
                                   cout)

    return gen_mlsag_ext(message, M, sk, kLRki, mscout, index, rows)
Esempio n. 8
0
    def gen_clsag_sig(self, ring_size=11, index=None):
        msg = bytearray(random.getrandbits(8) for _ in range(32))
        amnt = crypto.sc_init(random.randint(0, 0xFFFFFF) + 12)
        priv = crypto.random_scalar()
        msk = crypto.random_scalar()
        alpha = crypto.random_scalar()
        P = crypto.scalarmult_base(priv)
        C = crypto.add_keys2(msk, amnt, crypto.xmr_H())
        Cp = crypto.add_keys2(alpha, amnt, crypto.xmr_H())

        ring = []
        for i in range(ring_size - 1):
            tk = TmpKey(
                crypto.encodepoint(
                    crypto.scalarmult_base(crypto.random_scalar())),
                crypto.encodepoint(
                    crypto.scalarmult_base(crypto.random_scalar())),
            )
            ring.append(tk)

        index = index if index is not None else random.randint(0, len(ring))
        ring.insert(index, TmpKey(crypto.encodepoint(P),
                                  crypto.encodepoint(C)))
        ring2 = list(ring)
        mg_buffer = []

        self.assertTrue(
            crypto.point_eq(crypto.scalarmult_base(priv),
                            crypto.decodepoint(ring[index].dest)))
        self.assertTrue(
            crypto.point_eq(
                crypto.scalarmult_base(crypto.sc_sub(msk, alpha)),
                crypto.point_sub(crypto.decodepoint(ring[index].commitment),
                                 Cp),
            ))

        mlsag2.generate_clsag_simple(
            msg,
            ring,
            CtKey(dest=priv, mask=msk),
            alpha,
            Cp,
            index,
            mg_buffer,
        )

        sD = crypto.decodepoint(mg_buffer[-1])
        sc1 = crypto.decodeint(mg_buffer[-2])
        scalars = [crypto.decodeint(x) for x in mg_buffer[1:-2]]
        H = crypto.new_point()
        sI = crypto.new_point()

        crypto.hash_to_point_into(H, crypto.encodepoint(P))
        crypto.scalarmult_into(sI, H, priv)  # I = p*H
        return msg, scalars, sc1, sI, sD, ring2, Cp
Esempio n. 9
0
def prove_range_orig(amount, last_mask=None, use_asnl=False):
    """
    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, insecure
    :return: sumCi, mask, RangeSig.
        sumCi is Pedersen commitment on the amount value. sumCi = aG + amount*H
        mask is "a" from the Pedersent commitment above.
    """
    bb = d2b(amount,
             ATOMS)  # gives binary form of bb in "digits" binary digits
    logger.info("amount, amount in binary %s %s" % (amount, bb))
    ai = [None] * len(bb)
    Ci = [None] * len(bb)
    CiH = [None] * len(bb)  # this is like Ci - 2^i H
    H2 = crypto.gen_Hpow(ATOMS)
    a = crypto.sc_0()
    for i in range(0, ATOMS):
        ai[i] = crypto.random_scalar()
        if last_mask is not None and i == ATOMS - 1:
            ai[i] = crypto.sc_sub(last_mask, a)

        a = crypto.sc_add(
            a, ai[i]
        )  # creating the total mask since you have to pass this to receiver...
        if bb[i] == 0:
            Ci[i] = crypto.scalarmult_base(ai[i])
        if bb[i] == 1:
            Ci[i] = crypto.point_add(crypto.scalarmult_base(ai[i]), H2[i])
        CiH[i] = crypto.point_sub(Ci[i], H2[i])

    A = xmrtypes.BoroSig()

    if use_asnl:
        A.s0, A.s1, A.ee = asnl.gen_asnl(ai, Ci, CiH, bb)
    else:
        A.s0, A.s1, A.ee = mlsag2.gen_borromean(ai, Ci, CiH, bb)

    R = xmrtypes.RangeSig()
    R.asig = A
    R.Ci = Ci

    C = sum_Ci(Ci)
    return C, a, R
Esempio n. 10
0
    def poc(self):
        print('[+] PoC Ledger-app-Monero 1.4.2 spend key extraction')
        self.reset()
        self.set_mode()
        self.open_tx()

        # 1. call `monero_apdu_generate_keypair()`, obtain `{enc(x), hmac(enx(x))}`, where `x` is unknown.
        _, sec = self.gen_kp()
        print('  1. rsec: %s' % self.fmtkey(sec).decode('ascii'))

        # 2. call `sc_sub(enc(x), hmac(enc(x)), enc(x), hmac(enc(x)))`, obtain `{enc(0), hmac(enc(0))}`
        zero = self.sc_sub(sec, sec)
        print('  2. zero: %s' % self.fmtkey(zero).decode('ascii'))

        # 3. call `monero_apdu_derive_secret_key(
        #             enc(0), hmac(enc(0)), 0, C_FAKE_SEC_SPEND_KEY, hmac(C_FAKE_SEC_SPEND_KEY))`,
        #    obtain `{enc(r), hmac(enc(r))}`
        encr = self.derive_secret_key(zero, 0, self.fake_b)
        print('  3. encr: %s' % self.fmtkey(encr).decode('ascii'))

        # 4. call `monero_apdu_mlsag_sign(enc(0), hmac(enc(0)), enc(r), hmac(enc(r)))`, obtain `r`
        r = self.mlsag_sign_s(zero, encr)
        print('  4. r:  %s' % self.fmtkey(r).decode('ascii'))

        # 5. compute b: `b = r - H_s(00....00 || varint(0))`
        hs0 = crypto.hash_to_scalar(bytearray(33))
        rsc = crypto.decodeint(r[0])
        bsc = crypto.sc_sub(rsc, hs0)
        b = crypto.encodeint(bsc)
        print('  5. b:  %s' % binascii.hexlify(b).decode('ascii'))

        B = crypto.scalarmult_base(bsc)
        print('  5. B:  %s' %
              binascii.hexlify(crypto.encodepoint(B)).decode('ascii'))

        # 6. Verify
        BB = self.scalarmult(self.fake_b)
        print('  6. bG: %s' % binascii.hexlify(BB).decode('ascii'))

        if BB == crypto.encodepoint(B):
            print('[+] PoC successful')
        else:
            print('[-] PoC not working')

        if self.lite:
            return

        self.extra_poc(zero, hs0, B)
Esempio n. 11
0
def ver_mlsag_ext(message, pk, rv, dsRows):
    """
    Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures)
    c.f. http://eprint.iacr.org/2015/1098 section 2.
    keyImageV just does I[i] = xx[i] * Hash(xx[i] * G) for each i

    :param message:
    :param pk: matrix of EC points, point form.
    :param rv:
    :param dsRows:
    :return:
    """
    rows, cols = ver_mlsag_assert(pk, rv, dsRows)
    c_old = rv.cc

    Ip = key_vector(dsRows)
    for i in range(dsRows):
        Ip[i] = crypto.precomp(rv.II[i])

    i = 0
    while i < cols:
        c = 0
        hasher = hasher_message(message)
        for j in range(dsRows):
            L = crypto.add_keys2(rv.ss[i][j], c_old, pk[i][j])
            Hi = crypto.hash_to_point(crypto.encodepoint(
                pk[i][j]))  # originally hashToPoint()
            R = crypto.add_keys3(rv.ss[i][j], Hi, c_old, Ip[j])
            hasher.update(crypto.encodepoint(pk[i][j]))
            hasher.update(crypto.encodepoint(L))
            hasher.update(crypto.encodepoint(R))

        for j in range(dsRows, rows):
            L = crypto.add_keys2(rv.ss[i][j], c_old, pk[i][j])
            hasher.update(crypto.encodepoint(pk[i][j]))
            hasher.update(crypto.encodepoint(L))

        c = crypto.decodeint(hasher.digest())
        c_old = c
        i += 1

    c = crypto.sc_sub(c_old, rv.cc)
    return not crypto.sc_isnonzero(c)
Esempio n. 12
0
def generate_clsag_simple(
    message: bytes,
    pubs: List[MoneroRctKeyPublic],
    in_sk: CtKey,
    a: Sc25519,
    cout: Ge25519,
    index: int,
    mg_buff: List[bytes],
) -> List[bytes]:
    """
    CLSAG for RctType.Simple
    https://eprint.iacr.org/2019/654.pdf

    Corresponds to proveRctCLSAGSimple in rctSigs.cpp

    :param message: the full message to be signed (actually its hash)
    :param pubs: vector of MoneroRctKey; this forms the ring; point values in encoded form; (dest, mask) = (P, C)
    :param in_sk: CtKey; spending private key with input commitment mask (original); better_name: input_secret_key
    :param a: mask from the pseudo output commitment; better name: pseudo_out_alpha
    :param cout: pseudo output commitment; point, decoded; better name: pseudo_out_c
    :param index: specifies corresponding public key to the `in_sk` in the pubs array
    :param mg_buff: buffer to store the signature to
    """
    cols = len(pubs)
    if cols == 0:
        raise ValueError("Empty pubs")

    P = key_vector(cols)
    C_nonzero = key_vector(cols)
    p = in_sk.dest
    z = crypto.sc_sub(in_sk.mask, a)

    for i in range(cols):
        P[i] = pubs[i].dest
        C_nonzero[i] = pubs[i].commitment
        pubs[i] = None

    del pubs
    # gc.collect()

    return _generate_clsag(message, P, p, C_nonzero, z, cout, index, mg_buff)
Esempio n. 13
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()
    """
    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.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(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 = crypto.ge_frombytes_vartime(pubs[i])
            tmp2 = crypto.ge_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.ge_double_scalarmult_precomp_vartime(
                sig[i][1], tmp3, sig[i][0], image_pre)
            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
Esempio n. 14
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
    :return: sumCi, mask, RangeSig.
        sumCi is Pedersen commitment on the amount value. sumCi = aG + amount*H
        mask is "a" from the Pedersent commitment above.
    """
    n = ATOMS
    bb = d2b(amount, n)  # gives binary form of bb in "digits" binary digits
    ai = [None] * len(bb)
    Ci = [None] * len(bb)
    a = crypto.sc_0()

    C = crypto.identity()
    alpha = mlsag2.key_zero_vector(n)
    s1 = mlsag2.key_zero_vector(n)
    c_H = crypto.xmr_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 == ATOMS - 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:
            Ci[ii] = crypto.scalarmult_base(ai[ii])
        else:
            Ci[ii] = crypto.point_add(crypto.scalarmult_base(ai[ii]), c_H)
        C = crypto.point_add(C, Ci[ii])

        if bb[ii] == 0:
            s1[ii] = crypto.random_scalar()
            c = crypto.hash_to_scalar(crypto.encodepoint(L))
            L = crypto.add_keys2(s1[ii], c, crypto.point_sub(Ci[ii], c_H))
            kck.update(crypto.encodepoint(L))

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

        c_H = crypto.point_double(c_H)

    # Compute ee, memory cleanup
    ee = crypto.decodeint(kck.digest())
    del kck

    # Second phase computes: s0, s1
    c_H = crypto.xmr_H()
    s0 = mlsag2.key_zero_vector(n)

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

        else:
            s0[jj] = crypto.random_scalar()
            LL = crypto.add_keys2(s0[jj], ee, Ci[jj])
            cc = crypto.hash_to_scalar(crypto.encodepoint(LL))
            s1[jj] = crypto.sc_mulsub(ai[jj], cc, alpha[jj])
        c_H = crypto.point_double(c_H)

    A = xmrtypes.BoroSig()
    A.s0, A.s1, A.ee = s0, s1, ee

    R = xmrtypes.RangeSig()
    R.asig = A
    R.Ci = Ci

    return C, a, R
Esempio n. 15
0
def verify_clsag(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(_HASH_KEY_CLSAG_AGG_0)
    hsh_C.update(_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(_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")
Esempio n. 16
0
def prove_rct_mg(message, pubs, in_sk, out_sk, out_pk, kLRki, mscout, index,
                 txn_fee_key):
    """
    c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10.
    This does the MG sig on the "dest" part of the given key matrix, and
    the last row is the sum of input commitments from that column - sum output commitments
    this shows that sum inputs = sum outputs
    :param message:
    :param pubs: matrix of CtKeys. points are encoded.
    :param in_sk:
    :param out_sk:
    :param out_pk:
    :param kLRki:
    :param mscout:
    :param index:
    :param txn_fee_key:
    :return:
    """
    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) != len(out_pk):
        raise ValueError("Bad outsk/putpk size")
    if (not kLRki or not mscout) and (kLRki and mscout):
        raise ValueError("Only one of kLRki/mscout is present")

    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)):
            M[i][rows] = crypto.point_sub(
                M[i][rows], crypto.decodepoint(
                    out_pk[j].mask))  # 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)):
        sk[rows] = crypto.sc_sub(
            sk[rows], out_sk[j].mask)  # subtract output masks in last row

    return gen_mlsag_ext(message, M, sk, kLRki, mscout, index, rows)
Esempio n. 17
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:
    """
    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.ge_frombytes_vartime_check(k)

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

    buff = b"" + prefix_hash
    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)
            buff += crypto.encodepoint(tmp3)
            tmp3 = crypto.hash_to_ec(crypto.encodepoint(pubs[i]))
            tmp2 = crypto.scalarmult(tmp3, k)
            buff += crypto.encodepoint(tmp2)
        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])
            buff += crypto.encodepoint(tmp2)
            tmp3 = crypto.hash_to_ec(crypto.encodepoint(tmp3))
            tmp2 = crypto.ge_double_scalarmult_precomp_vartime(
                sig[i][1], tmp3, sig[i][0], image_pre)
            buff += crypto.encodepoint(tmp2)
            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
Esempio n. 18
0
 async def sc_sub(self):
     s1 = crypto.decodeint(self._fetch())
     s2 = crypto.decodeint(self._fetch())
     self._insert(crypto.encodeint(crypto.sc_sub(s1, s2)))
     return SW_OK
Esempio n. 19
0
    async def gen_rct_simple(
        self,
        in_sk,
        destinations,
        inamounts,
        outamounts,
        txn_fee,
        mix_ring,
        kLRki,
        msout,
        index,
    ):
        """
        Generate simple RCT signature.

        :param in_sk:
        :param destinations:
        :param inamounts:
        :param outamounts:
        :param txn_fee:
        :param mix_ring:
        :param kLRki:
        :param msout:
        :param index:
        :param out_sk:
        :return:
        """
        if len(inamounts) == 0:
            raise ValueError("Empty inamounts")
        if len(inamounts) != len(in_sk):
            raise ValueError("Different number of inamounts/inSk")
        if len(outamounts) != len(destinations):
            raise ValueError("Different number of amounts/destinations")
        if len(self.output_secrets) != len(destinations):
            raise ValueError("Different number of amount_keys/destinations")
        if len(index) != len(in_sk):
            raise ValueError("Different number of index/inSk")
        if len(mix_ring) != len(in_sk):
            raise ValueError("Different number of mixRing/inSk")
        for idx in range(len(mix_ring)):
            if index[idx] >= len(mix_ring[idx]):
                raise ValueError("Bad index into mixRing")

        rv, sumout, out_sk = await self.gen_rct_header(destinations, outamounts)
        rv.type = (
            xmrtypes.RctType.SimpleBulletproof
            if self.use_bulletproof
            else xmrtypes.RctType.Simple
        )
        rv.txnFee = txn_fee
        rv.mixRing = mix_ring

        # Pseudooutputs
        pseudo_outs = [None] * len(inamounts)
        rv.p.MGs = [None] * len(inamounts)
        sumpouts = crypto.sc_0()
        a = []
        for idx in range(len(inamounts) - 1):
            a.append(crypto.random_scalar())
            sumpouts = crypto.sc_add(sumpouts, a[idx])
            pseudo_outs[idx] = crypto.gen_c(a[idx], inamounts[idx])

        a.append(crypto.sc_sub(sumout, sumpouts))
        pseudo_outs[-1] = crypto.gen_c(a[-1], inamounts[-1])

        if self.use_bulletproof:
            rv.p.pseudoOuts = [crypto.encodepoint(x) for x in pseudo_outs]
        else:
            rv.pseudoOuts = [crypto.encodepoint(x) for x in pseudo_outs]

        full_message = await monero.get_pre_mlsag_hash(rv)

        # TODO: msout multisig
        for i in range(len(inamounts)):
            rv.p.MGs[i], msc = mlsag2.prove_rct_mg_simple(
                full_message,
                rv.mixRing[i],
                in_sk[i],
                a[i],
                pseudo_outs[i],
                kLRki[i] if kLRki else None,
                None,
                index[i],
            )

            if __debug__:
                assert mlsag2.ver_rct_mg_simple(
                    full_message, rv.p.MGs[i], rv.mixRing[i], pseudo_outs[i]
                )

        return rv
Esempio n. 20
0
 def __sub__(self, other):
     return self.__class__(key=crypto.sc_sub(self._key, other._key))
Esempio n. 21
0
    async def range_proof(self, idx, dest_pub_key, amount, amount_key):
        """
        Computes rangeproof and related information - out_sk, out_pk, ecdh_info.
        In order to optimize incremental transaction build, the mask computation is changed compared
        to the official Monero code. In the official code, the input pedersen commitments are computed
        after range proof in such a way summed masks for commitments (alpha) and rangeproofs (ai) are equal.

        In order to save roundtrips we compute commitments randomly and then for the last rangeproof
        a[63] = (\\sum_{i=0}^{num_inp}alpha_i - \\sum_{i=0}^{num_outs-1} amasks_i) - \\sum_{i=0}^{62}a_i

        The range proof is incrementally hashed to the final_message.

        :param idx:
        :param dest_pub_key:
        :param amount:
        :param amount_key:
        :return:
        """
        from monero_glue.xmr import ring_ct

        rsig = bytearray(32 * (64 + 64 + 64 + 1))
        rsig_mv = memoryview(rsig)

        out_pk = misc.StdObj(dest=dest_pub_key, mask=None)
        is_last = idx + 1 == self.num_dests()
        last_mask = (
            None
            if not is_last or not self.use_simple_rct
            else crypto.sc_sub(self.sumpouts_alphas, self.sumout)
        )

        # Pedersen commitment on the value, mask from the commitment, range signature.
        C, mask, rsig = None, 0, None

        # Rangeproof
        gc.collect()
        if self.use_bulletproof:
            raise ValueError("Bulletproof not yet supported")

        else:
            C, mask, rsig = ring_ct.prove_range(
                amount, last_mask, backend_impl=True, byte_enc=True, rsig=rsig_mv
            )
            rsig = memoryview(rsig)

            if __debug__:
                rsig_bytes = monero.inflate_rsig(rsig)
                self.assrt(ring_ct.ver_range(C, rsig_bytes))

            self.assrt(
                crypto.point_eq(
                    C,
                    crypto.point_add(
                        crypto.scalarmult_base(mask), crypto.scalarmult_h(amount)
                    ),
                ),
                "rproof",
            )

            # Incremental hashing
            await self.full_message_hasher.rsig_val(
                rsig, self.use_bulletproof, raw=True
            )
        gc.collect()
        self._log_trace("rproof")

        # Mask sum
        out_pk.mask = crypto.encodepoint(C)
        self.sumout = crypto.sc_add(self.sumout, mask)
        self.output_sk.append(misc.StdObj(mask=mask))

        # ECDH masking
        from monero_glue.xmr.sub.recode import recode_ecdh
        from monero_serialize.xmrtypes import EcdhTuple

        ecdh_info = EcdhTuple(mask=mask, amount=crypto.sc_init(amount))
        ecdh_info = ring_ct.ecdh_encode(
            ecdh_info, derivation=crypto.encodeint(amount_key)
        )
        recode_ecdh(ecdh_info, encode=True)
        gc.collect()

        return rsig, out_pk, ecdh_info