Skip to content

Commit

Permalink
xmr: move range signatures to seperate file; rename mlsag2 to mlsag
Browse files Browse the repository at this point in the history
  • Loading branch information
tsusanka committed Oct 12, 2018
1 parent a89f3ab commit 1ba72b6
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 172 deletions.
14 changes: 8 additions & 6 deletions src/apps/monero/protocol/signing/step_06_set_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def _range_proof(state, amount, rsig_data):
The range proof is incrementally hashed to the final_message.
"""
from apps.monero.xmr import ring_ct
from apps.monero.xmr import range_signatures

mask = state.output_masks[state.current_output_index]
provided_rsig = None
Expand Down Expand Up @@ -179,7 +179,9 @@ def _range_proof(state, amount, rsig_data):
state.mem_trace("pre-rproof" if __debug__ else None, collect=True)
if state.rsig_type == RsigType.Bulletproof and not state.rsig_offload:
"""Bulletproof calculation in trezor"""
rsig = ring_ct.prove_range_bp_batch(state.output_amounts, state.output_masks)
rsig = range_signatures.prove_range_bp_batch(
state.output_amounts, state.output_masks
)
state.mem_trace("post-bp" if __debug__ else None, collect=True)

# Incremental BP hashing
Expand All @@ -196,8 +198,8 @@ def _range_proof(state, amount, rsig_data):

elif state.rsig_type == RsigType.Borromean and not state.rsig_offload:
"""Borromean calculation in trezor"""
C, mask, rsig = ring_ct.prove_range_chunked(amount, mask)
del (ring_ct)
C, mask, rsig = range_signatures.prove_range_borromean(amount, mask)
del range_signatures

# Incremental hashing
state.full_message_hasher.rsig_val(rsig, False, raw=True)
Expand All @@ -219,10 +221,10 @@ def _range_proof(state, amount, rsig_data):
# array sizes compared to the serialized bulletproof format
# thus direct serialization cannot be used.
state.full_message_hasher.rsig_val(bp_obj, True, raw=False)
res = ring_ct.verify_bp(bp_obj, state.output_amounts, masks)
res = range_signatures.verify_bp(bp_obj, state.output_amounts, masks)
state.assrt(res, "BP verification fail")
state.mem_trace("BP verified" if __debug__ else None, collect=True)
del (bp_obj, ring_ct)
del (bp_obj, range_signatures)

elif state.rsig_type == RsigType.Borromean and state.rsig_offload:
"""Borromean offloading not supported"""
Expand Down
6 changes: 3 additions & 3 deletions src/apps/monero/protocol/signing/step_09_sign_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,12 @@ async def sign_input(
state.mem_trace(4)

# RCT signature
from apps.monero.xmr import mlsag2
from apps.monero.xmr import mlsag

if state.rct_type == RctType.Simple:
# Simple RingCT
mix_ring = [x.key for x in src_entr.outputs]
mg, msc = mlsag2.prove_rct_mg_simple(
mg, msc = mlsag.prove_rct_mg_simple(
state.full_message,
mix_ring,
input_secret_key,
Expand All @@ -149,7 +149,7 @@ async def sign_input(
txn_fee_key = crypto.scalarmult_h(state.fee)
mix_ring = [[x.key] for x in src_entr.outputs]

mg, msc = mlsag2.prove_rct_mg(
mg, msc = mlsag.prove_rct_mg(
state.full_message,
mix_ring,
[input_secret_key],
Expand Down
4 changes: 1 addition & 3 deletions src/apps/monero/xmr/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
# https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-00#section-4
# https://github.com/monero-project/research-lab

from trezor.crypto import hmac
from trezor.crypto import monero as tcry
from trezor.crypto import random
from trezor.crypto import hmac, monero as tcry, random
from trezor.crypto.hashlib import sha3_256

NULL_KEY_ENC = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
Expand Down
File renamed without changes.
141 changes: 141 additions & 0 deletions src/apps/monero/xmr/range_signatures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
"""
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()
Zero = 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)

# C_tmp += &Zero if BB(ii) == 0 else &C_h
crypto.point_add_into(C_tmp, C_tmp, Zero if ((amount >> ii) & 1) == 0 else 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, Zero)
gc.collect()

return C_acc, a, [s0s, s1s, ee_bin, Cis]
161 changes: 1 addition & 160 deletions src/apps/monero/xmr/ring_ct.py
Original file line number Diff line number Diff line change
@@ -1,170 +1,11 @@
# Author: https://github.com/monero-project/mininero
# Author: Dusan Klinec, ph4r05, 2018

import gc

from apps.monero.xmr import crypto


def prove_range_bp(amount, last_mask=None):
mask = last_mask if last_mask is not None else crypto.random_scalar()
bp_proof = prove_range_bp_batch([amount], [mask])

C = crypto.decodepoint(bp_proof.V[0])
C = crypto.point_mul8(C)

gc.collect()

# Return as struct as the hash(BP_struct) != hash(BP_serialized)
# as the original hashing does not take vector lengths into account which are dynamic
# in the serialization scheme (and thus extraneous)
return C, mask, bp_proof


def prove_range_bp_batch(amounts, masks):
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=None, masks=None):
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 as struct as the hash(BP_struct) != hash(BP_serialized)
# as the original hashing does not take vector lengths into account which are dynamic
# in the serialization scheme (and thus extraneous)
return res


def prove_range_chunked(amount, last_mask=None):
# 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()
Zero = 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)

# C_tmp += &Zero if BB(ii) == 0 else &C_h
crypto.point_add_into(C_tmp, C_tmp, Zero if ((amount >> ii) & 1) == 0 else 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, Zero)
gc.collect()

return C_acc, a, [s0s, s1s, ee_bin, Cis]


# Ring-ct MG sigs
# Prove:
# 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
# Ver:
# verifies the above sig is created corretly
# TODO


#
# Key image import / export
#


def generate_ring_signature(prefix_hash, image, pubs, sec, sec_idx, test=False):
"""
Generates ring signature with key image.
Expand Down

0 comments on commit 1ba72b6

Please sign in to comment.