From 43de24d88c87706366bd26d124cc6859366f336d Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 22 Apr 2021 13:29:28 +0800 Subject: [PATCH 01/10] Add Orchard generators --- orchard_generators.py | 61 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 orchard_generators.py diff --git a/orchard_generators.py b/orchard_generators.py new file mode 100644 index 0000000..1737108 --- /dev/null +++ b/orchard_generators.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +import sys; assert sys.version_info[0] >= 3, "Python 3 required." + +from pyblake2 import blake2s + +from tv_output import render_args, render_tv +from orchard_group_hash import group_hash +from orchard_sinsemilla import sinsemilla_hash_to_point + +# https://zips.z.cash/protocol/nu5.pdf#concretespendauthsig +SPENDING_KEY_BASE = group_hash(b'z.cash:Orchard', b'G') + +# https://zips.z.cash/protocol/nu5.pdf#commitmentsandnullifiers +NULLIFIER_K_BASE = group_hash(b'z.cash:Orchard', b'K') + +# https://zips.z.cash/protocol/nu5.pdf#concretehomomorphiccommit +VALUE_COMMITMENT_VALUE_BASE = group_hash(b'z.cash:Orchard-cv', b'v') +VALUE_COMMITMENT_RANDOMNESS_BASE = group_hash(b'z.cash:Orchard-cv', b'r') + +# Used in SinsemillaCommit (https://zips.z.cash/protocol/nu5.pdf#sinsemillacommitments) +NOTE_COMMITMENT_BASE = group_hash(b'z.cash:Orchard-NoteCommit-r', b'') +NOTE_COMMITMENT_Q = group_hash(b'z.cash:Orchard-NoteCommit-M', b'') + +# Used in SinsemillaShortCommit (https://zips.z.cash/protocol/nu5.pdf#sinsemillacommitments) +IVK_COMMITMENT_BASE = group_hash(b'z.cash:Orchard-CommitIvk-r', b'') +IVK_COMMITMENT_Q = group_hash(b'z.cash:Orchard-CommitIvk-M', b'') + +# Used in SinsemillaHash (https://zips.z.cash/protocol/nu5.pdf#orchardmerklecrh) +MERKLE_CRH_Q = group_hash(b'z.cash:Orchard-MerkleCRH', b'') + +def main(): + render_tv( + render_args(), + 'orchard_generators', + ( + ('skb', '[u8; 32]'), + ('nkb', '[u8; 32]'), + ('vcvb', '[u8; 32]'), + ('vcrb', '[u8; 32]'), + ('cmb', '[u8; 32]'), + ('cmq', '[u8; 32]'), + ('ivkb', '[u8; 32]'), + ('ivkq', '[u8; 32]'), + ('mcq', '[u8; 32]'), + ), + { + 'skb': bytes(SPENDING_KEY_BASE), + 'nkb': bytes(NULLIFIER_K_BASE), + 'vcvb': bytes(VALUE_COMMITMENT_VALUE_BASE), + 'vcrb': bytes(VALUE_COMMITMENT_RANDOMNESS_BASE), + 'cmb': bytes(NOTE_COMMITMENT_BASE), + 'cmq': bytes(NOTE_COMMITMENT_Q), + 'ivkb': bytes(IVK_COMMITMENT_BASE), + 'ivkq': bytes(IVK_COMMITMENT_Q), + 'mcq': bytes(MERKLE_CRH_Q), + }, + ) + + +if __name__ == '__main__': + main() From bd7c367590287922c01afc3125f947d101dbd7f8 Mon Sep 17 00:00:00 2001 From: ying tong Date: Tue, 27 Apr 2021 12:33:56 +0800 Subject: [PATCH 02/10] Apply suggestions from code review Co-authored-by: str4d --- orchard_generators.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/orchard_generators.py b/orchard_generators.py index 1737108..dff66e7 100644 --- a/orchard_generators.py +++ b/orchard_generators.py @@ -17,16 +17,16 @@ NULLIFIER_K_BASE = group_hash(b'z.cash:Orchard', b'K') VALUE_COMMITMENT_VALUE_BASE = group_hash(b'z.cash:Orchard-cv', b'v') VALUE_COMMITMENT_RANDOMNESS_BASE = group_hash(b'z.cash:Orchard-cv', b'r') -# Used in SinsemillaCommit (https://zips.z.cash/protocol/nu5.pdf#sinsemillacommitments) +# Used in SinsemillaCommit (https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit) NOTE_COMMITMENT_BASE = group_hash(b'z.cash:Orchard-NoteCommit-r', b'') -NOTE_COMMITMENT_Q = group_hash(b'z.cash:Orchard-NoteCommit-M', b'') +NOTE_COMMITMENT_Q = group_hash(b'z.cash:SinsemillaQ', b'z.cash:Orchard-NoteCommit-M') -# Used in SinsemillaShortCommit (https://zips.z.cash/protocol/nu5.pdf#sinsemillacommitments) +# Used in SinsemillaShortCommit (https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit) IVK_COMMITMENT_BASE = group_hash(b'z.cash:Orchard-CommitIvk-r', b'') -IVK_COMMITMENT_Q = group_hash(b'z.cash:Orchard-CommitIvk-M', b'') +IVK_COMMITMENT_Q = group_hash(b'z.cash:SinsemillaQ', b'z.cash:Orchard-CommitIvk-M') # Used in SinsemillaHash (https://zips.z.cash/protocol/nu5.pdf#orchardmerklecrh) -MERKLE_CRH_Q = group_hash(b'z.cash:Orchard-MerkleCRH', b'') +MERKLE_CRH_Q = group_hash(b'z.cash:SinsemillaQ', b'z.cash:Orchard-MerkleCRH') def main(): render_tv( From 72cae20b61d38735af4092ea95152bd15366b404 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Mon, 3 May 2021 14:26:44 +0800 Subject: [PATCH 03/10] Add Orchard commitments HomomorphicPedersenCommit -> ValueCommit SinsemillaCommit -> NoteCommit SinsemillaShortCommit -> CommitIvk --- orchard_commitments.py | 73 ++++++++++++++++++++++++++++++++++++++++++ orchard_pallas.py | 10 ++++++ 2 files changed, 83 insertions(+) create mode 100644 orchard_commitments.py diff --git a/orchard_commitments.py b/orchard_commitments.py new file mode 100644 index 0000000..288d0dd --- /dev/null +++ b/orchard_commitments.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +import sys; assert sys.version_info[0] >= 3, "Python 3 required." + +from orchard_group_hash import group_hash +from orchard_pallas import Fp, Scalar +from orchard_sinsemilla import sinsemilla_hash_to_point +from sapling_utils import i2lebsp + +# Commitment schemes used in Orchard https://zips.z.cash/protocol/nu5.pdf#concretecommit + +# https://zips.z.cash/protocol/nu5.pdf#constants +L_ORCHARD_BASE = 255 + +# https://zips.z.cash/protocol/nu5.pdf#concretehomomorphiccommit +def homomorphic_pedersen_commitment(rcv: Scalar, D, v: Scalar): + return group_hash(D, b"v") * v + group_hash(D, b"r") * rcv + +def value_commit(rcv: Scalar, v: Scalar): + return homomorphic_pedersen_commitment(rcv, b"z.cash:Orchard-cv", v) + +def rcv_trapdoor(randbytes): + return Scalar.random(randbytes) + +# https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit +def sinsemilla_commit(r: Scalar, D, M): + return sinsemilla_hash_to_point(D + b"-M", M).checked_incomplete_add( + r * group_hash(D + b"-r", "") + ) + +def sinsemilla_short_commit(r: Scalar, D, M): + return sinsemilla_commit(r, D, M).extract() + +# https://zips.z.cash/protocol/nu5.pdf#concreteorchardnotecommit +def note_commit(rcm, g_d, pk_d, v, rho, psi): + return sinsemilla_commit( + rcm, + "z.cash: Orchard-NoteCommit", + g_d + pk_d + i2lebsp(64, v) + i2lebsp(L_ORCHARD_BASE, rho) + i2lebsp(L_ORCHARD_BASE, psi) + ) + +def rcm_trapdoor(randbytes): + return Scalar.random(randbytes) + +# https://zips.z.cash/protocol/nu5.pdf#concreteorchardnotecommit +def commit_ivk(rivk, ak, nk): + return sinsemilla_short_commit( + rivk, + "z.cash: Orchard-CommitIvk", + i2lebsp(L_ORCHARD_BASE, ak) + i2lebsp(L_ORCHARD_BASE, nk) + ) + +def rivk_trapdoor(randbytes): + return Scalar.random(randbytes) + +# Test consistency of ValueCommit^{Orchard} with precomputed generators +def test_value_commit(): + from random import Random + from orchard_generators import VALUE_COMMITMENT_RANDOMNESS_BASE, VALUE_COMMITMENT_VALUE_BASE + + rng = Random(0xabad533d) + def randbytes(l): + ret = [] + while len(ret) < l: + ret.append(rng.randrange(0, 256)) + return bytes(ret) + + rcv = rcv_trapdoor(randbytes) + v = Scalar(100000000) + + assert value_commit(rcv, v) == VALUE_COMMITMENT_RANDOMNESS_BASE * rcv + VALUE_COMMITMENT_VALUE_BASE * v + +if __name__ == '__main__': + test_value_commit() diff --git a/orchard_pallas.py b/orchard_pallas.py index 29d5ad3..fe89954 100644 --- a/orchard_pallas.py +++ b/orchard_pallas.py @@ -31,6 +31,9 @@ class Fp(FieldElement): def from_bytes(buf): return Fp(leos2ip(buf), strict=True) + def random(randbytes): + return Fp(leos2ip(randbytes(32)), strict=False) + def __init__(self, s, strict=False): FieldElement.__init__(self, Fp, s, p, strict=strict) @@ -90,6 +93,13 @@ class Scalar(FieldElement): def __str__(self): return 'Scalar(%s)' % self.s + @staticmethod + def from_bytes(buf): + return Scalar(leos2ip(buf), strict=True) + + def random(randbytes): + return Scalar(leos2ip(randbytes(32)), strict=False) + Fp.ZERO = Fp(0) Fp.ONE = Fp(1) Fp.MINUS_ONE = Fp(-1) From a7a1e5b21738cf8d7aaf737c413862a0f1aad00c Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Thu, 6 May 2021 14:50:58 +0100 Subject: [PATCH 04/10] Use rejection sampling to implement `random` for `Fp` and `Scalar`. Signed-off-by: Daira Hopwood --- orchard_pallas.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/orchard_pallas.py b/orchard_pallas.py index fe89954..d060d76 100644 --- a/orchard_pallas.py +++ b/orchard_pallas.py @@ -32,7 +32,11 @@ class Fp(FieldElement): return Fp(leos2ip(buf), strict=True) def random(randbytes): - return Fp(leos2ip(randbytes(32)), strict=False) + while True: + try: + return Fp(leos2ip(randbytes(32)), strict=True) + except ValueError: + pass def __init__(self, s, strict=False): FieldElement.__init__(self, Fp, s, p, strict=strict) @@ -98,7 +102,12 @@ class Scalar(FieldElement): return Scalar(leos2ip(buf), strict=True) def random(randbytes): - return Scalar(leos2ip(randbytes(32)), strict=False) + while True: + try: + return Scalar(leos2ip(randbytes(32)), strict=True) + except ValueError: + pass + Fp.ZERO = Fp(0) Fp.ONE = Fp(1) From 929692ecdc112b1509be48f4d99809361c118c47 Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Thu, 6 May 2021 15:53:12 +0100 Subject: [PATCH 05/10] Rename sapling_utils.py to utils.py. Signed-off-by: Daira Hopwood --- ff1.py | 2 +- orchard_commitments.py | 2 +- orchard_group_hash.py | 2 +- orchard_map_to_curve.py | 2 +- orchard_merkle_tree.py | 2 +- orchard_pallas.py | 2 +- orchard_poseidon.py | 2 +- orchard_poseidon_hash.py | 2 +- orchard_sinsemilla.py | 2 +- sapling_generators.py | 2 +- sapling_jubjub.py | 2 +- sapling_key_components.py | 2 +- sapling_merkle_tree.py | 2 +- sapling_note_encryption.py | 2 +- sapling_notes.py | 2 +- sapling_pedersen.py | 2 +- sapling_signatures.py | 2 +- sapling_zip32.py | 2 +- transaction.py | 2 +- sapling_utils.py => utils.py | 0 20 files changed, 19 insertions(+), 19 deletions(-) rename sapling_utils.py => utils.py (100%) diff --git a/ff1.py b/ff1.py index 9524ea6..cf99992 100644 --- a/ff1.py +++ b/ff1.py @@ -7,7 +7,7 @@ from binascii import unhexlify, hexlify from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend -from sapling_utils import bebs2ip, i2bebsp, beos2ip, bebs2osp, cldiv +from utils import bebs2ip, i2bebsp, beos2ip, bebs2osp, cldiv # Morris Dworkin # NIST Special Publication 800-38G diff --git a/orchard_commitments.py b/orchard_commitments.py index 288d0dd..f60e35d 100644 --- a/orchard_commitments.py +++ b/orchard_commitments.py @@ -4,7 +4,7 @@ import sys; assert sys.version_info[0] >= 3, "Python 3 required." from orchard_group_hash import group_hash from orchard_pallas import Fp, Scalar from orchard_sinsemilla import sinsemilla_hash_to_point -from sapling_utils import i2lebsp +from utils import i2lebsp # Commitment schemes used in Orchard https://zips.z.cash/protocol/nu5.pdf#concretecommit diff --git a/orchard_group_hash.py b/orchard_group_hash.py index 5ef0c9e..153671f 100644 --- a/orchard_group_hash.py +++ b/orchard_group_hash.py @@ -8,7 +8,7 @@ import orchard_iso_pallas from pyblake2 import blake2b from orchard_pallas import Fp, p, q, PALLAS_B, Point from orchard_iso_pallas import PALLAS_ISO_B, PALLAS_ISO_A -from sapling_utils import i2beosp, cldiv, beos2ip, i2leosp, lebs2ip +from utils import i2beosp, cldiv, beos2ip, i2leosp, lebs2ip from tv_output import render_args, render_tv from tv_rand import Rand diff --git a/orchard_map_to_curve.py b/orchard_map_to_curve.py index 4d43dd1..840ed9f 100755 --- a/orchard_map_to_curve.py +++ b/orchard_map_to_curve.py @@ -3,7 +3,7 @@ from orchard_group_hash import map_to_curve_simple_swu from orchard_iso_pallas import Point as IsoPoint from orchard_pallas import Fp -from sapling_utils import leos2ip +from utils import leos2ip from tv_output import render_args, render_tv from tv_rand import Rand diff --git a/orchard_merkle_tree.py b/orchard_merkle_tree.py index 2cd8fe0..ac3b5fb 100644 --- a/orchard_merkle_tree.py +++ b/orchard_merkle_tree.py @@ -6,7 +6,7 @@ from binascii import unhexlify from orchard_pallas import Fp from orchard_sinsemilla import sinsemilla_hash -from sapling_utils import i2lebsp, leos2bsp +from utils import i2lebsp, leos2bsp # https://zips.z.cash/protocol/nu5.pdf#constants MERKLE_DEPTH = 32 diff --git a/orchard_pallas.py b/orchard_pallas.py index d060d76..df13535 100644 --- a/orchard_pallas.py +++ b/orchard_pallas.py @@ -3,7 +3,7 @@ import sys; assert sys.version_info[0] >= 3, "Python 3 required." from sapling_jubjub import FieldElement -from sapling_utils import leos2ip +from utils import leos2ip p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001 q = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001 diff --git a/orchard_poseidon.py b/orchard_poseidon.py index c091f97..a9ed9ba 100644 --- a/orchard_poseidon.py +++ b/orchard_poseidon.py @@ -1,6 +1,6 @@ from orchard_pallas import Fp import numpy as np -from sapling_utils import leos2ip +from utils import leos2ip from tv_output import render_args, render_tv from tv_rand import Rand diff --git a/orchard_poseidon_hash.py b/orchard_poseidon_hash.py index e415632..fc238d1 100644 --- a/orchard_poseidon_hash.py +++ b/orchard_poseidon_hash.py @@ -1,6 +1,6 @@ from orchard_pallas import Fp from orchard_poseidon import perm -from sapling_utils import leos2ip +from utils import leos2ip from tv_output import render_args, render_tv from tv_rand import Rand diff --git a/orchard_sinsemilla.py b/orchard_sinsemilla.py index 2662272..920be76 100755 --- a/orchard_sinsemilla.py +++ b/orchard_sinsemilla.py @@ -6,7 +6,7 @@ import math import orchard_iso_pallas from orchard_pallas import Fp, Point -from sapling_utils import cldiv, lebs2ip, i2leosp +from utils import cldiv, lebs2ip, i2leosp from orchard_group_hash import group_hash from tv_output import render_args, render_tv from tv_rand import Rand diff --git a/sapling_generators.py b/sapling_generators.py index 3b22097..e4af823 100644 --- a/sapling_generators.py +++ b/sapling_generators.py @@ -5,7 +5,7 @@ from pyblake2 import blake2s from sapling_jubjub import Point, JUBJUB_COFACTOR from tv_output import render_args, render_tv -from sapling_utils import i2leosp +from utils import i2leosp # First 64 bytes of the BLAKE2s input during group hash. # This is chosen to be some random string that we couldn't have diff --git a/sapling_jubjub.py b/sapling_jubjub.py index db4442f..7a0a40f 100644 --- a/sapling_jubjub.py +++ b/sapling_jubjub.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import sys; assert sys.version_info[0] >= 3, "Python 3 required." -from sapling_utils import i2lebsp, leos2ip, i2leosp +from utils import i2lebsp, leos2ip, i2leosp q_j = 52435875175126190479447740508185965837690552500527637822603658699938581184513 r_j = 6554484396890773809930967563523245729705921265872317281365359162392183254199 diff --git a/sapling_key_components.py b/sapling_key_components.py index 75cce51..e23eef4 100644 --- a/sapling_key_components.py +++ b/sapling_key_components.py @@ -7,7 +7,7 @@ from sapling_generators import PROVING_KEY_BASE, SPENDING_KEY_BASE, group_hash from sapling_jubjub import Fr from sapling_merkle_tree import MERKLE_DEPTH from sapling_notes import note_commit, note_nullifier -from sapling_utils import leos2bsp, leos2ip +from utils import leos2bsp, leos2ip from tv_output import render_args, render_tv # diff --git a/sapling_merkle_tree.py b/sapling_merkle_tree.py index 6d7b7f7..7cd047b 100644 --- a/sapling_merkle_tree.py +++ b/sapling_merkle_tree.py @@ -4,7 +4,7 @@ import sys; assert sys.version_info[0] >= 3, "Python 3 required." from binascii import unhexlify from sapling_pedersen import pedersen_hash -from sapling_utils import i2lebsp, leos2bsp +from utils import i2lebsp, leos2bsp MERKLE_DEPTH = 32 diff --git a/sapling_note_encryption.py b/sapling_note_encryption.py index 736b652..d6a522d 100644 --- a/sapling_note_encryption.py +++ b/sapling_note_encryption.py @@ -10,7 +10,7 @@ from sapling_generators import VALUE_COMMITMENT_VALUE_BASE, VALUE_COMMITMENT_RAN from sapling_jubjub import Fr, JUBJUB_COFACTOR from sapling_key_components import SpendingKey, diversify_hash from sapling_notes import note_commit -from sapling_utils import leos2bsp, leos2ip +from utils import leos2bsp, leos2ip from tv_output import render_args, render_tv diff --git a/sapling_notes.py b/sapling_notes.py index aa27258..c4e0dc2 100644 --- a/sapling_notes.py +++ b/sapling_notes.py @@ -7,7 +7,7 @@ from sapling_pedersen import ( mixing_pedersen_hash, windowed_pedersen_commitment, ) -from sapling_utils import i2lebsp +from utils import i2lebsp def note_commit(rcm, g_d, pk_d, v): return windowed_pedersen_commitment(rcm, [1] * 6 + i2lebsp(64, v) + g_d + pk_d) diff --git a/sapling_pedersen.py b/sapling_pedersen.py index f782efe..649ed46 100644 --- a/sapling_pedersen.py +++ b/sapling_pedersen.py @@ -7,7 +7,7 @@ from sapling_generators import ( WINDOWED_PEDERSEN_RANDOMNESS_BASE, ) from sapling_jubjub import Fr, Point -from sapling_utils import cldiv, i2leosp +from utils import cldiv, i2leosp # diff --git a/sapling_signatures.py b/sapling_signatures.py index f774935..f2bc335 100644 --- a/sapling_signatures.py +++ b/sapling_signatures.py @@ -7,7 +7,7 @@ from pyblake2 import blake2b from sapling_generators import SPENDING_KEY_BASE from sapling_jubjub import Fr, Point, r_j from sapling_key_components import to_scalar -from sapling_utils import cldiv, leos2ip +from utils import cldiv, leos2ip from tv_output import render_args, render_tv diff --git a/sapling_zip32.py b/sapling_zip32.py index 8fcf4d7..63beb94 100644 --- a/sapling_zip32.py +++ b/sapling_zip32.py @@ -5,7 +5,7 @@ from pyblake2 import blake2b from sapling_key_components import to_scalar, prf_expand, diversify_hash, DerivedAkNk, DerivedIvk from sapling_generators import SPENDING_KEY_BASE, PROVING_KEY_BASE -from sapling_utils import i2leosp, i2lebsp, lebs2osp +from utils import i2leosp, i2lebsp, lebs2osp from ff1 import ff1_aes256_encrypt from tv_output import render_args, render_tv, option, Some diff --git a/transaction.py b/transaction.py index aa3ca64..19aba90 100644 --- a/transaction.py +++ b/transaction.py @@ -3,7 +3,7 @@ import struct from sapling_generators import find_group_hash, SPENDING_KEY_BASE from sapling_jubjub import Fq, Point -from sapling_utils import leos2ip +from utils import leos2ip from zc_utils import write_compact_size MAX_MONEY = 21000000 * 100000000 diff --git a/sapling_utils.py b/utils.py similarity index 100% rename from sapling_utils.py rename to utils.py From 29c40dc885e89523bb9f3e243735afb6c55db717 Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Thu, 6 May 2021 15:54:33 +0100 Subject: [PATCH 06/10] orchard_commitments.py and orchard_group_hash.py: type fixes. Signed-off-by: Daira Hopwood --- orchard_commitments.py | 17 +++++++++-------- orchard_group_hash.py | 4 +++- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/orchard_commitments.py b/orchard_commitments.py index f60e35d..494a0c8 100644 --- a/orchard_commitments.py +++ b/orchard_commitments.py @@ -23,8 +23,9 @@ def rcv_trapdoor(randbytes): # https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit def sinsemilla_commit(r: Scalar, D, M): + assert isinstance(r, Scalar) return sinsemilla_hash_to_point(D + b"-M", M).checked_incomplete_add( - r * group_hash(D + b"-r", "") + group_hash(D + b"-r", b"") * r ) def sinsemilla_short_commit(r: Scalar, D, M): @@ -34,20 +35,20 @@ def sinsemilla_short_commit(r: Scalar, D, M): def note_commit(rcm, g_d, pk_d, v, rho, psi): return sinsemilla_commit( rcm, - "z.cash: Orchard-NoteCommit", - g_d + pk_d + i2lebsp(64, v) + i2lebsp(L_ORCHARD_BASE, rho) + i2lebsp(L_ORCHARD_BASE, psi) + b"z.cash: Orchard-NoteCommit", + g_d + pk_d + i2lebsp(64, v) + i2lebsp(L_ORCHARD_BASE, rho.s) + i2lebsp(L_ORCHARD_BASE, psi.s) ) def rcm_trapdoor(randbytes): return Scalar.random(randbytes) # https://zips.z.cash/protocol/nu5.pdf#concreteorchardnotecommit -def commit_ivk(rivk, ak, nk): - return sinsemilla_short_commit( +def commit_ivk(rivk: Scalar, ak: Fp, nk: Fp): + return Scalar(sinsemilla_short_commit( rivk, - "z.cash: Orchard-CommitIvk", - i2lebsp(L_ORCHARD_BASE, ak) + i2lebsp(L_ORCHARD_BASE, nk) - ) + b"z.cash: Orchard-CommitIvk", + i2lebsp(L_ORCHARD_BASE, ak.s) + i2lebsp(L_ORCHARD_BASE, nk.s) + ).s) def rivk_trapdoor(randbytes): return Scalar.random(randbytes) diff --git a/orchard_group_hash.py b/orchard_group_hash.py index 153671f..6202f27 100644 --- a/orchard_group_hash.py +++ b/orchard_group_hash.py @@ -16,7 +16,9 @@ from tv_rand import Rand def sxor(s1,s2): return bytes([a ^ b for a,b in zip(s1,s2)]) -def expand_message_xmd(msg, dst, len_in_bytes): +def expand_message_xmd(msg: bytes, dst: bytes, len_in_bytes: int): + assert isinstance(msg, bytes) + assert isinstance(dst, bytes) assert len(dst) <= 255 b_in_bytes = 64 # hash function output size From af040174070b903e89750b0826effefa4ae663c1 Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Thu, 6 May 2021 15:54:59 +0100 Subject: [PATCH 07/10] orchard_pallas.py: add constants for Scalar. Signed-off-by: Daira Hopwood --- orchard_pallas.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/orchard_pallas.py b/orchard_pallas.py index df13535..fd35336 100644 --- a/orchard_pallas.py +++ b/orchard_pallas.py @@ -109,16 +109,17 @@ class Scalar(FieldElement): pass -Fp.ZERO = Fp(0) -Fp.ONE = Fp(1) -Fp.MINUS_ONE = Fp(-1) +for F in (Fp, Scalar): + F.ZERO = F(0) + F.ONE = F(1) + F.MINUS_ONE = F(-1) -assert Fp.ZERO + Fp.ZERO == Fp.ZERO -assert Fp.ZERO + Fp.ONE == Fp.ONE -assert Fp.ONE + Fp.ZERO == Fp.ONE -assert Fp.ZERO - Fp.ONE == Fp.MINUS_ONE -assert Fp.ZERO * Fp.ONE == Fp.ZERO -assert Fp.ONE * Fp.ZERO == Fp.ZERO + assert F.ZERO + F.ZERO == F.ZERO + assert F.ZERO + F.ONE == F.ONE + assert F.ONE + F.ZERO == F.ONE + assert F.ZERO - F.ONE == F.MINUS_ONE + assert F.ZERO * F.ONE == F.ZERO + assert F.ONE * F.ZERO == F.ZERO # From fb08eeeb390eb2f458baa370440d96e0df93ddfc Mon Sep 17 00:00:00 2001 From: ying tong Date: Fri, 7 May 2021 11:54:19 +0800 Subject: [PATCH 08/10] Pass rand instead of randbytes Co-authored-by: str4d --- orchard_commitments.py | 16 +++++++++------- orchard_pallas.py | 8 ++++---- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/orchard_commitments.py b/orchard_commitments.py index 494a0c8..713d622 100644 --- a/orchard_commitments.py +++ b/orchard_commitments.py @@ -18,8 +18,8 @@ def homomorphic_pedersen_commitment(rcv: Scalar, D, v: Scalar): def value_commit(rcv: Scalar, v: Scalar): return homomorphic_pedersen_commitment(rcv, b"z.cash:Orchard-cv", v) -def rcv_trapdoor(randbytes): - return Scalar.random(randbytes) +def rcv_trapdoor(rand): + return Scalar.random(rand) # https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit def sinsemilla_commit(r: Scalar, D, M): @@ -39,8 +39,8 @@ def note_commit(rcm, g_d, pk_d, v, rho, psi): g_d + pk_d + i2lebsp(64, v) + i2lebsp(L_ORCHARD_BASE, rho.s) + i2lebsp(L_ORCHARD_BASE, psi.s) ) -def rcm_trapdoor(randbytes): - return Scalar.random(randbytes) +def rcm_trapdoor(rand): + return Scalar.random(rand) # https://zips.z.cash/protocol/nu5.pdf#concreteorchardnotecommit def commit_ivk(rivk: Scalar, ak: Fp, nk: Fp): @@ -50,12 +50,13 @@ def commit_ivk(rivk: Scalar, ak: Fp, nk: Fp): i2lebsp(L_ORCHARD_BASE, ak.s) + i2lebsp(L_ORCHARD_BASE, nk.s) ).s) -def rivk_trapdoor(randbytes): - return Scalar.random(randbytes) +def rivk_trapdoor(rand): + return Scalar.random(rand) # Test consistency of ValueCommit^{Orchard} with precomputed generators def test_value_commit(): from random import Random + from tv_rand import Rand from orchard_generators import VALUE_COMMITMENT_RANDOMNESS_BASE, VALUE_COMMITMENT_VALUE_BASE rng = Random(0xabad533d) @@ -64,8 +65,9 @@ def test_value_commit(): while len(ret) < l: ret.append(rng.randrange(0, 256)) return bytes(ret) + rand = Rand(randbytes) - rcv = rcv_trapdoor(randbytes) + rcv = rcv_trapdoor(rand) v = Scalar(100000000) assert value_commit(rcv, v) == VALUE_COMMITMENT_RANDOMNESS_BASE * rcv + VALUE_COMMITMENT_VALUE_BASE * v diff --git a/orchard_pallas.py b/orchard_pallas.py index fd35336..3ebe97d 100644 --- a/orchard_pallas.py +++ b/orchard_pallas.py @@ -31,10 +31,10 @@ class Fp(FieldElement): def from_bytes(buf): return Fp(leos2ip(buf), strict=True) - def random(randbytes): + def random(rand): while True: try: - return Fp(leos2ip(randbytes(32)), strict=True) + return Fp(leos2ip(rand.b(32)), strict=True) except ValueError: pass @@ -101,10 +101,10 @@ class Scalar(FieldElement): def from_bytes(buf): return Scalar(leos2ip(buf), strict=True) - def random(randbytes): + def random(rand): while True: try: - return Scalar(leos2ip(randbytes(32)), strict=True) + return Scalar(leos2ip(rand.b(32)), strict=True) except ValueError: pass From e0b8fd639a386a08acf162437736186ee8c56203 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Tue, 11 May 2021 20:06:32 +0800 Subject: [PATCH 09/10] Return base field element from commit_ivk() Co-authored-by: Jack Grigg --- orchard_commitments.py | 4 ++-- orchard_pallas.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/orchard_commitments.py b/orchard_commitments.py index 713d622..41a56a2 100644 --- a/orchard_commitments.py +++ b/orchard_commitments.py @@ -44,11 +44,11 @@ def rcm_trapdoor(rand): # https://zips.z.cash/protocol/nu5.pdf#concreteorchardnotecommit def commit_ivk(rivk: Scalar, ak: Fp, nk: Fp): - return Scalar(sinsemilla_short_commit( + return sinsemilla_short_commit( rivk, b"z.cash: Orchard-CommitIvk", i2lebsp(L_ORCHARD_BASE, ak.s) + i2lebsp(L_ORCHARD_BASE, nk.s) - ).s) + ) def rivk_trapdoor(rand): return Scalar.random(rand) diff --git a/orchard_pallas.py b/orchard_pallas.py index 3ebe97d..0ed16e2 100644 --- a/orchard_pallas.py +++ b/orchard_pallas.py @@ -229,6 +229,7 @@ class Point(object): return self.x def __mul__(self, s): + assert isinstance(s, Scalar) s = format(s.s, '0256b') ret = self.ZERO for c in s: From 99e71840386404f6f5d04a866ccf33e9a6202fb1 Mon Sep 17 00:00:00 2001 From: ying tong Date: Thu, 13 May 2021 12:57:07 +0800 Subject: [PATCH 10/10] Remove whitespace in note_commit and commit_ivk personalisations Co-authored-by: str4d --- orchard_commitments.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/orchard_commitments.py b/orchard_commitments.py index 41a56a2..8fcde60 100644 --- a/orchard_commitments.py +++ b/orchard_commitments.py @@ -35,7 +35,7 @@ def sinsemilla_short_commit(r: Scalar, D, M): def note_commit(rcm, g_d, pk_d, v, rho, psi): return sinsemilla_commit( rcm, - b"z.cash: Orchard-NoteCommit", + b"z.cash:Orchard-NoteCommit", g_d + pk_d + i2lebsp(64, v) + i2lebsp(L_ORCHARD_BASE, rho.s) + i2lebsp(L_ORCHARD_BASE, psi.s) ) @@ -46,7 +46,7 @@ def rcm_trapdoor(rand): def commit_ivk(rivk: Scalar, ak: Fp, nk: Fp): return sinsemilla_short_commit( rivk, - b"z.cash: Orchard-CommitIvk", + b"z.cash:Orchard-CommitIvk", i2lebsp(L_ORCHARD_BASE, ak.s) + i2lebsp(L_ORCHARD_BASE, nk.s) )