EC: Add C code generation to mk/generate_curves.py.
Generate some of the C boilerplate, particularly the large constants. The output is written into target/curves/, and can be merged into the actual code in crypto/fipsmodule/ec/ using a two-way merge tool; this is the same as the Rust code generation. Changes to gfp_p{256,384}.c are due to differences in the generator's output: * The generator doesn't generate trailing commas in arrays. * The generator consistently avoids adding leading zeros to hex constants, and consistently format values less than 10 in decimal; the exiting code used a mix of styles. * The generator wraps arrays consistently; the existing code used a mix of wrapping styles. * The generator does not nest constants in the functions that need them. This was changed to support future refactorings.
This commit is contained in:
parent
75cbe475ff
commit
d87972edc9
@ -23,17 +23,19 @@ typedef Limb Scalar[P256_LIMBS];
|
||||
|
||||
#include "../bn/internal.h"
|
||||
|
||||
static const BN_ULONG N[P256_LIMBS] = {
|
||||
TOBN(0xf3b9cac2, 0xfc632551),
|
||||
TOBN(0xbce6faad, 0xa7179e84),
|
||||
TOBN(0xffffffff, 0xffffffff),
|
||||
TOBN(0xffffffff, 0)
|
||||
};
|
||||
|
||||
static const BN_ULONG N_N0[] = {
|
||||
BN_MONT_CTX_N0(0xccd1c8aa, 0xee00bc4f)
|
||||
};
|
||||
|
||||
void p256_scalar_mul_mont(ScalarMont r, const ScalarMont a,
|
||||
const ScalarMont b) {
|
||||
static const BN_ULONG N[] = {
|
||||
TOBN(0xf3b9cac2, 0xfc632551),
|
||||
TOBN(0xbce6faad, 0xa7179e84),
|
||||
TOBN(0xffffffff, 0xffffffff),
|
||||
TOBN(0xffffffff, 0x00000000),
|
||||
};
|
||||
static const BN_ULONG N_N0[] = {
|
||||
BN_MONT_CTX_N0(0xccd1c8aa, 0xee00bc4f)
|
||||
};
|
||||
/* XXX: Inefficient. TODO: optimize with dedicated multiplication routine. */
|
||||
bn_mul_mont(r, a, b, N, N_N0, P256_LIMBS);
|
||||
}
|
||||
|
@ -29,14 +29,13 @@ typedef Limb Elem[P384_LIMBS];
|
||||
typedef Limb ScalarMont[P384_LIMBS];
|
||||
typedef Limb Scalar[P384_LIMBS];
|
||||
|
||||
|
||||
static const BN_ULONG Q[P384_LIMBS] = {
|
||||
TOBN(0x00000000, 0xffffffff),
|
||||
TOBN(0xffffffff, 0x00000000),
|
||||
TOBN(0, 0xffffffff),
|
||||
TOBN(0xffffffff, 0),
|
||||
TOBN(0xffffffff, 0xfffffffe),
|
||||
TOBN(0xffffffff, 0xffffffff),
|
||||
TOBN(0xffffffff, 0xffffffff),
|
||||
TOBN(0xffffffff, 0xffffffff),
|
||||
TOBN(0xffffffff, 0xffffffff)
|
||||
};
|
||||
|
||||
static const BN_ULONG N[P384_LIMBS] = {
|
||||
@ -45,15 +44,34 @@ static const BN_ULONG N[P384_LIMBS] = {
|
||||
TOBN(0xc7634d81, 0xf4372ddf),
|
||||
TOBN(0xffffffff, 0xffffffff),
|
||||
TOBN(0xffffffff, 0xffffffff),
|
||||
TOBN(0xffffffff, 0xffffffff),
|
||||
TOBN(0xffffffff, 0xffffffff)
|
||||
};
|
||||
|
||||
|
||||
static const BN_ULONG ONE[P384_LIMBS] = {
|
||||
TOBN(0xffffffff, 1), TOBN(0, 0xffffffff), TOBN(0, 1), TOBN(0, 0), TOBN(0, 0),
|
||||
TOBN(0xffffffff, 1),
|
||||
TOBN(0, 0xffffffff),
|
||||
TOBN(0, 1),
|
||||
TOBN(0, 0),
|
||||
TOBN(0, 0),
|
||||
TOBN(0, 0)
|
||||
};
|
||||
|
||||
static const Elem Q_PLUS_1_SHR_1 = {
|
||||
TOBN(0, 0x80000000),
|
||||
TOBN(0x7fffffff, 0x80000000),
|
||||
TOBN(0xffffffff, 0xffffffff),
|
||||
TOBN(0xffffffff, 0xffffffff),
|
||||
TOBN(0xffffffff, 0xffffffff),
|
||||
TOBN(0x7fffffff, 0xffffffff)
|
||||
};
|
||||
|
||||
static const BN_ULONG Q_N0[] = {
|
||||
BN_MONT_CTX_N0(1, 1)
|
||||
};
|
||||
|
||||
static const BN_ULONG N_N0[] = {
|
||||
BN_MONT_CTX_N0(0x6ed46089, 0xe88fdc45)
|
||||
};
|
||||
|
||||
/* XXX: MSVC for x86 warns when it fails to inline these functions it should
|
||||
* probably inline. */
|
||||
@ -141,12 +159,6 @@ static void elem_div_by_2(Elem r, const Elem a) {
|
||||
carry = new_carry;
|
||||
}
|
||||
|
||||
static const Elem Q_PLUS_1_SHR_1 = {
|
||||
TOBN(0x00000000, 0x80000000), TOBN(0x7fffffff, 0x80000000),
|
||||
TOBN(0xffffffff, 0xffffffff), TOBN(0xffffffff, 0xffffffff),
|
||||
TOBN(0xffffffff, 0xffffffff), TOBN(0x7fffffff, 0xffffffff),
|
||||
};
|
||||
|
||||
Elem adjusted;
|
||||
BN_ULONG carry2 = limbs_add(adjusted, r, Q_PLUS_1_SHR_1, P384_LIMBS);
|
||||
dev_assert_secret(carry2 == 0);
|
||||
@ -155,9 +167,6 @@ static void elem_div_by_2(Elem r, const Elem a) {
|
||||
}
|
||||
|
||||
static inline void elem_mul_mont(Elem r, const Elem a, const Elem b) {
|
||||
static const BN_ULONG Q_N0[] = {
|
||||
BN_MONT_CTX_N0(0x1, 0x1)
|
||||
};
|
||||
/* XXX: Not (clearly) constant-time; inefficient.*/
|
||||
bn_mul_mont(r, a, b, Q, Q_N0, P384_LIMBS);
|
||||
}
|
||||
@ -203,9 +212,6 @@ void p384_elem_neg(Elem r, const Elem a) {
|
||||
|
||||
void p384_scalar_mul_mont(ScalarMont r, const ScalarMont a,
|
||||
const ScalarMont b) {
|
||||
static const BN_ULONG N_N0[] = {
|
||||
BN_MONT_CTX_N0(0x6ed46089, 0xe88fdc45)
|
||||
};
|
||||
/* XXX: Inefficient. TODO: Add dedicated multiplication routine. */
|
||||
bn_mul_mont(r, a, b, N, N_N0, P384_LIMBS);
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
from textwrap import wrap
|
||||
|
||||
curve_template = """
|
||||
rs_template = """
|
||||
// Copyright 2016-2023 Brian Smith.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
@ -243,8 +243,11 @@ import sys
|
||||
def whole_bit_length(p, limb_bits):
|
||||
return (p.bit_length() + limb_bits - 1) // limb_bits * limb_bits
|
||||
|
||||
def to_montgomery_value(x, p, limb_bits):
|
||||
return (x * 2**whole_bit_length(p, limb_bits)) % p
|
||||
|
||||
def to_montgomery_(x, p, limb_bits):
|
||||
value = (x * 2**whole_bit_length(p, limb_bits)) % p
|
||||
value = to_montgomery_value(x, p, limb_bits)
|
||||
return '"%x"' % value
|
||||
|
||||
def to_montgomery(x, p):
|
||||
@ -296,25 +299,16 @@ def modinv(a, m):
|
||||
def format_curve_name(g):
|
||||
return "p%d" % g["q"].bit_length()
|
||||
|
||||
def format_prime_curve(g):
|
||||
def generate_rs(g, out_dir):
|
||||
q = g["q"]
|
||||
n = g["n"]
|
||||
if g["a"] != -3:
|
||||
raise ValueError("Only curves where a == -3 are supported.")
|
||||
if g["cofactor"] != 1:
|
||||
raise ValueError("Only curves with cofactor 1 are supported.")
|
||||
if q != g["q_formula"]:
|
||||
raise ValueError("Polynomial representation of q doesn't match the "
|
||||
"literal version given in the specification.")
|
||||
if n != g["n_formula"]:
|
||||
raise ValueError("Polynomial representation of n doesn't match the "
|
||||
"literal version given in the specification.")
|
||||
|
||||
name = format_curve_name(g)
|
||||
|
||||
q_minus_3 = "\\\n// ".join(wrap(hex(q - 3), 66))
|
||||
n_minus_2 = "\\\n// ".join(wrap(hex(n - 2), 66))
|
||||
|
||||
return curve_template % {
|
||||
output = rs_template % {
|
||||
"bits": g["q"].bit_length(),
|
||||
"name": name,
|
||||
"q" : q,
|
||||
@ -331,6 +325,133 @@ def format_prime_curve(g):
|
||||
"n_minus_2": n_minus_2,
|
||||
}
|
||||
|
||||
out_path = os.path.join(out_dir, "%s.rs" % name)
|
||||
with open(out_path, "wb") as f:
|
||||
f.write(output.encode("utf-8"))
|
||||
subprocess.run(["rustfmt", out_path])
|
||||
|
||||
c_template = """
|
||||
/* Copyright 2016-2023 Brian Smith.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
||||
|
||||
#include "../../limbs/limbs.h"
|
||||
#include "../bn/internal.h"
|
||||
#include "../../internal.h"
|
||||
|
||||
#include "../../limbs/limbs.inl"
|
||||
|
||||
typedef Limb Elem[P%(bits)d_LIMBS];
|
||||
typedef Limb ScalarMont[P%(bits)d_LIMBS];
|
||||
typedef Limb Scalar[P%(bits)d_LIMBS];
|
||||
|
||||
static const BN_ULONG Q[P%(bits)d_LIMBS] = {
|
||||
%(q)s
|
||||
};
|
||||
|
||||
static const BN_ULONG N[P%(bits)d_LIMBS] = {
|
||||
%(n)s
|
||||
};
|
||||
|
||||
static const BN_ULONG ONE[P%(bits)d_LIMBS] = {
|
||||
%(q_one)s
|
||||
};
|
||||
|
||||
static const Elem Q_PLUS_1_SHR_1 = {
|
||||
%(q_plus_1_shr_1)s
|
||||
};
|
||||
|
||||
static const BN_ULONG Q_N0[] = {
|
||||
%(q_n0)s
|
||||
};
|
||||
|
||||
static const BN_ULONG N_N0[] = {
|
||||
%(n_n0)s
|
||||
};
|
||||
|
||||
"""
|
||||
|
||||
# Given a number |x|, return a generator of a sequence |a| such that
|
||||
#
|
||||
# x == a[0] + a[1]*2**limb_bits + ...
|
||||
#
|
||||
def little_endian_limbs(x):
|
||||
if x < 0:
|
||||
raise ValueError("x must be positive");
|
||||
if x == 0:
|
||||
yield [0]
|
||||
return
|
||||
|
||||
while x != 0:
|
||||
x, digit = divmod(x, 2**64)
|
||||
yield digit
|
||||
|
||||
def format_bn(x, tobn):
|
||||
def format_limb(val):
|
||||
if val < 10:
|
||||
return "%d" % val
|
||||
else:
|
||||
return "0x%x" % val
|
||||
hi = format_limb(x // (2**32))
|
||||
lo = format_limb(x % (2**32))
|
||||
return "%s(%s, %s)" % (tobn, hi, lo)
|
||||
|
||||
tobn = lambda limb: format_bn(limb, "TOBN")
|
||||
bn_mont_ctx_n0 = lambda limb: format_bn(limb, "BN_MONT_CTX_N0")
|
||||
|
||||
def format_big_int(x, limb_count, f = tobn):
|
||||
limbs = list(little_endian_limbs(x))
|
||||
limbs += (limb_count - len(limbs)) * [0]
|
||||
return ",\n ".join([f(limb) for limb in limbs])
|
||||
|
||||
def generate_c(g, out_dir):
|
||||
q = g["q"]
|
||||
n = g["n"]
|
||||
|
||||
name = format_curve_name(g)
|
||||
limb_bits = 64
|
||||
limb_count = (q.bit_length() + limb_bits - 1) // limb_bits
|
||||
|
||||
output = c_template % {
|
||||
"bits": q.bit_length(),
|
||||
"q" : format_big_int(q, limb_count),
|
||||
"q_n0": format_big_int(modinv(-q, 2**limb_bits), 1, bn_mont_ctx_n0),
|
||||
"q_one" : format_big_int(to_montgomery_value(1, q, limb_bits), limb_count),
|
||||
"q_plus_1_shr_1": format_big_int((q + 1) >> 1, limb_count),
|
||||
"n" : format_big_int(n, limb_count),
|
||||
"n_n0": format_big_int(modinv(-n, 2**64), 1, bn_mont_ctx_n0),
|
||||
}
|
||||
|
||||
out_path = os.path.join(out_dir, "gfp_%s.c" % name)
|
||||
with open(out_path, "wb") as f:
|
||||
f.write(output.encode("utf-8"))
|
||||
|
||||
def generate(g, out_dir):
|
||||
if g["a"] != -3:
|
||||
raise ValueError("Only curves where a == -3 are supported.")
|
||||
if g["cofactor"] != 1:
|
||||
raise ValueError("Only curves with cofactor 1 are supported.")
|
||||
if g["q"] != g["q_formula"]:
|
||||
raise ValueError("Polynomial representation of q doesn't match the "
|
||||
"literal version given in the specification.")
|
||||
if g["n"] != g["n_formula"]:
|
||||
raise ValueError("Polynomial representation of n doesn't match the "
|
||||
"literal version given in the specification.")
|
||||
|
||||
generate_rs(g, out_dir)
|
||||
generate_c(g, out_dir)
|
||||
|
||||
|
||||
# The curve parameters are from
|
||||
# https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-186.pdf.
|
||||
# |q_formula| and |n_formula| are given in the polynomial representation so that
|
||||
@ -377,14 +498,7 @@ p521 = {
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
def generate_prime_curve_file(g, out_dir):
|
||||
code = format_prime_curve(g)
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
out_path = os.path.join(out_dir, "%s.rs" % format_curve_name(g))
|
||||
with open(out_path, "wb") as f:
|
||||
f.write(code.encode("utf-8"))
|
||||
subprocess.run(["rustfmt", out_path])
|
||||
|
||||
|
||||
out_dir = "target/curves"
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
for curve in [p256, p384, p521]:
|
||||
generate_prime_curve_file(curve, "target/curves")
|
||||
generate(curve, out_dir)
|
||||
|
Loading…
x
Reference in New Issue
Block a user