You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/src/apps/monero/xmr/range_signatures.py

145 lines
4.0 KiB

"""
Computes range signature
Can compute Borromean range proof or Bulletproof.
Also can verify Bulletproof, in case the computation was offloaded.
Mostly ported from official Monero client, but also inspired by Mininero.
Author: Dusan Klinec, ph4r05, 2018
"""
import gc
from apps.monero.xmr import crypto
def prove_range_bp_batch(amounts, masks):
"""Calculates Bulletproof in batches"""
from apps.monero.xmr import bulletproof as bp
bpi = bp.BulletProofBuilder()
bp_proof = bpi.prove_batch([crypto.sc_init(a) for a in amounts], masks)
del (bpi, bp)
gc.collect()
return bp_proof
def verify_bp(bp_proof, amounts, masks):
"""Verifies Bulletproof"""
from apps.monero.xmr import bulletproof as bp
if amounts:
bp_proof.V = []
for i in range(len(amounts)):
C = crypto.gen_commitment(masks[i], amounts[i])
crypto.scalarmult_into(C, C, crypto.sc_inv_eight())
bp_proof.V.append(crypto.encodepoint(C))
bpi = bp.BulletProofBuilder()
res = bpi.verify(bp_proof)
gc.collect()
return res
def prove_range_borromean(amount, last_mask):
"""Calculates Borromean range proof"""
# The large chunks allocated first to avoid potential memory fragmentation issues.
ai = bytearray(32 * 64)
alphai = bytearray(32 * 64)
Cis = bytearray(32 * 64)
s0s = bytearray(32 * 64)
s1s = bytearray(32 * 64)
buff = bytearray(32)
ee_bin = bytearray(32)
a = crypto.sc_init(0)
si = crypto.sc_init(0)
c = crypto.sc_init(0)
ee = crypto.sc_init(0)
tmp_ai = crypto.sc_init(0)
tmp_alpha = crypto.sc_init(0)
C_acc = crypto.identity()
C_h = crypto.xmr_H()
C_tmp = crypto.identity()
L = crypto.identity()
kck = crypto.get_keccak()
for ii in range(64):
crypto.random_scalar(tmp_ai)
if last_mask is not None and ii == 63:
crypto.sc_sub_into(tmp_ai, last_mask, a)
crypto.sc_add_into(a, a, tmp_ai)
crypto.random_scalar(tmp_alpha)
crypto.scalarmult_base_into(L, tmp_alpha)
crypto.scalarmult_base_into(C_tmp, tmp_ai)
# if 0: C_tmp += Zero (nothing is added)
# if 1: C_tmp += 2^i*H
# 2^i*H is already stored in C_h
if (amount >> ii) & 1 == 1:
crypto.point_add_into(C_tmp, C_tmp, C_h)
crypto.point_add_into(C_acc, C_acc, C_tmp)
# Set Ci[ii] to sigs
crypto.encodepoint_into(Cis, C_tmp, ii << 5)
crypto.encodeint_into(ai, tmp_ai, ii << 5)
crypto.encodeint_into(alphai, tmp_alpha, ii << 5)
if ((amount >> ii) & 1) == 0:
crypto.random_scalar(si)
crypto.encodepoint_into(buff, L)
crypto.hash_to_scalar_into(c, buff)
crypto.point_sub_into(C_tmp, C_tmp, C_h)
crypto.add_keys2_into(L, si, c, C_tmp)
crypto.encodeint_into(s1s, si, ii << 5)
crypto.encodepoint_into(buff, L)
kck.update(buff)
crypto.point_double_into(C_h, C_h)
# Compute ee
tmp_ee = kck.digest()
crypto.decodeint_into(ee, tmp_ee)
del (tmp_ee, kck)
C_h = crypto.xmr_H()
gc.collect()
# Second pass, s0, s1
for ii in range(64):
crypto.decodeint_into(tmp_alpha, alphai, ii << 5)
crypto.decodeint_into(tmp_ai, ai, ii << 5)
if ((amount >> ii) & 1) == 0:
crypto.sc_mulsub_into(si, tmp_ai, ee, tmp_alpha)
crypto.encodeint_into(s0s, si, ii << 5)
else:
crypto.random_scalar(si)
crypto.encodeint_into(s0s, si, ii << 5)
crypto.decodepoint_into(C_tmp, Cis, ii << 5)
crypto.add_keys2_into(L, si, ee, C_tmp)
crypto.encodepoint_into(buff, L)
crypto.hash_to_scalar_into(c, buff)
crypto.sc_mulsub_into(si, tmp_ai, c, tmp_alpha)
crypto.encodeint_into(s1s, si, ii << 5)
crypto.point_double_into(C_h, C_h)
crypto.encodeint_into(ee_bin, ee)
del (ai, alphai, buff, tmp_ai, tmp_alpha, si, c, ee, C_tmp, C_h, L)
gc.collect()
return C_acc, a, [s0s, s1s, ee_bin, Cis]