From 027b3627736de87624b9167cafe19ff66e2ec321 Mon Sep 17 00:00:00 2001 From: Taylor Hornby Date: Mon, 26 Apr 2021 18:28:43 -0600 Subject: [PATCH] Some cleanup and move group_hash into its own file --- orchard_group_hash.py | 134 ++++++++++++++++++++++++++++++++++++++++++ orchard_sinsemilla.py | 131 +---------------------------------------- 2 files changed, 137 insertions(+), 128 deletions(-) create mode 100755 orchard_group_hash.py diff --git a/orchard_group_hash.py b/orchard_group_hash.py new file mode 100755 index 0000000..c1ea902 --- /dev/null +++ b/orchard_group_hash.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +import sys; assert sys.version_info[0] >= 3, "Python 3 required." + +import math + +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 + +# https://stackoverflow.com/questions/2612720/how-to-do-bitwise-exclusive-or-of-two-strings-in-python +def sxor(s1,s2): + return bytes([a ^ b for a,b in zip(s1,s2)]) + +def expand_message_xmd(msg, dst, len_in_bytes): + assert len(dst) <= 255 + + b_in_bytes = 64 # hash function output size + r_in_bytes = 128 + + ell = cldiv(len_in_bytes, b_in_bytes) + + assert ell <= 255 + + dst_prime = dst + i2beosp(8, len(dst)) + z_pad = b"\x00" * r_in_bytes + l_i_b_str = i2beosp(16, len_in_bytes) + msg_prime = z_pad + msg + l_i_b_str + i2beosp(8, 0) + dst_prime + + b = [] + + b0_ctx = blake2b(digest_size=b_in_bytes, person=i2beosp(128,0)) + b0_ctx.update(msg_prime) + b.append(b0_ctx.digest()) + assert len(b[0]) == b_in_bytes + + b1_ctx = blake2b(digest_size=b_in_bytes, person=i2beosp(128,0)) + b1_ctx.update(b[0] + i2beosp(8, 1) + dst_prime) + b.append(b1_ctx.digest()) + assert len(b[1]) == b_in_bytes + + for i in range(2, ell + 1): + bi_input = sxor(b[0], b[i-1]) + + assert len(bi_input) == b_in_bytes + + bi_input += i2beosp(8, i) + dst_prime + + bi_ctx = blake2b(digest_size=b_in_bytes, person=i2beosp(128,0)) + bi_ctx.update(bi_input) + + b.append(bi_ctx.digest()) + assert len(b[i]) == b_in_bytes + + return b''.join(b[1:])[0:len_in_bytes] + +def hash_to_field(msg, dst): + k = 256 + count = 2 + m = 1 + + L = cldiv(math.ceil(math.log2(p)) + k, 8) + assert L == 512/8 + + len_in_bytes = count * m * L + uniform_bytes = expand_message_xmd(msg, dst, len_in_bytes) + + elements = [] + for i in range(0, count): + for j in range(0, m): + elm_offset = L * (j + i * m) + tv = uniform_bytes[elm_offset:elm_offset+L] + elements.append(Fp(beos2ip(tv), False)) + + assert len(elements) == count + + return elements + +def map_to_curve_simple_swu(u): + # The notation below follows Appendix F.2 of the Internet Draft + zero = Fp(0) + assert zero.inv() == Fp(0) + + A = PALLAS_ISO_A + B = PALLAS_ISO_B + Z = Fp(-13, False) + c1 = -B / A + c2 = Fp(-1) / Z + + tv1 = Z * u.exp(2) + tv2 = tv1.exp(2) + x1 = tv1 + tv2 + + x1 = x1.inv() + e1 = x1 == Fp(0) + x1 = x1 + Fp(1) + + if e1: + x1 = c2 + else: + x1 = x1 + + x1 = x1 * c1 # x1 = (-B / A) * (1 + (1 / (Z^2 * u^4 + Z * u^2))) + gx1 = x1.exp(2) + gx1 = gx1 + A + gx1 = gx1 * x1 + gx1 = gx1 + B # gx1 = g(x1) = x1^3 + A * x1 + B + x2 = tv1 * x1 # x2 = Z * u^2 * x1 + tv2 = tv1 * tv2 + gx2 = gx1 * tv2 # gx2 = (Z * u^2)^3 * gx1 + + e2 = (gx1.sqrt() is not None) + + x = x1 if e2 else x2 # If is_square(gx1), x = x1, else x = x2 + yy = gx1 if e2 else gx2 # If is_square(gx1), yy = gx1, else yy = gx2 + y = yy.sqrt() + + e3 = u.sgn0() == y.sgn0() + + y = y if e3 else -y #y = CMOV(-y, y, e3) + + return orchard_iso_pallas.Point(x, y) + +def group_hash(d, m): + dst = d + b"-" + b"pallas" + b"_XMD:BLAKE2b_SSWU_RO_" + + elems = hash_to_field(m, dst) + assert len(elems) == 2 + + q = [map_to_curve_simple_swu(elems[0]), map_to_curve_simple_swu(elems[1]) ] + + return (q[0] + q[1]).iso_map() \ No newline at end of file diff --git a/orchard_sinsemilla.py b/orchard_sinsemilla.py index e76224d..6a70498 100755 --- a/orchard_sinsemilla.py +++ b/orchard_sinsemilla.py @@ -5,135 +5,10 @@ import math 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 binascii import hexlify +from orchard_pallas import Fp, Point +from sapling_utils import cldiv, lebs2ip, i2leosp from bitstring import BitArray - -# https://stackoverflow.com/questions/2612720/how-to-do-bitwise-exclusive-or-of-two-strings-in-python -def sxor(s1,s2): - return bytes([a ^ b for a,b in zip(s1,s2)]) - -def expand_message_xmd(msg, dst, len_in_bytes): - assert len(dst) <= 255 - - b_in_bytes = 64 # hash function output size - r_in_bytes = 128 - - ell = cldiv(len_in_bytes, b_in_bytes) - - assert ell <= 255 - - dst_prime = dst + i2beosp(8, len(dst)) - z_pad = b"\x00" * r_in_bytes - l_i_b_str = i2beosp(16, len_in_bytes) - msg_prime = z_pad + msg + l_i_b_str + i2beosp(8, 0) + dst_prime - - b = [] - - b0_ctx = blake2b(digest_size=b_in_bytes, person=i2beosp(128,0)) - b0_ctx.update(msg_prime) - b.append(b0_ctx.digest()) - assert len(b[0]) == b_in_bytes - - b1_ctx = blake2b(digest_size=b_in_bytes, person=i2beosp(128,0)) - b1_ctx.update(b[0] + i2beosp(8, 1) + dst_prime) - b.append(b1_ctx.digest()) - assert len(b[1]) == b_in_bytes - - for i in range(2, ell + 1): - bi_input = sxor(b[0], b[i-1]) - - assert len(bi_input) == b_in_bytes - - bi_input += i2beosp(8, i) + dst_prime - - bi_ctx = blake2b(digest_size=b_in_bytes, person=i2beosp(128,0)) - bi_ctx.update(bi_input) - - b.append(bi_ctx.digest()) - assert len(b[i]) == b_in_bytes - - return b''.join(b[1:])[0:len_in_bytes] - -def hash_to_field(msg, dst): - k = 256 - count = 2 - m = 1 - - L = cldiv(math.ceil(math.log2(p)) + k, 8) - assert L == 512/8 - - len_in_bytes = count * m * L - uniform_bytes = expand_message_xmd(msg, dst, len_in_bytes) - - elements = [] - for i in range(0, count): - for j in range(0, m): - elm_offset = L * (j + i * m) - tv = uniform_bytes[elm_offset:elm_offset+L] - elements.append(Fp(beos2ip(tv), False)) - - assert len(elements) == count - - return elements - -def map_to_curve_simple_swu(u): - zero = Fp(0) - assert zero.inv() == Fp(0) - - A = PALLAS_ISO_A - B = PALLAS_ISO_B - Z = Fp(-13, False) - c1 = -B / A - c2 = Fp(-1) / Z - - tv1 = Z * u.exp(2) - tv2 = tv1.exp(2) - x1 = tv1 + tv2 - - x1 = x1.inv() - e1 = x1 == Fp(0) - x1 = x1 + Fp(1) - - if e1: - x1 = c2 - else: - x1 = x1 - - x1 = x1 * c1 # x1 = (-B / A) * (1 + (1 / (Z^2 * u^4 + Z * u^2))) - gx1 = x1.exp(2) - gx1 = gx1 + A - gx1 = gx1 * x1 - gx1 = gx1 + B # gx1 = g(x1) = x1^3 + A * x1 + B - x2 = tv1 * x1 # x2 = Z * u^2 * x1 - tv2 = tv1 * tv2 - gx2 = gx1 * tv2 # gx2 = (Z * u^2)^3 * gx1 - - e2 = (gx1.sqrt() is not None) - - x = x1 if e2 else x2 # If is_square(gx1), x = x1, else x = x2 - yy = gx1 if e2 else gx2 # If is_square(gx1), yy = gx1, else yy = gx2 - y = yy.sqrt() - - e3 = u.sgn0() == y.sgn0() - - y = y if e3 else -y #y = CMOV(-y, y, e3) - - return orchard_iso_pallas.Point(x, y) - - -def group_hash(d, m): - dst = d + b"-" + b"pallas" + b"_XMD:BLAKE2b_SSWU_RO_" - - elems = hash_to_field(m, dst) - assert len(elems) == 2 - - q = [map_to_curve_simple_swu(elems[0]), map_to_curve_simple_swu(elems[1]) ] - - return (q[0] + q[1]).iso_map() +from orchard_group_hash import group_hash SINSEMILLA_K = 10