commit 905e9986566e33998a675eedb91f77c163f1a8f9
parent c1b553ed5326df5a109d76910c48428d2debaed3
Author: lukechampine <luke.champine@gmail.com>
Date: Thu, 14 Apr 2022 13:55:25 -0400
crypto::ed25519: add package
Signed-off-by: lukechampine <luke.champine@gmail.com>
Diffstat:
5 files changed, 583 insertions(+), 0 deletions(-)
diff --git a/crypto/ed25519/+test.ha b/crypto/ed25519/+test.ha
@@ -0,0 +1,47 @@
+// License: MPL-2.0
+// (c) 2022 Luke Champine <luke.champine@gmail.com>
+use bytes;
+use encoding::hex;
+use strings;
+
+@test fn roundtrip() void = {
+ let seed: seed = [1...];
+ let priv_key: privatekey = [0...];
+ private_init(&priv_key, &seed);
+ let pub_key = skey_getpublic(&priv_key);
+
+ const msg = strings::toutf8("hello, world!");
+ let sig = sign(&priv_key, msg);
+ assert(verify(&pub_key, msg, &sig));
+
+ let bad_pub_key = pub_key;
+ bad_pub_key[0] ^= 1;
+ const bad_msg = strings::toutf8("HELLO, WORLD!");
+ let bad_sig = sig;
+ bad_sig[0] ^= 1;
+
+ assert(!verify(&bad_pub_key, msg, &sig));
+ assert(!verify(&pub_key, bad_msg, &sig));
+ assert(!verify(&pub_key, msg, &bad_sig));
+};
+
+@test fn golden() void = {
+ // TODO https://todo.sr.ht/~sircmpwn/hare/596
+ let priv_key: privatekey = [0...];
+ let priv_key_u8 = hex::decode("8ed7a797b9cea8a8370d419136bcdf683b759d2e3c6947f17e13e2485aa9d420b49f3a78b1c6a7fca8f3466f33bc0e929f01fba04306c2a7465f46c3759316d9") as []u8;
+ priv_key[..] = priv_key_u8[..];
+ free(priv_key_u8);
+ const pub_key = skey_getpublic(&priv_key);
+
+ let msg = hex::decode("a750c232933dc14b1184d86d8b4ce72e16d69744ba69818b6ac33b1d823bb2c3") as []u8;
+ defer free(msg);
+
+ let good_sig: privatekey = [0...];
+ let sig_u8 = hex::decode("04266c033b91c1322ceb3446c901ffcf3cc40c4034e887c9597ca1893ba7330becbbd8b48142ef35c012c6ba51a66df9308cb6268ad6b1e4b03e70102495790b") as []u8;
+ good_sig[..] = sig_u8[..];
+ free(sig_u8);
+
+ const sig = sign(&priv_key, msg);
+ assert(bytes::equal(sig, good_sig));
+ assert(verify(&pub_key, msg, &sig));
+};
diff --git a/crypto/ed25519/ed25519.ha b/crypto/ed25519/ed25519.ha
@@ -0,0 +1,128 @@
+// License: MPL-2.0
+// (c) 2022 Luke Champine <luke.champine@gmail.com>
+
+// Implements the Ed25519 signature scheme.
+//
+// This implementation is a straightforward port of TweetNaCl,
+// with the API of crypto/ed25519 from the Go standard library.
+use bytes;
+use crypto::sha512;
+use hash;
+
+// The size of an Ed25519 seed.
+export def SEEDSZ: size = 32;
+
+// The size of an Ed25519 public key.
+export def PUBLICKEYSZ: size = 32;
+
+// The size of an Ed25519 private key.
+export def PRIVATEKEYSZ: size = 64;
+
+// The size of an Ed25519 signature.
+export def SIGNATURESZ: size = 64;
+
+export type privatekey = [PRIVATEKEYSZ]u8;
+export type publickey =[PUBLICKEYSZ]u8;
+export type seed = [SEEDSZ]u8;
+
+// Derives a new Ed25519 private key from a given seed. The seed must be
+// initialized to cryptographically random data; [[crypto::random]] is
+// recommended for this purpose.
+export fn private_init(out: *privatekey, seed: *seed) void = {
+ let h: [64]u8 = [0...];
+ let sha = sha512::sha512();
+ hash::write(&sha, seed[..]);
+ hash::finish(&sha, h[..]);
+ hash::close(&sha);
+
+ let s: scalar = [0...];
+ s[..] = h[..SCALARSZ];
+ scalar_clamp(&s);
+
+ let A = point { ... };
+ scalarmult_base(&A, &s);
+ let A_bytes: [POINTSZ]u8 = [0...];
+ point_encode(&A_bytes, &A);
+
+ out[0..SEEDSZ] = seed[..];
+ out[SEEDSZ..PRIVATEKEYSZ] = A_bytes[..];
+};
+
+// Derive the public key for a given private key.
+export fn skey_getpublic(priv: *privatekey) publickey = {
+ let pk: publickey = [0...];
+ pk[0..] = priv[SEEDSZ..];
+ return pk;
+};
+
+// Signs a message with a private key, returning the signature.
+export fn sign(priv: *privatekey, msg: []u8) [SIGNATURESZ]u8 = {
+ let h: [64]u8 = [0...];
+ let sha = sha512::sha512();
+ hash::write(&sha, priv[0..SEEDSZ]);
+ hash::finish(&sha, h);
+ let esk: scalar = [0...];
+ esk[..] = h[0..32];
+ scalar_clamp(&esk);
+
+ hash::reset(&sha);
+ hash::write(&sha, h[32..64]);
+ hash::write(&sha, msg);
+ let msg_digest: [64]u8 = [0...];
+ hash::finish(&sha, msg_digest);
+ let msg_reduced: scalar = [0...];
+ scalar_reduce(&msg_reduced, &msg_digest);
+
+ let R = point {...};
+ scalarmult_base(&R, &msg_reduced);
+ let R_bytes: [POINTSZ]u8 = [0...];
+ point_encode(&R_bytes, &R);
+
+ hash::reset(&sha);
+ hash::write(&sha, R_bytes[..]);
+ hash::write(&sha, priv[32..64]);
+ hash::write(&sha, msg);
+ let hram: [64]u8 = [0...];
+ hash::finish(&sha, hram);
+ hash::close(&sha);
+ let hram_reduced: scalar = [0...];
+ scalar_reduce(&hram_reduced, &hram);
+
+ let s: scalar = [0...];
+ scalar_multiply_add(&s, &hram_reduced, &esk, &msg_reduced);
+
+ let sig: [SIGNATURESZ]u8 =[0...];
+ sig[0..32] = R_bytes[..];
+ sig[32..64] = s[..];
+ return sig;
+};
+
+// Given a public key, verifies a signature produced with the
+// corresponding private key for a given message, returning true if the
+// signature is valid and false otherwise.
+export fn verify(pub: *publickey, msg: []u8, sig: *[SIGNATURESZ]u8) bool = {
+ let A = point { ... };
+ if (!point_decode(&A, pub)) {
+ return false;
+ };
+
+ let sha = sha512::sha512();
+ hash::write(&sha, sig[0..32]);
+ hash::write(&sha, pub[..]);
+ hash::write(&sha, msg);
+ let hram: [64]u8 = [0...];
+ hash::finish(&sha, hram);
+
+ let hram_reduced: scalar = [0...];
+ scalar_reduce(&hram_reduced, &hram);
+ let check_R = point { ... };
+ scalarmult(&check_R, &A, &hram_reduced);
+
+ let s: scalar = [0...];
+ s[..] = sig[32..64];
+ scalarmult_base(&A, &s);
+ point_add(&check_R, &check_R, &A);
+ let R_bytes: [POINTSZ]u8 = [0...];
+ point_encode(&R_bytes, &check_R);
+ return bytes::equal(R_bytes, sig[0..32]);
+};
diff --git a/crypto/ed25519/edwards25519.ha b/crypto/ed25519/edwards25519.ha
@@ -0,0 +1,361 @@
+// License: MPL-2.0
+// (c) 2022 Luke Champine <luke.champine@gmail.com>
+
+def FIELDSZ: size = 16;
+type elem = [FIELDSZ]i64;
+
+const feZero: elem = [0...];
+const feOne: elem = [1, 0...];
+const D: elem = [0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203];
+const D2: elem = [0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406];
+const X: elem = [0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169];
+const Y: elem = [0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666];
+const I: elem = [0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83];
+
+fn fe_reduce(fe: *elem) void = {
+ let carry: i64 = 0;
+ for (let i = 0z; i < FIELDSZ; i += 1) {
+ carry = fe[i] >> 16;
+ fe[i] -= (carry << 16);
+ if (i+1 < FIELDSZ) {
+ fe[i + 1] += carry;
+ } else {
+ fe[0] += (38 * carry);
+ };
+ };
+};
+
+fn fe_add(out: *elem, a: const *elem, b: const *elem) *elem = {
+ for (let i = 0z; i < FIELDSZ; i += 1) {
+ out[i] = a[i] + b[i];
+ };
+ return out;
+};
+
+fn fe_sub(out: *elem, a: const *elem, b: const *elem) *elem = {
+ for (let i = 0z; i < FIELDSZ; i += 1) {
+ out[i] = a[i] - b[i];
+ };
+ return out;
+};
+
+fn fe_negate(out: *elem, a: const *elem) *elem = {
+ return fe_sub(out, &feZero, a);
+};
+
+fn fe_mul(out: *elem, a: const *elem, b: const *elem) *elem = {
+ let prod: [31]i64 = [0...];
+ for (let i = 0z; i < FIELDSZ; i += 1) {
+ for (let j = 0z; j < FIELDSZ; j += 1) {
+ prod[i + j] += a[i] * b[j];
+ };
+ };
+ for (let i = 0; i < 15; i += 1) {
+ prod[i] += (38 * prod[i + 16]);
+ };
+ out[0..FIELDSZ] = prod[0..FIELDSZ];
+ fe_reduce(out);
+ fe_reduce(out);
+ return out;
+};
+
+fn fe_square(out: *elem, a: const *elem) *elem = {
+ return fe_mul(out, a, a);
+};
+
+// out = i ** (2**252 - 3)
+fn fe_pow2523(out: *elem, a: *elem) *elem = {
+ let c: elem = [0...];
+ c[..] = a[..];
+ for (let i = 250i; i >= 0; i -= 1) {
+ fe_square(&c, &c);
+ if (i != 1) {
+ fe_mul(&c, &c, a);
+ };
+ };
+ out[..] = c[..];
+ return out;
+};
+
+fn fe_inv(out: *elem, a: const *elem) *elem = {
+ let c: elem = [0...];
+ c[..] = a[..];
+ for (let i = 253i; i >= 0; i -= 1) {
+ fe_square(&c, &c);
+ if (i != 2 && i != 4) {
+ fe_mul(&c, &c, a);
+ };
+ };
+ out[..] = c[..];
+ return out;
+};
+
+fn fe_parity(a: const *elem) u8 = {
+ let d: scalar = [0...];
+ fe_encode(&d, a);
+ return d[0]&1;
+};
+
+// a == b -> 0
+// a != b -> 1
+fn fe_cmp(a: const *elem, b: const *elem) u8 = {
+ let x: scalar = [0...];
+ fe_encode(&x, a);
+ let y: scalar = [0...];
+ fe_encode(&y, a);
+
+ // constant-time compare
+ let d: u32 = 0;
+ for (let i = 0z; i < SCALARSZ; i += 1) {
+ d |= x[i] ^ y[i];
+ };
+ return (1 & ((d - 1) >> 8): u8) - 1;
+};
+
+// swap p and q if bit is 1, otherwise noop
+fn fe_swap(p: *elem, q: *elem, bit: u8) void = {
+ let c = ~(bit: u64 - 1): i64;
+ for (let i = 0z; i < FIELDSZ; i += 1) {
+ let t = c & (p[i] ^ q[i]);
+ p[i] ^= t;
+ q[i] ^= t;
+ };
+};
+
+fn fe_encode(out: *scalar, a: const *elem) void = {
+ let m: elem = [0...];
+ let t: elem = *a;
+
+ fe_reduce(&t);
+ fe_reduce(&t);
+ fe_reduce(&t);
+
+ for (let _i = 0; _i < 2; _i += 1) {
+ m[0] = t[0] - 0xffed;
+ for (let i = 1z; i < 15; i += 1) {
+ m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);
+ m[i - 1] &= 0xffff;
+ };
+ m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);
+ let b = ((m[15] >> 16): u8) & 1;
+ m[14] &= 0xffff;
+ fe_swap(&t, &m, 1-b);
+ };
+
+ for (let i = 0z; i < FIELDSZ; i += 1) {
+ out[2*i+0] = (t[i] & 0xff) : u8;
+ out[2*i+1] = (t[i] >> 8) : u8;
+ };
+};
+
+fn fe_decode(fe: *elem, in: *[SCALARSZ]u8) *elem = {
+ for (let i = 0z; i < FIELDSZ; i += 1) {
+ fe[i] = in[2 * i] : i64 + ((in[2 * i + 1] : i64) << 8);
+ };
+ fe[15] &= 0x7fff;
+ return fe;
+};
+
+
+def SCALARSZ: size = 32;
+type scalar = [SCALARSZ]u8;
+
+const L: scalar = [
+ 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2,
+ 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+];
+
+fn scalar_clamp(s: *scalar) void = {
+ s[0] &= 248;
+ s[31] &= 127;
+ s[31] |= 64;
+};
+
+// r = x % -1
+fn scalar_mod_L(r: *scalar, x: *[64]i64) void = {
+ for (let i: i64 = 63; i >= 32; i -= 1) {
+ let carry: i64 = 0;
+ let j = i - 32;
+ for (j < i - 12; j += 1) {
+ x[j] += carry - 16 * x[i] * (L[j - (i - 32)]: i64);
+ carry = (x[j] + 128) >> 8;
+ x[j] -= carry << 8;
+ };
+ x[j] += carry;
+ x[i] = 0;
+ };
+
+ let carry: i64 = 0;
+ for (let j = 0; j < 32; j += 1) {
+ x[j] += carry - (x[31] >> 4) * (L[j]: i64);
+ carry = x[j] >> 8;
+ x[j] &= 255;
+ };
+ for (let j = 0; j < 32; j += 1) {
+ x[j] -= carry * (L[j]: i64);
+ };
+ for (let i = 0; i < 32; i += 1) {
+ x[i+1] += x[i] >> 8;
+ r[i] = (x[i]&255): u8;
+ };
+};
+
+fn scalar_reduce(r: *scalar, h: *[64]u8) void = {
+ let x: [64]i64 = [0...];
+ for (let i = 0; i < 64; i += 1) {
+ x[i] = h[i]: i64;
+ };
+ scalar_mod_L(r, &x);
+};
+
+// s = a*b + c
+fn scalar_multiply_add(s: *scalar, a: *scalar, b: *scalar, c: *scalar) void = {
+ let x: [64]i64 = [0...];
+ for (let i = 0; i < 32; i += 1) {
+ for (let j = 0; j < 32; j += 1) {
+ x[i+j] += (a[i]: i64) * (b[j]: i64);
+ };
+ };
+ for (let i = 0; i < 32; i += 1) {
+ x[i] += (c[i]: i64);
+ };
+ scalar_mod_L(s, &x);
+};
+
+
+def POINTSZ: size = 32;
+
+type point = struct {
+ x: elem,
+ y: elem,
+ z: elem,
+ t: elem,
+};
+
+// out = p += q
+fn point_add(out: *point, p: *point, q: *point) *point = {
+ let a: elem = [0...];
+ let b: elem = [0...];
+ let c: elem = [0...];
+ let d: elem = [0...];
+ let t: elem = [0...];
+ let e: elem = [0...];
+ let f: elem = [0...];
+ let g: elem = [0...];
+ let h: elem = [0...];
+
+ fe_sub(&a, &p.y, &p.x);
+ fe_sub(&t, &q.y, &q.x);
+ fe_mul(&a, &a, &t);
+ fe_add(&b, &p.x, &p.y);
+ fe_add(&t, &q.x, &q.y);
+ fe_mul(&b, &b, &t);
+ fe_mul(&c, &p.t, &q.t);
+ fe_mul(&c, &c, &D2);
+ fe_mul(&d, &p.z, &q.z);
+ fe_add(&d, &d, &d);
+ fe_sub(&e, &b, &a);
+ fe_sub(&f, &d, &c);
+ fe_add(&g, &d, &c);
+ fe_add(&h, &b, &a);
+
+ fe_mul(&out.x, &e, &f);
+ fe_mul(&out.y, &h, &g);
+ fe_mul(&out.z, &g, &f);
+ fe_mul(&out.t, &e, &h);
+ return out;
+};
+
+// swap p and q if bit is 1, otherwise noop
+fn point_swap(p: *point, q: *point, bit: u8) void = {
+ fe_swap(&p.x, &q.x, bit);
+ fe_swap(&p.y, &q.y, bit);
+ fe_swap(&p.z, &q.z, bit);
+ fe_swap(&p.t, &q.t, bit);
+};
+
+// p = q * s
+fn scalarmult(p: *point, q: *point, s: const *scalar) *point = {
+ p.x[..] = feZero[..];
+ p.y[..] = feOne[..];
+ p.z[..] = feOne[..];
+ p.t[..] = feZero[..];
+ for (let i = 255; i >= 0; i -= 1) {
+ let b: u8 = (s[i/8]>>((i: u8)&7))&1;
+ point_swap(p, q, b);
+ point_add(q, q, p);
+ point_add(p, p, p);
+ point_swap(p, q, b);
+ };
+ return p;
+};
+
+// p = B * s
+fn scalarmult_base(p: *point, s: const *scalar) *point = {
+ let B = point {...};
+ B.x[..] = X[..];
+ B.y[..] = Y[..];
+ B.z[..] = feOne[..];
+ fe_mul(&B.t, &X, &Y);
+
+ return scalarmult(p, &B, s);
+};
+
+fn point_encode(out: *[POINTSZ]u8, p: *point) void = {
+ let tx: elem = [0...];
+ let ty: elem = [0...];
+ let zi: elem = [0...];
+ fe_inv(&zi, &p.z);
+ fe_mul(&tx, &p.x, &zi);
+ fe_mul(&ty, &p.y, &zi);
+ fe_encode(out, &ty);
+ out[31] ^= fe_parity(&tx) << 7;
+};
+
+fn point_decode(p: *point, in: *[POINTSZ]u8) bool = {
+ let t: elem = [0...];
+ let chk: elem = [0...];
+ let num: elem = [0...];
+ let den: elem = [0...];
+ let den2: elem = [0...];
+ let den4: elem = [0...];
+ let den6: elem = [0...];
+ p.z[..] = feOne[..];
+ fe_decode(&p.y, in);
+ fe_square(&num, &p.y);
+ fe_mul(&den, &num, &D);
+ fe_sub(&num, &num, &p.z);
+ fe_add(&den, &p.z, &den);
+
+ fe_square(&den2, &den);
+ fe_square(&den4, &den2);
+ fe_mul(&den6, &den4, &den2);
+ fe_mul(&t, &den6, &num);
+ fe_mul(&t, &t, &den);
+
+ fe_pow2523(&t, &t);
+ fe_mul(&t, &t, &num);
+ fe_mul(&t, &t, &den);
+ fe_mul(&t, &t, &den);
+ fe_mul(&p.x, &t, &den);
+
+ fe_square(&chk, &p.x);
+ fe_mul(&chk, &chk, &den);
+ if (fe_cmp(&chk, &num) == 1) {
+ fe_mul(&p.x, &p.x, &I);
+ };
+
+ fe_square(&chk, &p.x);
+ fe_mul(&chk, &chk, &den);
+ if (fe_cmp(&chk, &num) == 1) {
+ return false;
+ };
+
+ if (fe_parity(&p.x) == (in[31]>>7)) {
+ fe_negate(&p.x, &p.x);
+ };
+
+ fe_mul(&p.t, &p.x, &p.y);
+ return true;
+};
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -440,6 +440,17 @@ crypto_curve25519() {
fi
}
+crypto_ed25519() {
+ if [ $testing -eq 0 ]
+ then
+ gen_srcs crypto::ed25519 ed25519.ha edwards25519.ha
+ gen_ssa crypto::ed25519
+ else
+ gen_srcs crypto::ed25519 ed25519.ha edwards25519.ha +test.ha
+ gen_ssa crypto::ed25519 bytes crypto::sha512 hash encoding::hex strings
+ fi
+}
+
dirs() {
gen_srcs dirs \
xdg.ha
@@ -1307,6 +1318,7 @@ crypto::sha1
crypto::sha256
crypto::sha512
crypto::curve25519
+crypto::ed25519
datetime
dirs
encoding::base64
diff --git a/stdlib.mk b/stdlib.mk
@@ -264,6 +264,12 @@ stdlib_deps_any+=$(stdlib_crypto_curve25519_any)
stdlib_crypto_curve25519_linux=$(stdlib_crypto_curve25519_any)
stdlib_crypto_curve25519_freebsd=$(stdlib_crypto_curve25519_any)
+# gen_lib crypto::ed25519 (any)
+stdlib_crypto_ed25519_any=$(HARECACHE)/crypto/ed25519/crypto_ed25519-any.o
+stdlib_deps_any+=$(stdlib_crypto_ed25519_any)
+stdlib_crypto_ed25519_linux=$(stdlib_crypto_ed25519_any)
+stdlib_crypto_ed25519_freebsd=$(stdlib_crypto_ed25519_any)
+
# gen_lib datetime (any)
stdlib_datetime_any=$(HARECACHE)/datetime/datetime-any.o
stdlib_deps_any+=$(stdlib_datetime_any)
@@ -935,6 +941,17 @@ $(HARECACHE)/crypto/curve25519/crypto_curve25519-any.ssa: $(stdlib_crypto_curve2
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::curve25519 \
-t$(HARECACHE)/crypto/curve25519/crypto_curve25519.td $(stdlib_crypto_curve25519_any_srcs)
+# crypto::ed25519 (+any)
+stdlib_crypto_ed25519_any_srcs= \
+ $(STDLIB)/crypto/ed25519/ed25519.ha \
+ $(STDLIB)/crypto/ed25519/edwards25519.ha
+
+$(HARECACHE)/crypto/ed25519/crypto_ed25519-any.ssa: $(stdlib_crypto_ed25519_any_srcs) $(stdlib_rt)
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(HARECACHE)/crypto/ed25519
+ @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::ed25519 \
+ -t$(HARECACHE)/crypto/ed25519/crypto_ed25519.td $(stdlib_crypto_ed25519_any_srcs)
+
# datetime (+any)
stdlib_datetime_any_srcs= \
$(STDLIB)/datetime/arithmetic.ha \
@@ -2236,6 +2253,12 @@ testlib_deps_any+=$(testlib_crypto_curve25519_any)
testlib_crypto_curve25519_linux=$(testlib_crypto_curve25519_any)
testlib_crypto_curve25519_freebsd=$(testlib_crypto_curve25519_any)
+# gen_lib crypto::ed25519 (any)
+testlib_crypto_ed25519_any=$(TESTCACHE)/crypto/ed25519/crypto_ed25519-any.o
+testlib_deps_any+=$(testlib_crypto_ed25519_any)
+testlib_crypto_ed25519_linux=$(testlib_crypto_ed25519_any)
+testlib_crypto_ed25519_freebsd=$(testlib_crypto_ed25519_any)
+
# gen_lib datetime (any)
testlib_datetime_any=$(TESTCACHE)/datetime/datetime-any.o
testlib_deps_any+=$(testlib_datetime_any)
@@ -2924,6 +2947,18 @@ $(TESTCACHE)/crypto/curve25519/crypto_curve25519-any.ssa: $(testlib_crypto_curve
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::curve25519 \
-t$(TESTCACHE)/crypto/curve25519/crypto_curve25519.td $(testlib_crypto_curve25519_any_srcs)
+# crypto::ed25519 (+any)
+testlib_crypto_ed25519_any_srcs= \
+ $(STDLIB)/crypto/ed25519/ed25519.ha \
+ $(STDLIB)/crypto/ed25519/edwards25519.ha \
+ $(STDLIB)/crypto/ed25519/+test.ha
+
+$(TESTCACHE)/crypto/ed25519/crypto_ed25519-any.ssa: $(testlib_crypto_ed25519_any_srcs) $(testlib_rt) $(testlib_bytes_$(PLATFORM)) $(testlib_crypto_sha512_$(PLATFORM)) $(testlib_hash_$(PLATFORM)) $(testlib_encoding_hex_$(PLATFORM)) $(testlib_strings_$(PLATFORM))
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(TESTCACHE)/crypto/ed25519
+ @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::ed25519 \
+ -t$(TESTCACHE)/crypto/ed25519/crypto_ed25519.td $(testlib_crypto_ed25519_any_srcs)
+
# datetime (+any)
testlib_datetime_any_srcs= \
$(STDLIB)/datetime/arithmetic.ha \