Some cleanup and move group_hash into its own file

This commit is contained in:
Taylor Hornby 2021-04-26 18:28:43 -06:00
parent a2bf6c5a04
commit 027b362773
2 changed files with 137 additions and 128 deletions

134
orchard_group_hash.py Executable file
View File

@ -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()

View File

@ -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