def generate_tx_spend_and_key_image( ack: AccountCreds, out_key: Ge25519, recv_derivation: Ge25519, real_output_index: int, received_index: Tuple[int, int], ) -> Optional[Tuple[Sc25519, Ge25519]]: """ Generates UTXO spending key and key image. Corresponds to generate_key_image_helper_precomp() in the Monero codebase. :param ack: sender credentials :type ack: apps.monero.xmr.credentials.AccountCreds :param out_key: real output (from input RCT) destination key :param recv_derivation: :param real_output_index: :param received_index: subaddress index this payment was received to :return: """ if not crypto.sc_isnonzero(ack.spend_key_private): raise ValueError("Watch-only wallet not supported") # derive secret key with subaddress - step 1: original CN derivation scalar_step1 = crypto.derive_secret_key( recv_derivation, real_output_index, ack.spend_key_private ) # step 2: add Hs(SubAddr || a || index_major || index_minor) subaddr_sk = None if received_index == (0, 0): scalar_step2 = scalar_step1 else: subaddr_sk = get_subaddress_secret_key( ack.view_key_private, major=received_index[0], minor=received_index[1] ) scalar_step2 = crypto.sc_add(scalar_step1, subaddr_sk) # When not in multisig, we know the full spend secret key, so the output pubkey can be obtained by scalarmultBase pub_ver = crypto.scalarmult_base(scalar_step2) # <Multisig>, branch deactivated until implemented # # When in multisig, we only know the partial spend secret key. But we do know the full spend public key, # # so the output pubkey can be obtained by using the standard CN key derivation. # pub_ver = crypto.derive_public_key( # recv_derivation, real_output_index, ack.spend_key_public # ) # # # Add the contribution from the subaddress part # if received_index != (0, 0): # subaddr_pk = crypto.scalarmult_base(subaddr_sk) # pub_ver = crypto.point_add(pub_ver, subaddr_pk) # </Multisig> if not crypto.point_eq(pub_ver, out_key): raise ValueError( "key image helper precomp: given output pubkey doesn't match the derived one" ) ki = generate_key_image(crypto.encodepoint(pub_ver), scalar_step2) return scalar_step2, ki
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