833 lines
31 KiB
Python
833 lines
31 KiB
Python
import field
|
|
import ec
|
|
import ec_ops
|
|
import ccgen
|
|
import constants
|
|
|
|
import argparse
|
|
import pathlib
|
|
|
|
import random
|
|
|
|
import math
|
|
import hashlib
|
|
|
|
|
|
class CRepr:
|
|
width = 64
|
|
def repr_list(self, lst):
|
|
return '{' + ','.join(lst) + '}'
|
|
|
|
def fp(self, n):
|
|
b = []
|
|
while n > 0:
|
|
b.append(n & ((
|
|
~(
|
|
(0xFFFFFFFFFFFFFFFF << self.width) & 0xFFFFFFFFFFFFFFFF
|
|
)
|
|
) & 0xFFFFFFFFFFFFFFFF))
|
|
n >>= self.width
|
|
return self.repr_list(map(str, b))
|
|
|
|
def fp2(self, x):
|
|
a, b = x
|
|
return self.repr_list([self.fp(a), self.fp(b)])
|
|
|
|
def fp_array(self, ns):
|
|
return self.repr_list(map(self.fp, ns))
|
|
|
|
def fp2_array(self, ns):
|
|
return self.repr_list(map(self.fp2, ns))
|
|
|
|
def ec_component(self, c):
|
|
if type(c) is not tuple:
|
|
c = (c,)
|
|
return self.repr_list(map(self.fp, c))
|
|
|
|
def ec_point(self, p):
|
|
return self.repr_list(map(self.ec_component, p))
|
|
|
|
def ec_array(self, points):
|
|
return self.repr_list(map(self.ec_point, points))
|
|
|
|
def fp_constant(self, f):
|
|
return '''static constexpr FpConstant {name} {{
|
|
.bits = {bits},
|
|
.rexp = {rexp},
|
|
.pinv = {pinv}u,
|
|
.p = {p},
|
|
.p_minus_2 = {p_minus_2},
|
|
.pp = {pp},
|
|
.r = {r},
|
|
.r2 = {r2},
|
|
.adicity2 = {adicity2},
|
|
.generator={generator},
|
|
.inv2={inv2},
|
|
}};'''.format(
|
|
name=f.name,
|
|
bits=f.bits,
|
|
rexp=f.rexp,
|
|
pinv=f.pinv(f.width),
|
|
p=self.fp(f.p),
|
|
p_minus_2=self.fp(f.p - 2),
|
|
pp=self.fp(f.pp),
|
|
r=self.fp(f.r),
|
|
r2=self.fp(f.r2),
|
|
adicity2=f.adicity2,
|
|
generator=self.fp(f.generator),
|
|
inv2=self.fp(f.inv2),
|
|
)
|
|
|
|
@staticmethod
|
|
def fp2_constant(f):
|
|
return '''static constexpr Fp2Constant {name} {{
|
|
.alpha = {alpha},
|
|
}};'''.format(
|
|
name=f.name,
|
|
alpha=f.alpha,
|
|
)
|
|
|
|
# @staticmethod
|
|
def ec_constant(self, e):
|
|
return '''static constexpr ECConstant {name} {{
|
|
.a = {a},
|
|
.a_mont = {a_mont},
|
|
}};'''.format(
|
|
name=e.name,
|
|
a=e.a,
|
|
a_mont=self.fp(e.field.to_mont(e.a)),
|
|
)
|
|
|
|
@staticmethod
|
|
def ec2_constant(e):
|
|
assert e.a[1] == 0
|
|
return '''static constexpr ECConstant {name} {{
|
|
.a = {a},
|
|
}};'''.format(
|
|
name=e.name,
|
|
a=e.a[0],
|
|
)
|
|
|
|
# @staticmethod
|
|
def ecdsa_constant(self, ec):
|
|
k = math.ceil(math.log(ec.field.bits, 2))
|
|
n = 1 << k
|
|
p = ec.generator
|
|
|
|
sig_affine = [None] * (n)
|
|
for i in range(n):
|
|
sig_affine[i] = p
|
|
p = ec.affine_double(p)
|
|
|
|
return '''static constexpr ECDSAConstant G1_1_{name} {{
|
|
.K = {K},
|
|
.SIG_AFF = {SIG_AFF},
|
|
}};'''.format(
|
|
name=ec.name,
|
|
K=k,
|
|
SIG_AFF=self.ec_array(map(ec.to_mont, sig_affine)),
|
|
)
|
|
|
|
def generate_fp_test(out, name, f, k, width):
|
|
n = 1 << k
|
|
a = [random.randint(0, f.p - 1) for i in range(n)]
|
|
b = [random.randint(0, f.p - 1) for i in range(n)]
|
|
|
|
crepr = CRepr()
|
|
crepr.width = width
|
|
|
|
out.write('namespace {}_fp_test {{\n'.format(name))
|
|
out.write('static const size_t N = {};\n'.format(n))
|
|
out.write('static const uint64_t A[{}][MAX_LIMBS] = {};\n'.format(
|
|
n, crepr.fp_array(a)))
|
|
out.write('static const uint64_t B[{}][MAX_LIMBS] = {};\n'.format(
|
|
n, crepr.fp_array(b)))
|
|
out.write('static const uint64_t SUM[{}][MAX_LIMBS] = {};\n'.format(
|
|
n, crepr.fp_array([(a[i] + b[i]) % f.p for i in range(n)])))
|
|
out.write('static const uint64_t PROD[{}][MAX_LIMBS] = {};\n'.format(
|
|
n, crepr.fp_array([a[i] * b[i] % f.p for i in range(n)])))
|
|
# out.write('static const uint64_t PROD[{}][MAX_LIMBS] = {};\n'.format(
|
|
# n, crepr.fp_array([f.to_mont(a[i] * b[i] % f.p) for i in range(n)])))
|
|
out.write('}}\n\n'.format(name))
|
|
|
|
# pass test
|
|
# print(f.to_bytes(f.to_mont(a[0])))
|
|
# print(f.to_bytes(f.to_mont(b[0])))
|
|
# f.mont_mul(f.to_bytes(f.to_mont(a[0])), f.to_bytes(f.to_mont(b[0])), f.to_bytes(f.p))
|
|
|
|
def generate_ecdsa_test(out, f, ec, width):
|
|
# Set fixed seed for reproducible test constants
|
|
random.seed(42)
|
|
|
|
e = 0x10D51CB90C0C0522E94875A2BEA7AB72299EBE7192E64EFE0573B1C77110E5C9
|
|
priv_key = 0x128B2FA8BD433C6C068C8D803DFF79792A519A55171B1B650C23661D15897263
|
|
k = 0xE11F5909F947D5BE08C84A22CE9F7C338F7CF4A5B941B9268025495D7D433071
|
|
s = 0xE11F5909F947D5BE08C84A22CE9F7C338F7CF4A5B941B9268025495D7D433071
|
|
r = 0x23B20B796AAAFEAAA3F1592CB9B4A93D5A8D279843E1C57980E64E0ABC5F5B05
|
|
# r = 0x23B20B796AAAFEAAA3F1592CB9B4A93D5A8D279843E1C57980E64E0ABC5F5B96
|
|
key_x = 0xD5548C7825CBB56150A3506CD57464AF8A1AE0519DFAF3C58221DC810CAF28DD
|
|
key_y = 0x921073768FE3D59CE54E79A49445CF73FED23086537027264D168946D479533E
|
|
|
|
n = 3972
|
|
random_r = [random.randint(0, f.p - 1) for i in range(n)]
|
|
random_s = [random.randint(0, f.p - 1) for i in range(n)]
|
|
random_e = [random.randint(0, f.p - 1) for i in range(n)]
|
|
random_priv_key = [random.randint(0, f.p - 1) for i in range(n)]
|
|
random_k = [random.randint(0, f.p - 1) for i in range(n)]
|
|
|
|
# Generate valid curve points for RANDOM_KEY_X and RANDOM_KEY_Y
|
|
random_key_x = []
|
|
random_key_y = []
|
|
for i in range(n):
|
|
point = ec.random_element()
|
|
random_key_x.append(point[0])
|
|
random_key_y.append(point[1])
|
|
|
|
crepr = CRepr()
|
|
crepr.width = width
|
|
|
|
out.write('static const uint64_t E[MAX_LIMBS] = {};\n'.format(
|
|
crepr.fp(e)))
|
|
out.write('static const uint64_t PRIV_KEY[MAX_LIMBS] = {};\n'.format(
|
|
crepr.fp(priv_key)))
|
|
out.write('static const uint64_t K[MAX_LIMBS] = {};\n'.format(
|
|
crepr.fp(k)))
|
|
out.write('static const uint64_t S[MAX_LIMBS] = {};\n'.format(
|
|
crepr.fp(s)))
|
|
out.write('static const uint64_t R[MAX_LIMBS] = {};\n'.format(
|
|
crepr.fp(r)))
|
|
out.write('static const uint64_t KEY_X[MAX_LIMBS] = {};\n'.format(
|
|
crepr.fp(key_x)))
|
|
out.write('static const uint64_t KEY_Y[MAX_LIMBS] = {};\n'.format(
|
|
crepr.fp(key_y)))
|
|
out.write('static const uint64_t RANDOM_R[{}][MAX_LIMBS] = {};\n'.format(
|
|
n, crepr.fp_array(random_r)))
|
|
out.write('static const uint64_t RANDOM_S[{}][MAX_LIMBS] = {};\n'.format(
|
|
n, crepr.fp_array(random_s)))
|
|
out.write('static const uint64_t RANDOM_E[{}][MAX_LIMBS] = {};\n'.format(
|
|
n, crepr.fp_array(random_e)))
|
|
out.write('static const uint64_t RANDOM_PRIV_KEY[{}][MAX_LIMBS] = {};\n'.format(
|
|
n, crepr.fp_array(random_priv_key)))
|
|
out.write('static const uint64_t RANDOM_K[{}][MAX_LIMBS] = {};\n'.format(
|
|
n, crepr.fp_array(random_k)))
|
|
out.write('static const uint64_t RANDOM_KEY_X[{}][MAX_LIMBS] = {};\n'.format(
|
|
n, crepr.fp_array(random_key_x)))
|
|
out.write('static const uint64_t RANDOM_KEY_Y[{}][MAX_LIMBS] = {};\n'.format(
|
|
n, crepr.fp_array(random_key_y)))
|
|
|
|
def generate_ecdsa_fixed_test(out, f, ec, width):
|
|
"""
|
|
Generate test constants for fixed-point multiplication using the secp256k1 generator point G.
|
|
All test cases use the same base point (G), only the scalars vary.
|
|
This is appropriate for testing the fixed-point multiplication algorithm.
|
|
"""
|
|
# Set fixed seed for reproducible test constants
|
|
random.seed(43) # Different seed from unknown-point test
|
|
|
|
# Get the generator point
|
|
g_x, g_y = constants.SECP256K1_g1_generator
|
|
|
|
n = 3972 # Same number of test cases as unknown-point test
|
|
|
|
# Generate random scalars
|
|
random_s = [random.randint(0, f.p - 1) for i in range(n)]
|
|
|
|
# For fixed-point multiplication, all base points are the generator G
|
|
# (In a real fixed-point multiplication setup, these would be the same point,
|
|
# but we store them in arrays for compatibility with the existing test structure)
|
|
random_key_x = [g_x for i in range(n)]
|
|
random_key_y = [g_y for i in range(n)]
|
|
|
|
crepr = CRepr()
|
|
crepr.width = width
|
|
|
|
out.write('// ECDSA Fixed-Point Multiplication Test Constants\n')
|
|
out.write('// All base points are the secp256k1 generator G\n')
|
|
out.write('// Only scalars vary for each test case\n\n')
|
|
|
|
out.write('static const uint64_t RANDOM_S[{}][MAX_LIMBS] = {};\n'.format(
|
|
n, crepr.fp_array(random_s)))
|
|
out.write('static const uint64_t RANDOM_KEY_X[{}][MAX_LIMBS] = {};\n'.format(
|
|
n, crepr.fp_array(random_key_x)))
|
|
out.write('static const uint64_t RANDOM_KEY_Y[{}][MAX_LIMBS] = {};\n'.format(
|
|
n, crepr.fp_array(random_key_y)))
|
|
|
|
def generate_batch_add_test(out, curve, width, num_tests=10):
|
|
"""Generate test vectors for batch point addition"""
|
|
import random
|
|
random.seed(44) # Different seed from other batch tests
|
|
|
|
crepr = CRepr()
|
|
crepr.width = width
|
|
|
|
out.write('// Test vectors for batch point addition on secp256k1\n')
|
|
out.write('// Generated for correctness testing\n\n')
|
|
out.write(f'#define BATCH_ADD_NUM_TESTS {num_tests}\n\n')
|
|
|
|
points1_x = []
|
|
points1_y = []
|
|
points2_x = []
|
|
points2_y = []
|
|
results_x = []
|
|
results_y = []
|
|
|
|
for i in range(num_tests):
|
|
# Generate two random points
|
|
point1 = curve.random_element()
|
|
point2 = curve.random_element()
|
|
|
|
# Compute point addition using Python reference implementation
|
|
p1_jac = curve.to_jacobian(point1)
|
|
p2_jac = curve.to_jacobian(point2)
|
|
result_jac = curve.add_jacobian(p1_jac, p2_jac)
|
|
result = curve.get_xy(result_jac)
|
|
|
|
points1_x.append(point1[0])
|
|
points1_y.append(point1[1])
|
|
points2_x.append(point2[0])
|
|
points2_y.append(point2[1])
|
|
results_x.append(result[0])
|
|
results_y.append(result[1])
|
|
|
|
# Output as C arrays
|
|
out.write('static const uint64_t BATCH_ADD_POINTS1_X[][MAX_LIMBS] = {\n')
|
|
for px in points1_x:
|
|
out.write(f' {crepr.fp(px)},\n')
|
|
out.write('};\n\n')
|
|
|
|
out.write('static const uint64_t BATCH_ADD_POINTS1_Y[][MAX_LIMBS] = {\n')
|
|
for py in points1_y:
|
|
out.write(f' {crepr.fp(py)},\n')
|
|
out.write('};\n\n')
|
|
|
|
out.write('static const uint64_t BATCH_ADD_POINTS2_X[][MAX_LIMBS] = {\n')
|
|
for px in points2_x:
|
|
out.write(f' {crepr.fp(px)},\n')
|
|
out.write('};\n\n')
|
|
|
|
out.write('static const uint64_t BATCH_ADD_POINTS2_Y[][MAX_LIMBS] = {\n')
|
|
for py in points2_y:
|
|
out.write(f' {crepr.fp(py)},\n')
|
|
out.write('};\n\n')
|
|
|
|
out.write('static const uint64_t BATCH_ADD_EXPECTED_X[][MAX_LIMBS] = {\n')
|
|
for rx in results_x:
|
|
out.write(f' {crepr.fp(rx)},\n')
|
|
out.write('};\n\n')
|
|
|
|
out.write('static const uint64_t BATCH_ADD_EXPECTED_Y[][MAX_LIMBS] = {\n')
|
|
for ry in results_y:
|
|
out.write(f' {crepr.fp(ry)},\n')
|
|
out.write('};\n')
|
|
|
|
|
|
def generate_batch_fpmul_test(out, curve, width, num_tests=10):
|
|
"""Generate test vectors for batch fixed-point scalar multiplication"""
|
|
import random
|
|
random.seed(43) # Different seed from batch_pmul_test
|
|
|
|
crepr = CRepr()
|
|
crepr.width = width
|
|
|
|
out.write('// Test vectors for batch fixed-point scalar multiplication on secp256k1\n')
|
|
out.write('// All tests use the generator G as the fixed base point\n')
|
|
out.write('// Generated for correctness testing\n\n')
|
|
out.write(f'#define BATCH_FPMUL_NUM_TESTS {num_tests}\n\n')
|
|
|
|
# Use the generator as the fixed base point
|
|
base_point = curve.generator
|
|
|
|
out.write('// Fixed base point (generator G)\n')
|
|
out.write(f'static const uint64_t BATCH_FPMUL_BASE_POINT_X[MAX_LIMBS] = {crepr.fp(base_point[0])};\n')
|
|
out.write(f'static const uint64_t BATCH_FPMUL_BASE_POINT_Y[MAX_LIMBS] = {crepr.fp(base_point[1])};\n\n')
|
|
|
|
scalars = []
|
|
results_x = []
|
|
results_y = []
|
|
|
|
for i in range(num_tests):
|
|
# Generate random scalar (use smaller scalars for reasonable test execution time)
|
|
scalar = random.randint(1, 2**128)
|
|
|
|
# Compute scalar multiplication using Python reference implementation
|
|
base_jac = curve.to_jacobian(base_point)
|
|
result_jac = curve.multiply_jacobian(base_jac, scalar)
|
|
result = curve.get_xy(result_jac)
|
|
|
|
scalars.append(scalar)
|
|
results_x.append(result[0])
|
|
results_y.append(result[1])
|
|
|
|
# Output as C arrays
|
|
out.write('static const uint64_t BATCH_FPMUL_SCALARS[][MAX_LIMBS] = {\n')
|
|
for s in scalars:
|
|
out.write(f' {crepr.fp(s)},\n')
|
|
out.write('};\n\n')
|
|
|
|
out.write('static const uint64_t BATCH_FPMUL_EXPECTED_X[][MAX_LIMBS] = {\n')
|
|
for rx in results_x:
|
|
out.write(f' {crepr.fp(rx)},\n')
|
|
out.write('};\n\n')
|
|
|
|
out.write('static const uint64_t BATCH_FPMUL_EXPECTED_Y[][MAX_LIMBS] = {\n')
|
|
for ry in results_y:
|
|
out.write(f' {crepr.fp(ry)},\n')
|
|
out.write('};\n')
|
|
|
|
|
|
def generate_batch_pmul_test(out, curve, width, num_tests=10):
|
|
"""Generate test vectors for batch scalar multiplication"""
|
|
import random
|
|
random.seed(42) # Fixed seed for reproducibility
|
|
|
|
crepr = CRepr()
|
|
crepr.width = width
|
|
|
|
out.write('// Test vectors for batch scalar multiplication on secp256k1\n')
|
|
out.write('// Generated for correctness testing\n\n')
|
|
out.write(f'#define BATCH_PMUL_NUM_TESTS {num_tests}\n\n')
|
|
|
|
points_x = []
|
|
points_y = []
|
|
scalars = []
|
|
results_x = []
|
|
results_y = []
|
|
|
|
for i in range(num_tests):
|
|
# Generate random point
|
|
point = curve.random_element()
|
|
|
|
# Generate random scalar (use smaller scalars for reasonable test execution time)
|
|
scalar = random.randint(1, 2**128)
|
|
|
|
# Compute scalar multiplication using Python reference implementation
|
|
point_jac = curve.to_jacobian(point)
|
|
result_jac = curve.multiply_jacobian(point_jac, scalar)
|
|
result = curve.get_xy(result_jac)
|
|
|
|
points_x.append(point[0])
|
|
points_y.append(point[1])
|
|
scalars.append(scalar)
|
|
results_x.append(result[0])
|
|
results_y.append(result[1])
|
|
|
|
# Output as C arrays
|
|
out.write('static const uint64_t BATCH_PMUL_POINTS_X[][MAX_LIMBS] = {\n')
|
|
for px in points_x:
|
|
out.write(f' {crepr.fp(px)},\n')
|
|
out.write('};\n\n')
|
|
|
|
out.write('static const uint64_t BATCH_PMUL_POINTS_Y[][MAX_LIMBS] = {\n')
|
|
for py in points_y:
|
|
out.write(f' {crepr.fp(py)},\n')
|
|
out.write('};\n\n')
|
|
|
|
out.write('static const uint64_t BATCH_PMUL_SCALARS[][MAX_LIMBS] = {\n')
|
|
for s in scalars:
|
|
out.write(f' {crepr.fp(s)},\n')
|
|
out.write('};\n\n')
|
|
|
|
out.write('static const uint64_t BATCH_PMUL_EXPECTED_X[][MAX_LIMBS] = {\n')
|
|
for rx in results_x:
|
|
out.write(f' {crepr.fp(rx)},\n')
|
|
out.write('};\n\n')
|
|
|
|
out.write('static const uint64_t BATCH_PMUL_EXPECTED_Y[][MAX_LIMBS] = {\n')
|
|
for ry in results_y:
|
|
out.write(f' {crepr.fp(ry)},\n')
|
|
out.write('};\n')
|
|
|
|
|
|
def generate_ec_test(out, curve, width):
|
|
"""Generate test vectors for EC operations"""
|
|
crepr = CRepr()
|
|
crepr.width = width
|
|
|
|
out.write('// Test vectors for EC operations on secp256k1\n')
|
|
out.write('// Generated with fixed random seed for reproducibility\n\n')
|
|
|
|
# Test 1: Point addition - two distinct random points (use Jacobian arithmetic)
|
|
p1 = curve.random_element()
|
|
p2 = curve.random_element()
|
|
p1_jac = curve.to_jacobian(p1)
|
|
p2_jac = curve.to_jacobian(p2)
|
|
p3_jac = curve.add_jacobian(p1_jac, p2_jac)
|
|
p3 = curve.get_xy(p3_jac)
|
|
|
|
out.write('// Test 1: Point Addition (P1 + P2 = P3)\n')
|
|
out.write('static const uint64_t EC_P1_X[MAX_LIMBS] = {};\n'.format(crepr.fp(p1[0])))
|
|
out.write('static const uint64_t EC_P1_Y[MAX_LIMBS] = {};\n'.format(crepr.fp(p1[1])))
|
|
out.write('static const uint64_t EC_P2_X[MAX_LIMBS] = {};\n'.format(crepr.fp(p2[0])))
|
|
out.write('static const uint64_t EC_P2_Y[MAX_LIMBS] = {};\n'.format(crepr.fp(p2[1])))
|
|
out.write('static const uint64_t EC_P1_PLUS_P2_X[MAX_LIMBS] = {};\n'.format(crepr.fp(p3[0])))
|
|
out.write('static const uint64_t EC_P1_PLUS_P2_Y[MAX_LIMBS] = {};\n\n'.format(crepr.fp(p3[1])))
|
|
|
|
# Test 2: Point doubling (use Jacobian arithmetic for consistency with GPU)
|
|
p4 = curve.random_element()
|
|
p4_jac = curve.to_jacobian(p4)
|
|
p5_jac = curve.double_jacobian(p4_jac)
|
|
p5 = curve.get_xy(p5_jac)
|
|
|
|
out.write('// Test 2: Point Doubling (2*P4 = P5)\n')
|
|
out.write('static const uint64_t EC_P4_X[MAX_LIMBS] = {};\n'.format(crepr.fp(p4[0])))
|
|
out.write('static const uint64_t EC_P4_Y[MAX_LIMBS] = {};\n'.format(crepr.fp(p4[1])))
|
|
out.write('static const uint64_t EC_P4_DOUBLED_X[MAX_LIMBS] = {};\n'.format(crepr.fp(p5[0])))
|
|
out.write('static const uint64_t EC_P4_DOUBLED_Y[MAX_LIMBS] = {};\n\n'.format(crepr.fp(p5[1])))
|
|
|
|
# Test 3: Scalar multiplication (arbitrary point)
|
|
p_base = curve.random_element()
|
|
scalar1 = random.randint(1, curve.field.p - 1)
|
|
p_jac = curve.to_jacobian(p_base)
|
|
p_result_jac = curve.multiply_jacobian(p_jac, scalar1)
|
|
p_result = curve.get_xy(p_result_jac)
|
|
|
|
out.write('// Test 3: Scalar Multiplication of arbitrary point (scalar1 * P_base = P_result)\n')
|
|
out.write('static const uint64_t EC_SCALAR1[MAX_LIMBS] = {};\n'.format(crepr.fp(scalar1)))
|
|
out.write('static const uint64_t EC_P_BASE_X[MAX_LIMBS] = {};\n'.format(crepr.fp(p_base[0])))
|
|
out.write('static const uint64_t EC_P_BASE_Y[MAX_LIMBS] = {};\n'.format(crepr.fp(p_base[1])))
|
|
out.write('static const uint64_t EC_P_SCALAR_RESULT_X[MAX_LIMBS] = {};\n'.format(crepr.fp(p_result[0])))
|
|
out.write('static const uint64_t EC_P_SCALAR_RESULT_Y[MAX_LIMBS] = {};\n\n'.format(crepr.fp(p_result[1])))
|
|
|
|
# Test 4: Generator point multiplication
|
|
g = curve.generator
|
|
scalar2 = random.randint(1, curve.field.p - 1)
|
|
g_jac = curve.to_jacobian(g)
|
|
g_result_jac = curve.multiply_jacobian(g_jac, scalar2)
|
|
g_result = curve.get_xy(g_result_jac)
|
|
|
|
out.write('// Test 4: Generator Point Multiplication (scalar2 * G = G_result)\n')
|
|
out.write('static const uint64_t EC_SCALAR2[MAX_LIMBS] = {};\n'.format(crepr.fp(scalar2)))
|
|
out.write('static const uint64_t EC_G_X[MAX_LIMBS] = {};\n'.format(crepr.fp(g[0])))
|
|
out.write('static const uint64_t EC_G_Y[MAX_LIMBS] = {};\n'.format(crepr.fp(g[1])))
|
|
out.write('static const uint64_t EC_G_SCALAR_RESULT_X[MAX_LIMBS] = {};\n'.format(crepr.fp(g_result[0])))
|
|
out.write('static const uint64_t EC_G_SCALAR_RESULT_Y[MAX_LIMBS] = {};\n\n'.format(crepr.fp(g_result[1])))
|
|
|
|
# Test 5: Small scalar multiplications of generator
|
|
out.write('// Test 5: Small scalar multiplications of generator\n')
|
|
for k in [2, 3, 5, 10]:
|
|
g_jac = curve.to_jacobian(g)
|
|
result_jac = curve.multiply_jacobian(g_jac, k)
|
|
result = curve.get_xy(result_jac)
|
|
out.write('static const uint64_t EC_G_TIMES_{}_X[MAX_LIMBS] = {};\n'.format(k, crepr.fp(result[0])))
|
|
out.write('static const uint64_t EC_G_TIMES_{}_Y[MAX_LIMBS] = {};\n'.format(k, crepr.fp(result[1])))
|
|
out.write('\n')
|
|
|
|
|
|
# def generate_ecdsa_test(out, f, ec, width):
|
|
# e = 0x10D51CB90C0C0522E94875A2BEA7AB72299EBE7192E64EFE0573B1C77110E5C9
|
|
# priv_key = 0x128B2FA8BD433C6C068C8D803DFF79792A519A55171B1B650C23661D15897263
|
|
# k = 0xE11F5909F947D5BE08C84A22CE9F7C338F7CF4A5B941B9268025495D7D433071
|
|
# s = 0xE11F5909F947D5BE08C84A22CE9F7C338F7CF4A5B941B9268025495D7D433071
|
|
# r = 0x23B20B796AAAFEAAA3F1592CB9B4A93D5A8D279843E1C57980E64E0ABC5F5B05
|
|
# key_x = 0xD5548C7825CBB56150A3506CD57464AF8A1AE0519DFAF3C58221DC810CAF28DD
|
|
# key_y = 0x921073768FE3D59CE54E79A49445CF73FED23086537027264D168946D479533E
|
|
|
|
# n = 1 << 10
|
|
# random_r = [random.randint(0, f.p - 1) for i in range(n)]
|
|
# random_s = [random.randint(0, f.p - 1) for i in range(n)]
|
|
# random_e = [random.randint(0, f.p - 1) for i in range(n)]
|
|
# random_priv_key = [random.randint(0, f.p - 1) for i in range(n)]
|
|
# random_k = [random.randint(0, f.p - 1) for i in range(n)]
|
|
# distinct_bases = 1<<6
|
|
# base_aff = [
|
|
# ec.random_element()
|
|
# for _ in range(distinct_bases)
|
|
# ]
|
|
# base_id = [random.randint(0, distinct_bases - 1) for i in range(n)]
|
|
# random_key_x = []
|
|
# random_key_y = []
|
|
# for i in range(n):
|
|
# x, y = base_aff[base_id[i]]
|
|
# random_key_x.append(x)
|
|
# random_key_y.append(y)
|
|
|
|
# crepr = CRepr()
|
|
# crepr.width = width
|
|
|
|
# out.write('static const uint64_t E[MAX_LIMBS] = {};\n'.format(
|
|
# crepr.fp(e)))
|
|
# out.write('static const uint64_t PRIV_KEY[MAX_LIMBS] = {};\n'.format(
|
|
# crepr.fp(priv_key)))
|
|
# out.write('static const uint64_t K[MAX_LIMBS] = {};\n'.format(
|
|
# crepr.fp(k)))
|
|
# out.write('static const uint64_t S[MAX_LIMBS] = {};\n'.format(
|
|
# crepr.fp(s)))
|
|
# out.write('static const uint64_t R[MAX_LIMBS] = {};\n'.format(
|
|
# crepr.fp(r)))
|
|
# out.write('static const uint64_t KEY_X[MAX_LIMBS] = {};\n'.format(
|
|
# crepr.fp(key_x)))
|
|
# out.write('static const uint64_t KEY_Y[MAX_LIMBS] = {};\n'.format(
|
|
# crepr.fp(key_y)))
|
|
# out.write('static const uint64_t RANDOM_R[{}][MAX_LIMBS] = {};\n'.format(
|
|
# n, crepr.fp_array(random_r)))
|
|
# out.write('static const uint64_t RANDOM_S[{}][MAX_LIMBS] = {};\n'.format(
|
|
# n, crepr.fp_array(random_s)))
|
|
# out.write('static const uint64_t RANDOM_E[{}][MAX_LIMBS] = {};\n'.format(
|
|
# n, crepr.fp_array(random_e)))
|
|
# out.write('static const uint64_t RANDOM_PRIV_KEY[{}][MAX_LIMBS] = {};\n'.format(
|
|
# n, crepr.fp_array(random_priv_key)))
|
|
# out.write('static const uint64_t RANDOM_K[{}][MAX_LIMBS] = {};\n'.format(
|
|
# n, crepr.fp_array(random_k)))
|
|
# out.write('static const uint64_t RANDOM_KEY_X[{}][MAX_LIMBS] = {};\n'.format(
|
|
# n, crepr.fp_array(random_key_x)))
|
|
# out.write('static const uint64_t RANDOM_KEY_Y[{}][MAX_LIMBS] = {};\n'.format(
|
|
# n, crepr.fp_array(random_key_y)))
|
|
|
|
def generate_sha256_test(out, curve, num_tests=10):
|
|
"""Generate test vectors for SHA-256"""
|
|
import random
|
|
random.seed(55) # Different seed for SHA-256 tests
|
|
|
|
out.write('// Test vectors for SHA-256\n')
|
|
out.write('// Including standard test vectors and EC point hashing\n\n')
|
|
|
|
# Standard SHA-256 test vectors from NIST/official sources
|
|
standard_tests = [
|
|
("", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"),
|
|
("abc", "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"),
|
|
("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
|
|
"248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"),
|
|
("The quick brown fox jumps over the lazy dog",
|
|
"d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"),
|
|
("The quick brown fox jumps over the lazy dog.",
|
|
"ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c"),
|
|
]
|
|
|
|
out.write(f'#define SHA256_NUM_STANDARD_TESTS {len(standard_tests)}\n\n')
|
|
|
|
# Output standard test inputs
|
|
out.write('static const uint8_t* SHA256_STANDARD_INPUTS[] = {\n')
|
|
for msg, _ in standard_tests:
|
|
escaped_msg = msg.replace('"', '\\"')
|
|
out.write(f' (const uint8_t*)"{escaped_msg}",\n')
|
|
out.write('};\n\n')
|
|
|
|
# Output standard test input lengths
|
|
out.write('static const uint64_t SHA256_STANDARD_INPUT_LENS[] = {\n')
|
|
for msg, _ in standard_tests:
|
|
out.write(f' {len(msg)},\n')
|
|
out.write('};\n\n')
|
|
|
|
# Output expected hashes
|
|
out.write('static const char* SHA256_STANDARD_EXPECTED[] = {\n')
|
|
for _, hash_hex in standard_tests:
|
|
out.write(f' "{hash_hex}",\n')
|
|
out.write('};\n\n')
|
|
|
|
# Generate EC point test vectors (64 bytes: 32-byte x || 32-byte y)
|
|
out.write(f'#define SHA256_EC_NUM_TESTS {num_tests}\n\n')
|
|
|
|
ec_points = []
|
|
ec_hashes = []
|
|
|
|
for i in range(num_tests):
|
|
# Generate random EC point
|
|
point = curve.random_element()
|
|
x, y = point
|
|
|
|
# Convert to 32-byte big-endian representation
|
|
x_bytes = x.to_bytes(32, byteorder='big')
|
|
y_bytes = y.to_bytes(32, byteorder='big')
|
|
point_bytes = x_bytes + y_bytes
|
|
|
|
# Compute SHA-256 hash
|
|
hash_digest = hashlib.sha256(point_bytes).hexdigest()
|
|
|
|
ec_points.append(point_bytes)
|
|
ec_hashes.append(hash_digest)
|
|
|
|
# Output EC points as byte arrays
|
|
out.write('static const uint8_t SHA256_EC_POINTS[][64] = {\n')
|
|
for point_bytes in ec_points:
|
|
out.write(' {')
|
|
for i, byte in enumerate(point_bytes):
|
|
if i > 0:
|
|
out.write(',')
|
|
if i % 16 == 0:
|
|
out.write('\n ')
|
|
out.write(f'0x{byte:02x}')
|
|
out.write('\n },\n')
|
|
out.write('};\n\n')
|
|
|
|
# Output expected hashes
|
|
out.write('static const char* SHA256_EC_EXPECTED[] = {\n')
|
|
for hash_hex in ec_hashes:
|
|
out.write(f' "{hash_hex}",\n')
|
|
out.write('};\n\n')
|
|
|
|
def generate_batch_pmul_sha256_test(out, curve, width, num_tests=10):
|
|
"""Generate test vectors for batch scalar multiplication followed by SHA-256"""
|
|
import random
|
|
random.seed(66) # Different seed for this test
|
|
|
|
crepr = CRepr()
|
|
crepr.width = width
|
|
|
|
out.write('// Test vectors for batch scalar multiplication followed by SHA-256\n')
|
|
out.write('// Each test computes: hash = SHA256(scalar * point)\n\n')
|
|
out.write(f'#define BATCH_PMUL_SHA256_NUM_TESTS {num_tests}\n\n')
|
|
|
|
points_x = []
|
|
points_y = []
|
|
scalars = []
|
|
hashes = []
|
|
|
|
for i in range(num_tests):
|
|
# Generate random point
|
|
point = curve.random_element()
|
|
|
|
# Generate random scalar (use smaller scalars for reasonable test execution time)
|
|
scalar = random.randint(1, 2**128)
|
|
|
|
# Compute scalar multiplication using Python reference implementation
|
|
point_jac = curve.to_jacobian(point)
|
|
result_jac = curve.multiply_jacobian(point_jac, scalar)
|
|
result = curve.get_xy(result_jac)
|
|
|
|
# Convert result to 32-byte big-endian representation
|
|
x_bytes = result[0].to_bytes(32, byteorder='big')
|
|
y_bytes = result[1].to_bytes(32, byteorder='big')
|
|
point_bytes = x_bytes + y_bytes
|
|
|
|
# Compute SHA-256 hash
|
|
hash_digest = hashlib.sha256(point_bytes).hexdigest()
|
|
|
|
points_x.append(point[0])
|
|
points_y.append(point[1])
|
|
scalars.append(scalar)
|
|
hashes.append(hash_digest)
|
|
|
|
# Output as C arrays
|
|
out.write('static const uint64_t BATCH_PMUL_SHA256_POINTS_X[][MAX_LIMBS] = {\n')
|
|
for px in points_x:
|
|
out.write(f' {crepr.fp(px)},\n')
|
|
out.write('};\n\n')
|
|
|
|
out.write('static const uint64_t BATCH_PMUL_SHA256_POINTS_Y[][MAX_LIMBS] = {\n')
|
|
for py in points_y:
|
|
out.write(f' {crepr.fp(py)},\n')
|
|
out.write('};\n\n')
|
|
|
|
out.write('static const uint64_t BATCH_PMUL_SHA256_SCALARS[][MAX_LIMBS] = {\n')
|
|
for s in scalars:
|
|
out.write(f' {crepr.fp(s)},\n')
|
|
out.write('};\n\n')
|
|
|
|
# Output expected hashes
|
|
out.write('static const char* BATCH_PMUL_SHA256_EXPECTED[] = {\n')
|
|
for hash_hex in hashes:
|
|
out.write(f' "{hash_hex}",\n')
|
|
out.write('};\n\n')
|
|
|
|
random.seed(233)
|
|
if __name__ == '__main__':
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('--out', type=str, required=True)
|
|
args = parser.parse_args()
|
|
root = pathlib.Path(args.out)
|
|
|
|
root.mkdir(exist_ok=True)
|
|
|
|
with open(root / 'fp_constants.h', 'w') as f:
|
|
crepr_64 = CRepr()
|
|
crepr_64.width = 64
|
|
f.write(crepr_64.fp_constant(field.Fq_SECP256K1) + '\n')
|
|
f.write(crepr_64.fp_constant(field.Fq_SECP256K1_n) + '\n')
|
|
|
|
with open(root / 'ec_constants.h', 'w') as f:
|
|
crepr_64 = CRepr()
|
|
crepr_64.width = 64
|
|
f.write(crepr_64.ec_constant(ec.G1_SECP256K1) + '\n')
|
|
# f.write(crepr_64.ec_constant(ec.G1_ECDSA_VERIFY) + '\n')
|
|
|
|
with open(root / 'ecdsa_constants.h', 'w') as f:
|
|
crepr_64 = CRepr()
|
|
crepr_64.width = 64
|
|
f.write(crepr_64.ecdsa_constant(ec.G1_SECP256K1) + '\n')
|
|
# f.write(crepr_64.ecdsa_constant(ec.G1_ECDSA_VERIFY) + '\n')
|
|
|
|
with open(root / 'fp_ops_cc_details.h', 'w') as f:
|
|
for limbs_per_lane in range(1, 17):
|
|
f.write(ccgen.gen(64, limbs_per_lane))
|
|
for limbs_per_lane in range(1, 33):
|
|
f.write(ccgen.gen(32, limbs_per_lane))
|
|
|
|
with open(root / 'ec_ops_add_details.h', 'w') as f:
|
|
f.write(ec_ops.EC_ADD.cucode() + '\n')
|
|
|
|
with open(root / 'ec_ops_add_details_with_reduce.h', 'w') as f:
|
|
f.write(ec_ops.EC_ADD.cucode_with_reduce() + '\n')
|
|
|
|
with open(root / 'ec_ops_dbl_details.h', 'w') as f:
|
|
f.write(ec_ops.EC_DBL.cucode() + '\n')
|
|
|
|
with open(root / 'ec_ops_dbl_details_with_reduce.h', 'w') as f:
|
|
f.write(ec_ops.EC_DBL.cucode_with_reduce() + '\n')
|
|
|
|
with open(root / 'ec_ops_dbl_1_details.h', 'w') as f:
|
|
f.write(ec_ops.EC_DBL_1.cucode() + '\n')
|
|
|
|
with open(root / 'ec_ops_dbl_1_details_with_reduce.h', 'w') as f:
|
|
f.write(ec_ops.EC_DBL_1.cucode_with_reduce() + '\n')
|
|
|
|
with open(root / 'ec_ops_dbl_2_details.h', 'w') as f:
|
|
f.write(ec_ops.EC_DBL_2.cucode() + '\n')
|
|
|
|
with open(root / 'ec_ops_dbl_2_details_with_reduce.h', 'w') as f:
|
|
f.write(ec_ops.EC_DBL_2.cucode_with_reduce() + '\n')
|
|
|
|
with open(root / 'ec_ops_mixed_add_details.h', 'w') as f:
|
|
f.write(ec_ops.EC_MIXED_ADD.cucode() + '\n')
|
|
|
|
with open(root / 'ec_ops_mixed_add_details_with_reduce.h', 'w') as f:
|
|
f.write(ec_ops.EC_MIXED_ADD.cucode_with_reduce() + '\n')
|
|
|
|
with open(root / 'ec_ops_affine_dbl_details.h', 'w') as f:
|
|
f.write(ec_ops.EC_AFF_DBL.cucode() + '\n')
|
|
|
|
with open(root / 'ec_ops_affine_dbl_details_with_reduce.h', 'w') as f:
|
|
f.write(ec_ops.EC_AFF_DBL.cucode_with_reduce() + '\n')
|
|
|
|
with open(root / 'affine_ops_dbl_details.h', 'w') as f:
|
|
f.write(ec_ops.AFF_DBL.cucode(0) + '\n')
|
|
|
|
with open(root / 'affine_ops_dbl_details_with_reduce.h', 'w') as f:
|
|
f.write(ec_ops.AFF_DBL.cucode_with_reduce(0) + '\n')
|
|
|
|
with open(root / 'affine_ops_add_details.h', 'w') as f:
|
|
f.write(ec_ops.AFF_ADD.cucode(0) + '\n')
|
|
|
|
with open(root / 'affine_ops_add_details_with_reduce.h', 'w') as f:
|
|
f.write(ec_ops.AFF_ADD.cucode_with_reduce(0) + '\n')
|
|
|
|
# tests
|
|
with open(root / 'fp_test_constants.h', 'w') as f:
|
|
generate_fp_test(f, field.Fq_SECP256K1.name, field.Fq_SECP256K1, 6, field.Fq_SECP256K1.width)
|
|
generate_fp_test(f, field.Fq_SECP256K1_n.name, field.Fq_SECP256K1_n, 6, field.Fq_SECP256K1_n.width)
|
|
|
|
with open(root / 'ec_test_constants.h', 'w') as f:
|
|
generate_ec_test(f, ec.G1_SECP256K1, field.Fq_SECP256K1.width)
|
|
|
|
with open(root / 'batch_pmul_test_constants.h', 'w') as f:
|
|
generate_batch_pmul_test(f, ec.G1_SECP256K1, field.Fq_SECP256K1.width, num_tests=10)
|
|
|
|
with open(root / 'batch_fpmul_test_constants.h', 'w') as f:
|
|
generate_batch_fpmul_test(f, ec.G1_SECP256K1, field.Fq_SECP256K1.width, num_tests=10)
|
|
|
|
with open(root / 'batch_add_test_constants.h', 'w') as f:
|
|
generate_batch_add_test(f, ec.G1_SECP256K1, field.Fq_SECP256K1.width, num_tests=10)
|
|
|
|
with open(root / 'sha256_test_constants.h', 'w') as f:
|
|
generate_sha256_test(f, ec.G1_SECP256K1, num_tests=10)
|
|
|
|
with open(root / 'batch_pmul_sha256_test_constants.h', 'w') as f:
|
|
generate_batch_pmul_sha256_test(f, ec.G1_SECP256K1, field.Fq_SECP256K1.width, num_tests=10)
|
|
|
|
with open(root / 'ecdsa_test_constants.h', 'w') as f:
|
|
generate_ecdsa_test(
|
|
f, field.Fq_SECP256K1_n, ec.G1_SECP256K1, field.Fq_SECP256K1_n.width)
|
|
|
|
with open(root / 'ecdsa_fixed_test_constants.h', 'w') as f:
|
|
generate_ecdsa_fixed_test(
|
|
f, field.Fq_SECP256K1_n, ec.G1_SECP256K1, field.Fq_SECP256K1_n.width)
|