From 2f152758ba21b64998883481555d48874e9defe5 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 18 May 2018 15:23:08 +1200 Subject: [PATCH] Implement RedJubjub --- sapling_key_components.py | 5 +- sapling_signatures.py | 143 ++++++++++++++++++++++++++++++++++++++ sapling_utils.py | 4 ++ 3 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 sapling_signatures.py diff --git a/sapling_key_components.py b/sapling_key_components.py index 14a06e5..1fe0f8a 100644 --- a/sapling_key_components.py +++ b/sapling_key_components.py @@ -4,6 +4,7 @@ from pyblake2 import blake2b, blake2s from sapling_generators import PROVING_KEY_BASE, SPENDING_KEY_BASE, group_hash from sapling_jubjub import Fr +from sapling_utils import chunk # # PRFs and hashes @@ -80,10 +81,6 @@ class SpendingKey(object): return group_hash(b'Zcash_gd', self.default_d()) * self.ivk() -def chunk(h): - h = str(h, 'utf-8') - return '0x' + ', 0x'.join([h[i:i+2] for i in range(0, len(h), 2)]) - def main(): print(''' struct TestVector { diff --git a/sapling_signatures.py b/sapling_signatures.py new file mode 100644 index 0000000..ea1d1eb --- /dev/null +++ b/sapling_signatures.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +from binascii import hexlify +import os +from pyblake2 import blake2b + +from sapling_generators import SPENDING_KEY_BASE +from sapling_jubjub import Fr, Point +from sapling_utils import cldiv, chunk, leos2ip + + +def H(x): + digest = blake2b(person=b'Zcash_RedJubjubH') + digest.update(x) + return digest.digest() + +def h_star(B): + return Fr(leos2ip(H(B))) + + +class RedJubjub(object): + l_G = 256 # l_J + l_H = 512 + Public = Point + Private = Fr + Random = Fr + + def __init__(self, P_g, random=os.urandom): + self.P_g = P_g + self._random = random + + def gen_private(self): + return self.Private.from_bytes(self._random(64)) + + def derive_public(self, sk): + return self.P_g * sk + + def gen_random(self): + T = self._random((self.l_H + 128) // 8) + return h_star(T) + + @staticmethod + def randomize_private(sk, alpha): + return sk + alpha + + def randomize_public(self, vk, alpha): + return vk + self.P_g * alpha + + def sign(self, sk, M): + T = self._random((self.l_H + 128) // 8) + r = h_star(T + M) + R = self.P_g * r + Rbar = bytes(R) + S = r + h_star(Rbar + M) * sk + Sbar = bytes(S) # TODO: bitlength(r_j) + return Rbar + Sbar + + def verify(self, vk, M, sig): + mid = cldiv(self.l_G, 8) + (Rbar, Sbar) = (sig[:mid], sig[mid:]) # TODO: bitlength(r_j) + R = Point.from_bytes(Rbar) + S = Fr.from_bytes(Sbar) + c = h_star(Rbar + M) + return R and S.s == leos2ip(Sbar) and self.P_g * S == R + vk * c + + +def main(): + from random import Random + rng = Random(0xabad533d) + def randbytes(l): + ret = [] + while len(ret) < l: + ret.append(rng.randrange(0, 256)) + return bytes(ret) + rj = RedJubjub(SPENDING_KEY_BASE, randbytes) + + print(''' + struct TestVector { + sk: [u8; 32], + vk: [u8; 32], + alpha: [u8; 32], + rsk: [u8; 32], + rvk: [u8; 32], + m: [u8; 32], + sig: [u8; 32], + rsig: [u8; 32], + }; + + let test_vectors = vec![''') + for i in range(0, 10): + sk = rj.gen_private() + vk = rj.derive_public(sk) + alpha = rj.gen_random() + rsk = rj.randomize_private(sk, alpha) + rvk = rj.randomize_public(vk, alpha) + + M = bytes([i] * 32) + sig = rj.sign(sk, M) + rsig = rj.sign(rsk, M) + assert rj.verify(vk, M, sig) + assert rj.verify(rvk, M, rsig) + assert not rj.verify(vk, M, rsig) + assert not rj.verify(rvk, M, sig) + + print(''' TestVector { + sk: [ + %s + ], + vk: [ + %s + ], + alpha: [ + %s + ], + rsk: [ + %s + ], + rvk: [ + %s + ], + m: [ + %s + ], + sig: [ + %s + ], + rsig: [ + %s + ], + },''' % ( + chunk(hexlify(bytes(sk))), + chunk(hexlify(bytes(vk))), + chunk(hexlify(bytes(alpha))), + chunk(hexlify(bytes(rsk))), + chunk(hexlify(bytes(rvk))), + chunk(hexlify(M)), + chunk(hexlify(sig)), + chunk(hexlify(rsig)), + )) + print(' ];') + + +if __name__ == '__main__': + main() diff --git a/sapling_utils.py b/sapling_utils.py index dea7215..7692155 100644 --- a/sapling_utils.py +++ b/sapling_utils.py @@ -3,6 +3,10 @@ def cldiv(n, divisor): return (n + (divisor - 1)) // divisor +def chunk(h): + h = str(h, 'utf-8') + return '0x' + ', 0x'.join([h[i:i+2] for i in range(0, len(h), 2)]) + def i2lebsp(l, x): return [int(c) for c in format(x, '0%sb' % l)[::-1]]