def derive_subaddress_public_key(out_key, derivation, output_index): """ out_key - H_s(derivation || varint(output_index))G :param out_key: :param derivation: :param output_index: :return: """ crypto.check_ed25519point(out_key) scalar = crypto.derivation_to_scalar(derivation, output_index) point2 = crypto.scalarmult_base(scalar) point4 = crypto.point_sub(out_key, point2) return point4
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
def ver_rct_mg_simple(message, mg, pubs, C): """ Verifies the above sig is created corretly :param message: :param mg: :param pubs: vector of points, encoded :param C: :return: """ rows = 1 cols = len(pubs) if cols == 0: raise ValueError("Empty pubs") M = key_matrix(rows + 1, cols) for i in range(cols): M[i][0] = crypto.decodepoint(pubs[i].dest) M[i][1] = crypto.point_sub(crypto.decodepoint(pubs[i].commitment), C) return ver_mlsag_ext(message, M, mg, rows)
def decode_rct(rv, sk, i): """ c.f. http:#eprint.iacr.org/2015/1098 section 5.1.1 Uses the attached ecdh info to find the amounts represented by each output commitment must know the destination private key to find the correct amount, else will return a random number :param rv: :param sk: :param i: :return: """ decodedTuple = ecdh_decode(rv.ecdhInfo[i], sk) mask = decodedTuple.mask amount = decodedTuple.amount C = rv.outPk[i].mask H = crypto.xmr_H() Ctmp = crypto.point_add(crypto.scalarmult_base(mask), crypto.scalarmult(H, amount)) if not crypto.point_eq(crypto.point_sub(C, Ctmp), crypto.identity()): logger.warning("warning, amount decoded incorrectly, will be unable to spend") return amount
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
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")
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)