Fit STM32 Companions with equivalent compact field arithmetic
This commit is contained in:
1 parent
264fc2bccb
commit
05c0ed8bb2
8 files changed
+371
No files matched your search
@@ -114,6 +114,7 @@ jobs:
|
||||
run: |
|
||||
python3 -B test/test_stm32_companion_profile.py -v
|
||||
python3 -B test/test_ed25519_compact_scalar.py -v
|
||||
python3 -B test/test_ed25519_compact_field.py -v
|
||||
python3 -B test/test_ed25519_compact.py -v
|
||||
|
||||
- name: Compile ordinary STM32 USB Companion with pinned core and RadioLib
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#include "fixedint.h"
|
||||
#include "fe.h"
|
||||
|
||||
#if defined(ED25519_COMPACT_FIELD) && ED25519_COMPACT_FIELD
|
||||
#include "fe_compact.h"
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
helper functions
|
||||
@@ -78,6 +82,7 @@ void fe_1(fe h) {
|
||||
|h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
|
||||
*/
|
||||
|
||||
#if !defined(ED25519_COMPACT_FIELD) || !ED25519_COMPACT_FIELD
|
||||
void fe_add(fe h, const fe f, const fe g) {
|
||||
int32_t f0 = f[0];
|
||||
int32_t f1 = f[1];
|
||||
@@ -294,6 +299,8 @@ void fe_copy(fe h, const fe f) {
|
||||
Ignores top bit of h.
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
void fe_frombytes(fe h, const unsigned char *s) {
|
||||
int64_t h0 = load_4(s);
|
||||
int64_t h1 = load_3(s + 4) << 6;
|
||||
@@ -553,6 +560,7 @@ int fe_isnonzero(const fe f) {
|
||||
With tighter constraints on inputs can squeeze carries into int32.
|
||||
*/
|
||||
|
||||
#if !defined(ED25519_COMPACT_FIELD) || !ED25519_COMPACT_FIELD
|
||||
void fe_mul(fe h, const fe f, const fe g) {
|
||||
int32_t f0 = f[0];
|
||||
int32_t f1 = f[1];
|
||||
@@ -763,6 +771,7 @@ void fe_mul(fe h, const fe f, const fe g) {
|
||||
h[8] = (int32_t) h8;
|
||||
h[9] = (int32_t) h9;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ -843,6 +852,7 @@ Postconditions:
|
||||
|h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
|
||||
*/
|
||||
|
||||
#if !defined(ED25519_COMPACT_FIELD) || !ED25519_COMPACT_FIELD
|
||||
void fe_neg(fe h, const fe f) {
|
||||
int32_t f0 = f[0];
|
||||
int32_t f1 = f[1];
|
||||
@@ -878,6 +888,8 @@ void fe_neg(fe h, const fe f) {
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
void fe_pow22523(fe out, const fe z) {
|
||||
fe t0;
|
||||
fe t1;
|
||||
@@ -979,6 +991,7 @@ Postconditions:
|
||||
See fe_mul.c for discussion of implementation strategy.
|
||||
*/
|
||||
|
||||
#if !defined(ED25519_COMPACT_FIELD) || !ED25519_COMPACT_FIELD
|
||||
void fe_sq(fe h, const fe f) {
|
||||
int32_t f0 = f[0];
|
||||
int32_t f1 = f[1];
|
||||
@@ -1298,6 +1311,7 @@ void fe_sq2(fe h, const fe f) {
|
||||
h[8] = (int32_t) h8;
|
||||
h[9] = (int32_t) h9;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ -1312,6 +1326,7 @@ Postconditions:
|
||||
|h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
|
||||
*/
|
||||
|
||||
#if !defined(ED25519_COMPACT_FIELD) || !ED25519_COMPACT_FIELD
|
||||
void fe_sub(fe h, const fe f, const fe g) {
|
||||
int32_t f0 = f[0];
|
||||
int32_t f1 = f[1];
|
||||
@@ -1383,6 +1398,8 @@ Proof:
|
||||
so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
void fe_tobytes(unsigned char *s, const fe h) {
|
||||
int32_t h0 = h[0];
|
||||
int32_t h1 = h[1];
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Fixed-loop form of the ref10 field products in fe.c. Only the headless
|
||||
* STM32 Companion size recipes opt in; other builds keep the unrolled code.
|
||||
* The radix alternates 26 and 25 bits. Odd-by-odd products therefore have
|
||||
* weight 2, and terms beyond bit 254 fold back with weight 19, exactly as in
|
||||
* ref10. Product accumulation and the rounded carry order are unchanged.
|
||||
*
|
||||
* All loops, branches and array addresses depend only on public limb indices
|
||||
* or the public operation (multiply, square, double-square), not input values.
|
||||
* Under ref10's 1.65*2^26 / 1.65*2^25 input bounds, even doubled sums fit in
|
||||
* signed 64 bits. Outputs are written only after reading every input, so h
|
||||
* may alias f or g. No additional persistent RAM or heap is needed.
|
||||
*/
|
||||
|
||||
void fe_add(fe h, const fe f, const fe g) {
|
||||
int i;
|
||||
for (i = 0; i < 10; ++i) h[i] = f[i] + g[i];
|
||||
}
|
||||
|
||||
void fe_sub(fe h, const fe f, const fe g) {
|
||||
int i;
|
||||
for (i = 0; i < 10; ++i) h[i] = f[i] - g[i];
|
||||
}
|
||||
|
||||
void fe_neg(fe h, const fe f) {
|
||||
int i;
|
||||
for (i = 0; i < 10; ++i) h[i] = -f[i];
|
||||
}
|
||||
|
||||
void fe_copy(fe h, const fe f) {
|
||||
int i;
|
||||
for (i = 0; i < 10; ++i) h[i] = f[i];
|
||||
}
|
||||
|
||||
void fe_cmov(fe f, const fe g, unsigned int b) {
|
||||
const int32_t mask = -(int32_t)b;
|
||||
int i;
|
||||
for (i = 0; i < 10; ++i) f[i] ^= mask & (f[i] ^ g[i]);
|
||||
}
|
||||
|
||||
void fe_cswap(fe f, fe g, unsigned int b) {
|
||||
const int32_t mask = -(int32_t)b;
|
||||
int i;
|
||||
for (i = 0; i < 10; ++i) {
|
||||
const int32_t swap = mask & (f[i] ^ g[i]);
|
||||
f[i] ^= swap;
|
||||
g[i] ^= swap;
|
||||
}
|
||||
}
|
||||
|
||||
static void fe_compact_product(fe h, const fe f, const fe g, int doubled) {
|
||||
static const unsigned char carry_order[] = {0, 4, 1, 5, 2, 6, 3, 7, 4, 8, 9, 0};
|
||||
int64_t limbs[10] = {0};
|
||||
int i, j;
|
||||
for (i = 0; i < 10; ++i) {
|
||||
for (j = 0; j < 10; ++j) {
|
||||
const int sum = i + j;
|
||||
const int index = sum < 10 ? sum : sum - 10;
|
||||
const int weight = (sum < 10 ? 1 : 19) * (1 + (i & j & 1));
|
||||
limbs[index] += (int64_t)f[i] * g[j] * weight;
|
||||
}
|
||||
}
|
||||
if (doubled) {
|
||||
for (i = 0; i < 10; ++i) limbs[i] += limbs[i];
|
||||
}
|
||||
for (i = 0; i < (int)sizeof(carry_order); ++i) {
|
||||
const int index = carry_order[i];
|
||||
const int bits = 26 - (index & 1);
|
||||
const int64_t radix = (int64_t)1 << bits;
|
||||
const int64_t carry = (limbs[index] + radix / 2) >> bits;
|
||||
if (index == 9) limbs[0] += carry * 19;
|
||||
else limbs[index + 1] += carry;
|
||||
/* A carry may be negative; do not left-shift that signed value. */
|
||||
limbs[index] -= carry * radix;
|
||||
}
|
||||
for (i = 0; i < 10; ++i) h[i] = (int32_t)limbs[i];
|
||||
}
|
||||
|
||||
void fe_mul(fe h, const fe f, const fe g) {
|
||||
fe_compact_product(h, f, g, 0);
|
||||
}
|
||||
|
||||
void fe_sq(fe h, const fe f) {
|
||||
fe_compact_product(h, f, f, 0);
|
||||
}
|
||||
|
||||
void fe_sq2(fe h, const fe f) {
|
||||
fe_compact_product(h, f, f, 1);
|
||||
}
|
||||
@@ -52,6 +52,10 @@ void public_from_scalar(unsigned char *out, const unsigned char *scalar) {
|
||||
("small_sha512", ["-DED25519_SMALL_BASE=1", "-DED25519_COMPACT_SHA512=1"]),
|
||||
("small_all", ["-DED25519_SMALL_BASE=1", "-DED25519_COMPACT_SHA512=1",
|
||||
"-DED25519_COMPACT_SCALAR=1"]),
|
||||
("small_field", ["-DED25519_SMALL_BASE=1", "-DED25519_COMPACT_SHA512=1",
|
||||
"-DED25519_COMPACT_SCALAR=1", "-DED25519_COMPACT_FIELD=1"]),
|
||||
("compact_field", ["-DED25519_COMPACT_BASE=1", "-DED25519_COMPACT_SHA512=1",
|
||||
"-DED25519_COMPACT_SCALAR=1", "-DED25519_COMPACT_FIELD=1"]),
|
||||
):
|
||||
path = Path(directory) / (label + ".so")
|
||||
subprocess.run(["cc", "-std=c99", "-O2", "-Wall", "-Wextra", "-Werror",
|
||||
@@ -65,6 +69,9 @@ void public_from_scalar(unsigned char *out, const unsigned char *scalar) {
|
||||
library.ed25519_sign.argtypes = [ctypes.c_void_p, ctypes.c_void_p,
|
||||
ctypes.c_size_t, ctypes.c_void_p, ctypes.c_void_p]
|
||||
library.ed25519_key_exchange.argtypes = [ctypes.c_void_p] * 3
|
||||
library.ed25519_verify.argtypes = [ctypes.c_void_p, ctypes.c_void_p,
|
||||
ctypes.c_size_t, ctypes.c_void_p]
|
||||
library.ed25519_verify.restype = ctypes.c_int
|
||||
libraries.append(library)
|
||||
rng = random.Random(0xED960)
|
||||
scalars = [0, (1 << 255) - 1]
|
||||
@@ -93,6 +100,12 @@ void public_from_scalar(unsigned char *out, const unsigned char *scalar) {
|
||||
library.ed25519_create_keypair(peer, peer_prv, peer_seed)
|
||||
library.ed25519_sign(sig, message, len(message), pub, prv)
|
||||
library.ed25519_key_exchange(secret, peer, prv)
|
||||
self.assertEqual(library.ed25519_verify(sig, message, len(message), pub), 1)
|
||||
damaged = bytearray(sig.raw)
|
||||
damaged[iteration % len(damaged)] ^= 1 << (iteration % 8)
|
||||
self.assertEqual(library.ed25519_verify(bytes(damaged), message, len(message), pub), 0)
|
||||
wrong_message = bytes([message[0] ^ 1]) + message[1:] if message else b"x"
|
||||
self.assertEqual(library.ed25519_verify(sig, wrong_message, len(wrong_message), pub), 0)
|
||||
outputs.append((pub.raw, prv.raw, sig.raw, secret.raw))
|
||||
for output in outputs[1:]:
|
||||
self.assertEqual(outputs[0], output)
|
||||
@@ -187,6 +200,10 @@ void public_from_scalar(unsigned char *out, const unsigned char *scalar) {
|
||||
("compact_all", ["-DED25519_COMPACT_BASE=1",
|
||||
"-DED25519_COMPACT_SHA512=1",
|
||||
"-DED25519_COMPACT_SCALAR=1"]),
|
||||
("compact_field", ["-DED25519_COMPACT_BASE=1",
|
||||
"-DED25519_COMPACT_SHA512=1",
|
||||
"-DED25519_COMPACT_SCALAR=1",
|
||||
"-DED25519_COMPACT_FIELD=1"]),
|
||||
):
|
||||
executable = Path(temp_dir) / label
|
||||
subprocess.run(
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
"""Prove compact ref10 products against unrolled code and integer arithmetic."""
|
||||
|
||||
import ctypes
|
||||
from pathlib import Path
|
||||
import random
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
ED25519 = ROOT / "lib/ed25519"
|
||||
SHIFTS = (0, 26, 51, 77, 102, 128, 153, 179, 204, 230)
|
||||
BOUNDS = tuple(165 * (1 << (26 - (i & 1))) // 100 for i in range(10))
|
||||
PRIME = 2**255 - 19
|
||||
Field = ctypes.c_int32 * 10
|
||||
|
||||
|
||||
def integer(limbs):
|
||||
return sum(value << shift for value, shift in zip(limbs, SHIFTS)) % PRIME
|
||||
|
||||
|
||||
def vectors(seed, count):
|
||||
values = [[0] * 10, [1] + [0] * 9, list(BOUNDS), [-x for x in BOUNDS],
|
||||
[x if i & 1 else -x for i, x in enumerate(BOUNDS)]]
|
||||
for index, bound in enumerate(BOUNDS):
|
||||
for value in (-bound, -bound + 1, -1, 1, bound - 1, bound):
|
||||
item = [0] * 10
|
||||
item[index] = value
|
||||
values.append(item)
|
||||
for bit in range(26 - (index & 1)):
|
||||
for delta in (-1, 0, 1):
|
||||
item = [0] * 10
|
||||
item[index] = (1 << bit) + delta
|
||||
values.append(item)
|
||||
rng = random.Random(seed)
|
||||
values += [[rng.randint(-bound, bound) for bound in BOUNDS]
|
||||
for _ in range(count)]
|
||||
return values
|
||||
|
||||
|
||||
class Ed25519CompactFieldTest(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.directory = tempfile.TemporaryDirectory()
|
||||
cls.libraries = []
|
||||
for compact in (0, 1):
|
||||
path = Path(cls.directory.name) / ("field-%d.so" % compact)
|
||||
subprocess.run(["cc", "-std=c99", "-O2", "-Wall", "-Wextra", "-Werror",
|
||||
"-shared", "-fPIC", "-DED25519_COMPACT_FIELD=%d" % compact,
|
||||
str(ED25519 / "fe.c"), "-o", str(path)], check=True)
|
||||
library = ctypes.CDLL(str(path))
|
||||
for name, arguments in (("fe_mul", 3), ("fe_sq", 2), ("fe_sq2", 2)):
|
||||
function = getattr(library, name)
|
||||
function.argtypes = [ctypes.POINTER(ctypes.c_int32)] * arguments
|
||||
function.restype = None
|
||||
for name, arguments in (("fe_add", 3), ("fe_sub", 3),
|
||||
("fe_copy", 2), ("fe_neg", 2)):
|
||||
getattr(library, name).argtypes = [ctypes.POINTER(ctypes.c_int32)] * arguments
|
||||
getattr(library, name).restype = None
|
||||
for name in ("fe_cmov", "fe_cswap"):
|
||||
getattr(library, name).argtypes = [ctypes.POINTER(ctypes.c_int32)] * 2 + [ctypes.c_uint]
|
||||
getattr(library, name).restype = None
|
||||
cls.libraries.append(library)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.directory.cleanup()
|
||||
|
||||
def check_bounds(self, result):
|
||||
for index, value in enumerate(result):
|
||||
self.assertLessEqual(abs(value), 101 * (1 << (25 - (index & 1))) // 100)
|
||||
|
||||
def test_multiply_matches_original_integer_oracle_and_aliases(self):
|
||||
values = vectors(0xEDFE, 8000)
|
||||
rng = random.Random(0x26A25)
|
||||
for index, first in enumerate(values):
|
||||
second = values[rng.randrange(len(values))]
|
||||
expected = integer(first) * integer(second) % PRIME
|
||||
reference = Field()
|
||||
self.libraries[0].fe_mul(reference, Field(*first), Field(*second))
|
||||
for alias in range(3):
|
||||
a, b = Field(*first), Field(*second)
|
||||
output = a if alias == 0 else b if alias == 1 else Field()
|
||||
self.libraries[1].fe_mul(output, a, b)
|
||||
self.assertEqual(list(output), list(reference), (index, alias))
|
||||
self.assertEqual(integer(output), expected, (index, alias))
|
||||
self.check_bounds(output)
|
||||
|
||||
def test_square_and_double_square_match_original_oracle_and_aliases(self):
|
||||
for index, first in enumerate(vectors(0x25519, 8000)):
|
||||
for name, scale in (("fe_sq", 1), ("fe_sq2", 2)):
|
||||
expected = integer(first)**2 * scale % PRIME
|
||||
reference = Field()
|
||||
getattr(self.libraries[0], name)(reference, Field(*first))
|
||||
for alias in (False, True):
|
||||
data = Field(*first)
|
||||
output = data if alias else Field()
|
||||
getattr(self.libraries[1], name)(output, data)
|
||||
self.assertEqual(list(output), list(reference), (index, name, alias))
|
||||
self.assertEqual(integer(output), expected, (index, name, alias))
|
||||
self.check_bounds(output)
|
||||
data = Field(*first)
|
||||
self.libraries[1].fe_mul(data, data, data)
|
||||
self.assertEqual(integer(data), integer(first)**2 % PRIME)
|
||||
|
||||
def test_worst_case_products_and_carries_fit_signed_64_bits(self):
|
||||
# Absolute products bound every mixed-sign partial sum as well as the
|
||||
# final sum. Include the doubled-square operation and carry growth.
|
||||
limbs = [0] * 10
|
||||
for i in range(10):
|
||||
for j in range(10):
|
||||
weight = (1 if i + j < 10 else 19) * (1 + (i & j & 1))
|
||||
limbs[(i + j) % 10] += BOUNDS[i] * BOUNDS[j] * weight * 2
|
||||
self.assertLess(max(limbs), 2**62)
|
||||
for index in (0, 4, 1, 5, 2, 6, 3, 7, 4, 8, 9, 0):
|
||||
radix = 1 << (26 - (index & 1))
|
||||
self.assertLess(limbs[index] + radix // 2, 2**63)
|
||||
carry_bound = (limbs[index] + radix // 2) // radix + 1
|
||||
limbs[0 if index == 9 else index + 1] += carry_bound * (19 if index == 9 else 1)
|
||||
limbs[index] = radix // 2
|
||||
self.assertLess(max(limbs), 2**63)
|
||||
|
||||
def test_limb_helpers_match_original_and_aliases(self):
|
||||
values = vectors(0xC0A5, 4000)
|
||||
for index, first in enumerate(values):
|
||||
second = values[-1 - index]
|
||||
for name in ("fe_add", "fe_sub", "fe_copy", "fe_neg"):
|
||||
binary = name in ("fe_add", "fe_sub")
|
||||
reference = Field()
|
||||
inputs = [Field(*first)] + ([Field(*second)] if binary else [])
|
||||
getattr(self.libraries[0], name)(reference, *inputs)
|
||||
for alias in range(3 if binary else 2):
|
||||
inputs = [Field(*first)] + ([Field(*second)] if binary else [])
|
||||
output = inputs[alias] if alias < len(inputs) else Field()
|
||||
getattr(self.libraries[1], name)(output, *inputs)
|
||||
self.assertEqual(list(output), list(reference), (index, name, alias))
|
||||
for name in ("fe_cmov", "fe_cswap"):
|
||||
for select in (0, 1):
|
||||
expected_a, expected_b = Field(*first), Field(*second)
|
||||
getattr(self.libraries[0], name)(expected_a, expected_b, select)
|
||||
a, b = Field(*first), Field(*second)
|
||||
getattr(self.libraries[1], name)(a, b, select)
|
||||
self.assertEqual((list(a), list(b)), (list(expected_a), list(expected_b)))
|
||||
a = Field(*first)
|
||||
getattr(self.libraries[1], name)(a, a, select)
|
||||
self.assertEqual(list(a), first)
|
||||
|
||||
def test_compact_products_under_address_and_undefined_sanitizers(self):
|
||||
values = vectors(0xC0FE, 128)
|
||||
# Keep every edge/carry-boundary vector and a bounded random selection.
|
||||
rows = []
|
||||
for index, first in enumerate(values):
|
||||
second = values[-1 - index]
|
||||
outputs = []
|
||||
for name in ("fe_mul", "fe_sq", "fe_sq2"):
|
||||
output = Field()
|
||||
arguments = (output, Field(*first), Field(*second)) if name == "fe_mul" else (output, Field(*first))
|
||||
getattr(self.libraries[0], name)(*arguments)
|
||||
outputs.append(list(output))
|
||||
rows.append("{" + ",".join("{" + ",".join(map(str, row)) + "}"
|
||||
for row in (first, second, *outputs)) + "}")
|
||||
source = r'''
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "fe.h"
|
||||
struct Vector { fe a, b, product, square, twice; };
|
||||
static const struct Vector vectors[] = { @VECTORS@ };
|
||||
int main(void) {
|
||||
unsigned int i, alias;
|
||||
for (i = 0; i < sizeof(vectors) / sizeof(vectors[0]); ++i) {
|
||||
for (alias = 0; alias < 3; ++alias) {
|
||||
fe a, b, separate;
|
||||
int32_t *output;
|
||||
memcpy(a, vectors[i].a, sizeof(a));
|
||||
memcpy(b, vectors[i].b, sizeof(b));
|
||||
output = alias == 0 ? a : alias == 1 ? b : separate;
|
||||
fe_mul(output, a, b);
|
||||
assert(memcmp(output, vectors[i].product, sizeof(fe)) == 0);
|
||||
memcpy(a, vectors[i].a, sizeof(a));
|
||||
output = alias == 0 ? a : separate;
|
||||
fe_sq(output, a);
|
||||
assert(memcmp(output, vectors[i].square, sizeof(fe)) == 0);
|
||||
memcpy(a, vectors[i].a, sizeof(a));
|
||||
fe_sq2(output, a);
|
||||
assert(memcmp(output, vectors[i].twice, sizeof(fe)) == 0);
|
||||
memcpy(a, vectors[i].a, sizeof(a));
|
||||
memcpy(b, vectors[i].b, sizeof(b));
|
||||
fe_add(separate, a, b);
|
||||
for (unsigned int limb = 0; limb < 10; ++limb)
|
||||
assert(separate[limb] == a[limb] + b[limb]);
|
||||
fe_sub(separate, a, b);
|
||||
for (unsigned int limb = 0; limb < 10; ++limb)
|
||||
assert(separate[limb] == a[limb] - b[limb]);
|
||||
fe_neg(separate, a);
|
||||
for (unsigned int limb = 0; limb < 10; ++limb)
|
||||
assert(separate[limb] == -a[limb]);
|
||||
fe_copy(separate, a);
|
||||
assert(memcmp(separate, a, sizeof(fe)) == 0);
|
||||
fe_cmov(a, b, 0);
|
||||
assert(memcmp(a, vectors[i].a, sizeof(fe)) == 0);
|
||||
fe_cmov(a, b, 1);
|
||||
assert(memcmp(a, b, sizeof(fe)) == 0);
|
||||
memcpy(a, vectors[i].a, sizeof(a));
|
||||
fe_cswap(a, b, 0);
|
||||
assert(memcmp(a, vectors[i].a, sizeof(fe)) == 0);
|
||||
fe_cswap(a, b, 1);
|
||||
assert(memcmp(a, vectors[i].b, sizeof(fe)) == 0);
|
||||
assert(memcmp(b, vectors[i].a, sizeof(fe)) == 0);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
'''.replace("@VECTORS@", ",\n".join(rows))
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
harness = Path(directory) / "fields.c"
|
||||
harness.write_text(source)
|
||||
executable = Path(directory) / "fields"
|
||||
subprocess.run(["cc", "-std=c99", "-O1", "-g", "-Wall", "-Wextra", "-Werror",
|
||||
"-fsanitize=address,undefined", "-fno-sanitize-recover=all", "-no-pie",
|
||||
"-DED25519_COMPACT_FIELD=1", "-I", str(ED25519),
|
||||
str(harness), str(ED25519 / "fe.c"), "-o", str(executable)], check=True)
|
||||
subprocess.run([str(executable)], check=True)
|
||||
|
||||
def test_only_stm32_companion_recipes_opt_in(self):
|
||||
profiles = []
|
||||
for ini in (ROOT / "variants").glob("*/platformio.ini"):
|
||||
section = ""
|
||||
for line in ini.read_text().splitlines():
|
||||
if line.startswith("["):
|
||||
section = line
|
||||
if "ED25519_COMPACT_FIELD" in line:
|
||||
profiles.append((ini.parent.name, section))
|
||||
self.assertEqual(sorted(profiles), [
|
||||
("rak3x72", "[env:RAK_3x72_companion_radio_usb]"),
|
||||
("wio-e5-mini", "[env:wio-e5-mini_companion_radio_usb]"),
|
||||
])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -55,6 +55,10 @@ class STM32CompanionProfileTest(unittest.TestCase):
|
||||
self.assertIn("-Oz", self.companion["build_flags"])
|
||||
self.assertEqual(self.companion["build_unflags"].split(), ["-Os"])
|
||||
|
||||
def test_size_profile_uses_equivalent_fixed_loop_field_products(self):
|
||||
self.assertRegex(self.companion["build_flags"],
|
||||
r"-D\s+ED25519_COMPACT_FIELD=1\b")
|
||||
|
||||
|
||||
class WioSTM32CompanionProfileTest(STM32CompanionProfileTest):
|
||||
variant = "wio-e5-mini"
|
||||
|
||||
@@ -72,6 +72,7 @@ build_flags = ${rak3x72.build_flags}
|
||||
-D HAL_TIM_MODULE_ONLY
|
||||
-D ED25519_COMPACT_SHA512=1 ; same hash rounds, compact loop preserves the filesystem boundary
|
||||
-D ED25519_COMPACT_SCALAR=1 ; same ref10 arithmetic with fixed loops instead of unrolled code
|
||||
-D ED25519_COMPACT_FIELD=1 ; share fixed-loop field products without cutting features or storage
|
||||
-Oz
|
||||
-flto
|
||||
-fno-reorder-blocks
|
||||
|
||||
@@ -85,6 +85,7 @@ build_flags = ${lora_e5_mini.build_flags}
|
||||
-D HAL_TIM_MODULE_ONLY
|
||||
-D ED25519_COMPACT_SHA512=1 ; same hash rounds, compact loop preserves the filesystem boundary
|
||||
-D ED25519_COMPACT_SCALAR=1 ; same ref10 arithmetic with fixed loops instead of unrolled code
|
||||
-D ED25519_COMPACT_FIELD=1 ; share fixed-loop field products without cutting features or storage
|
||||
-Oz
|
||||
-flto
|
||||
-fno-reorder-blocks
|
||||
|
||||
Reference in new issue
Block a user