mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Implement Ed25519 (#2297)
* Implement Ed25519 * Overloading addition with a pointer would not work. * Added @addr macro. --------- Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
This commit is contained in:
@@ -96,6 +96,14 @@ macro anycast(any v, $Type) @builtin
|
||||
return ($Type*)v.ptr;
|
||||
}
|
||||
|
||||
macro @addr(#val) @builtin
|
||||
{
|
||||
$if $defined(&#val):
|
||||
return &#val;
|
||||
$else
|
||||
return &&#val;
|
||||
$endif
|
||||
}
|
||||
fn bool print_backtrace(String message, int backtraces_to_ignore) @if (env::NATIVE_STACKTRACE) => @stack_mem(0x1100; Allocator smem)
|
||||
{
|
||||
void*[256] buffer;
|
||||
|
||||
737
lib/std/crypto/ed25519.c3
Normal file
737
lib/std/crypto/ed25519.c3
Normal file
@@ -0,0 +1,737 @@
|
||||
/*
|
||||
Ed25519 Digital Signature Algorithm
|
||||
*/
|
||||
|
||||
module std::crypto::ed25519;
|
||||
import std::hash::sha512;
|
||||
|
||||
alias PrivateKey = char[32];
|
||||
alias PublicKey = char[PrivateKey.len];
|
||||
alias Signature = char[2 * PublicKey.len];
|
||||
|
||||
<*
|
||||
Generate a public key from a private key.
|
||||
|
||||
@param [in] private_key : "32 bytes of cryptographically secure random data"
|
||||
@require private_key.len == PrivateKey.len
|
||||
*>
|
||||
fn PublicKey public_keygen(char[] private_key)
|
||||
{
|
||||
return pack(&&unproject(&&(BASE * expand_private_key(private_key)[:FBaseInt.len])));
|
||||
}
|
||||
|
||||
<*
|
||||
Sign a message.
|
||||
|
||||
@param [in] message
|
||||
@param [in] private_key
|
||||
@param [in] public_key
|
||||
@require private_key.len == PrivateKey.len
|
||||
@require public_key.len == PublicKey.len
|
||||
*>
|
||||
fn Signature sign(char[] message, char[] private_key, char[] public_key)
|
||||
{
|
||||
Signature r @noinit;
|
||||
|
||||
char[*] exp = expand_private_key(private_key);
|
||||
|
||||
Sha512 sha @noinit;
|
||||
sha.init();
|
||||
|
||||
sha.update(exp[FBaseInt.len..]);
|
||||
sha.update(message);
|
||||
|
||||
FBaseInt k = from_bytes(&&sha.final());
|
||||
|
||||
r[:F25519Int.len] = pack(&&unproject(&&(BASE * k[..])))[..];
|
||||
|
||||
sha.init();
|
||||
|
||||
sha.update(r[:F25519Int.len]);
|
||||
sha.update(public_key);
|
||||
sha.update(message);
|
||||
|
||||
FBaseInt z = from_bytes(&&sha.final());
|
||||
FBaseInt e = from_bytes(exp[:FBaseInt.len]);
|
||||
|
||||
r[F25519Int.len..] = (z * e + k)[..];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
<*
|
||||
Verify the signature of a message.
|
||||
|
||||
@param [in] message
|
||||
@param [in] signature
|
||||
@param [in] public_key
|
||||
@require signature.len == Signature.len
|
||||
@require public_key.len == PublicKey.len
|
||||
*>
|
||||
fn bool verify(char[] message, char[] signature, char[] public_key)
|
||||
{
|
||||
char ok = 1;
|
||||
|
||||
F25519Int lhs = pack(&&unproject(&&(BASE * signature[F25519Int.len..])));
|
||||
|
||||
Unpacking unp_p = unpack_on_curve((F25519Int*)public_key);
|
||||
Projection p = project(&unp_p.point);
|
||||
ok &= unp_p.on_curve;
|
||||
|
||||
Sha512 sha @noinit;
|
||||
sha.init();
|
||||
|
||||
sha.update(signature[:F25519Int.len]);
|
||||
sha.update(public_key);
|
||||
sha.update(message);
|
||||
|
||||
FBaseInt z = from_bytes(&&sha.final());
|
||||
|
||||
p = p * z[..];
|
||||
|
||||
Unpacking unp_q = unpack_on_curve((F25519Int*)signature[:F25519Int.len]);
|
||||
Projection q = project(&unp_q.point);
|
||||
ok &= unp_q.on_curve;
|
||||
|
||||
p = p + q;
|
||||
|
||||
F25519Int rhs = pack(&&unproject(&p));
|
||||
|
||||
return (bool)(ok & eq(&lhs, &rhs));
|
||||
}
|
||||
|
||||
// Base point for Ed25519. Generate a subgroup of order 2^252+0x14def9dea2f79cd65812631a5cf5d3ed
|
||||
const Projection BASE @private =
|
||||
{
|
||||
x"1ad5258f602d56c9 b2a7259560c72c69 5cdcd6fd31e2a4c0 fe536ecdd3366921",
|
||||
x"5866666666666666 6666666666666666 6666666666666666 6666666666666666",
|
||||
x"a3ddb7a5b38ade6d f5525177809ff020 7de3ab648e4eea66 65768bd70f5f8767",
|
||||
ONE
|
||||
};
|
||||
|
||||
<*
|
||||
Compute the pruned SHA-512 hash of a private key.
|
||||
|
||||
@param [in] private_key
|
||||
@require private_key.len == PrivateKey.len
|
||||
*>
|
||||
fn char[sha512::HASH_SIZE] expand_private_key(char[] private_key) @local
|
||||
{
|
||||
char[*] r = sha512::hash(private_key);
|
||||
|
||||
r[0] &= 0b11111000;
|
||||
r[FBaseInt.len - 1] &= 0b01111111;
|
||||
r[FBaseInt.len - 1] |= 0b01000000;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Operations on the twisted Edwards curve -x^2+y^2=1-121665/121666*x^2*y^2 over the prime field F_(2^255-19) (edwards25519)
|
||||
The set of F_(2^255-19)-rational curve points is a group of order 2^3*(2^252+0x14def9dea2f79cd65812631a5cf5d3ed)
|
||||
*/
|
||||
|
||||
module std::crypto::ed25519 @private;
|
||||
|
||||
// Affine coordinates.
|
||||
struct Point
|
||||
{
|
||||
F25519Int x;
|
||||
F25519Int y;
|
||||
}
|
||||
|
||||
// Projective coordinates.
|
||||
struct Projection
|
||||
{
|
||||
F25519Int x;
|
||||
F25519Int y;
|
||||
F25519Int t;
|
||||
F25519Int z;
|
||||
}
|
||||
|
||||
// Neutral.
|
||||
const Projection NEUTRAL =
|
||||
{
|
||||
ZERO,
|
||||
ONE,
|
||||
ZERO,
|
||||
ONE
|
||||
};
|
||||
|
||||
<*
|
||||
Convert affine to projective coordinates.
|
||||
|
||||
@param [&in] p
|
||||
*>
|
||||
fn Projection project(Point* p) => { p.x, p.y, p.x * p.y, ONE };
|
||||
|
||||
<*
|
||||
Convert projective to affine coordinates.
|
||||
|
||||
@param [&in] p
|
||||
*>
|
||||
fn Point unproject(Projection* p)
|
||||
{
|
||||
Point r @noinit;
|
||||
|
||||
F25519Int inv = p.z.inv();
|
||||
r.x = p.x * inv;
|
||||
r.y = p.y * inv;
|
||||
|
||||
r.x.normalize();
|
||||
r.y.normalize();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
// d parameter for edwards25519 : -121665/121666
|
||||
const F25519Int D = x"a3785913ca4deb75 abd841414d0a7000 98e879777940c78c 73fe6f2bee6c0352";
|
||||
|
||||
// 2*d
|
||||
const F25519Int DD = x"59f1b226949bd6eb 56b183829a14e000 30d1f3eef2808e19 e7fcdf56dcd90624";
|
||||
|
||||
<*
|
||||
Compress a point.
|
||||
|
||||
@param [&in] p
|
||||
*>
|
||||
fn F25519Int pack(Point* p)
|
||||
{
|
||||
Point r = *p;
|
||||
|
||||
r.x.normalize();
|
||||
r.y.normalize();
|
||||
|
||||
r.y[^1] |= (r.x[0] & 1) << 7;
|
||||
|
||||
return r.y;
|
||||
}
|
||||
|
||||
struct Unpacking
|
||||
{
|
||||
Point point;
|
||||
char on_curve; // Non-zero if true.
|
||||
}
|
||||
|
||||
<*
|
||||
Uncompress a point. Check if it is on the curve.
|
||||
|
||||
@param [&in] encoding
|
||||
*>
|
||||
fn Unpacking unpack_on_curve(F25519Int* encoding)
|
||||
{
|
||||
Point p @noinit;
|
||||
|
||||
char parity = (*encoding)[^1] >> 7;
|
||||
|
||||
p.y = *encoding;
|
||||
p.y[^1] &= 0b01111111;
|
||||
|
||||
F25519Int y2 = p.y * p.y;
|
||||
F25519Int x2 = (D * y2 + ONE).inv() * (y2 - ONE);
|
||||
|
||||
F25519Int x = x2.sqrt();
|
||||
|
||||
p.x = f25519_select(&x, &&-x, (x[0] ^ parity) & 1);
|
||||
|
||||
F25519Int _x2 = p.x * p.x;
|
||||
|
||||
x2.normalize();
|
||||
_x2.normalize();
|
||||
|
||||
return {p, eq(&x2, &_x2)};
|
||||
}
|
||||
|
||||
macro Projection Projection.@add(&s, Projection #p) @operator(+) => s.add(@addr(#p));
|
||||
<*
|
||||
Addition.
|
||||
|
||||
@param [&in] s
|
||||
*>
|
||||
fn Projection Projection.add(&s, Projection* p) @operator(+)
|
||||
{
|
||||
Projection r @noinit;
|
||||
|
||||
F25519Int a = (s.y - s.x) * (p.y - p.x);
|
||||
F25519Int b = (s.y + s.x) * (p.y + p.x);
|
||||
F25519Int c = s.t * DD * p.t;
|
||||
F25519Int d = (s.z * p.z).mul_s(2);
|
||||
F25519Int e = b - a;
|
||||
F25519Int f = d - c;
|
||||
F25519Int g = d + c;
|
||||
F25519Int h = b + a;
|
||||
|
||||
r.x = e * f;
|
||||
r.y = g * h;
|
||||
r.t = e * h;
|
||||
r.z = f * g;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
<*
|
||||
Double a point.
|
||||
|
||||
@param [&in] s
|
||||
*>
|
||||
fn Projection Projection.twice(&s)
|
||||
{
|
||||
Projection r @noinit;
|
||||
|
||||
F25519Int a = s.x * s.x;
|
||||
F25519Int b = s.y * s.y;
|
||||
F25519Int c = (s.z * s.z).mul_s(2);
|
||||
F25519Int d = s.x + s.y;
|
||||
F25519Int e = d * d - a - b;
|
||||
F25519Int g = b - a;
|
||||
F25519Int f = g - c;
|
||||
F25519Int h = -b - a;
|
||||
|
||||
r.x = e * f;
|
||||
r.y = g * h;
|
||||
r.t = e * h;
|
||||
r.z = f * g;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
<*
|
||||
Variable base scalar multiplication.
|
||||
|
||||
@param [&in] s
|
||||
@param [in] n
|
||||
*>
|
||||
fn Projection Projection.mul(&s, char[] n) @operator(*)
|
||||
{
|
||||
Projection r = NEUTRAL;
|
||||
|
||||
for (isz i = n.len << 3 - 1; i >= 0; i--)
|
||||
{
|
||||
r = r.twice();
|
||||
|
||||
Projection t = r + s;
|
||||
|
||||
char bit = n[i >> 3] >> (i & 7) & 1;
|
||||
r.x = f25519_select(&r.x, &t.x, bit);
|
||||
r.y = f25519_select(&r.y, &t.y, bit);
|
||||
r.z = f25519_select(&r.z, &t.z, bit);
|
||||
r.t = f25519_select(&r.t, &t.t, bit);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Modular arithmetic over the prime field F_(2^255-19)
|
||||
*/
|
||||
|
||||
module std::crypto::ed25519 @private;
|
||||
|
||||
typedef F25519Int = inline char[32];
|
||||
|
||||
const F25519Int ZERO = {};
|
||||
const F25519Int ONE = {[0] = 1};
|
||||
|
||||
<*
|
||||
Reduce an element with carry to at most 2^255+18 (32 bytes)
|
||||
|
||||
@param [&inout] s
|
||||
*>
|
||||
fn void F25519Int.reduce_carry(&s, uint carry)
|
||||
{
|
||||
// Reduce using 2^255 = 19 mod p
|
||||
(*s)[^1] &= 0b01111111;
|
||||
|
||||
carry *= 19;
|
||||
|
||||
for (usz i; i < F25519Int.len; i++)
|
||||
{
|
||||
carry += (*s)[i];
|
||||
(*s)[i] = (char)carry;
|
||||
carry >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Reduce an element to at most 2^255-19
|
||||
|
||||
@param [&inout] s
|
||||
*>
|
||||
fn void F25519Int.normalize(&s)
|
||||
{
|
||||
s.reduce_carry((*s)[^1] >> 7);
|
||||
|
||||
// Substract p
|
||||
F25519Int sub @noinit;
|
||||
ushort c = 19;
|
||||
for (usz i; i + 1 < F25519Int.len; i++)
|
||||
{
|
||||
c += (*s)[i];
|
||||
sub[i] = (char)c;
|
||||
c >>= 8;
|
||||
}
|
||||
c += (*s)[^1] - 0b10000000;
|
||||
sub[^1] = (char)c;
|
||||
|
||||
*s = f25519_select(&sub, s, (char)(c >> 15));
|
||||
}
|
||||
|
||||
<*
|
||||
Constant-time equality comparison. Return is non-zero if true.
|
||||
|
||||
@param [&in] a
|
||||
@param [&in] b
|
||||
*>
|
||||
fn char eq(F25519Int* a, F25519Int* b)
|
||||
{
|
||||
char e;
|
||||
for (usz i; i < F25519Int.len; i++) e |= (*a)[i] ^ (*b)[i];
|
||||
|
||||
e |= (e >> 4);
|
||||
e |= (e >> 2);
|
||||
e |= (e >> 1);
|
||||
|
||||
return e ^ 1;
|
||||
}
|
||||
|
||||
<*
|
||||
Constant-time conditonal selection. Result is undefined if condition is neither 0 nor 1.
|
||||
|
||||
@param [&in] zero : "selected if condition is 0"
|
||||
@param [&in] one : "selected if condition is 1"
|
||||
*>
|
||||
fn F25519Int f25519_select(F25519Int* zero, F25519Int* one, char condition)
|
||||
{
|
||||
F25519Int r @noinit;
|
||||
|
||||
for (usz i; i < F25519Int.len; i++) r[i] = (*zero)[i] ^ (-condition & ((*one)[i] ^ (*zero)[i]));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
macro F25519Int F25519Int.@add(&s, F25519Int #n) @operator(+) => s.add(@addr(#n));
|
||||
|
||||
<*
|
||||
Addition.
|
||||
|
||||
@param [&in] s
|
||||
@param [&in] n
|
||||
*>
|
||||
fn F25519Int F25519Int.add(&s, F25519Int* n) @operator(+)
|
||||
{
|
||||
F25519Int r @noinit;
|
||||
|
||||
ushort c;
|
||||
foreach (i, v : *s)
|
||||
{
|
||||
c >>= 8;
|
||||
c += v + (*n)[i];
|
||||
r[i] = (char)c;
|
||||
}
|
||||
|
||||
r.reduce_carry(c >> 7);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
macro F25519Int F25519Int.@sub(&s, F25519Int #n) @operator(-) => s.sub(@addr(#n));
|
||||
|
||||
<*
|
||||
Substraction.
|
||||
|
||||
@param [&in] s
|
||||
@param [&in] n
|
||||
*>
|
||||
fn F25519Int F25519Int.sub(&s, F25519Int* n) @operator(-)
|
||||
{
|
||||
// Compute s+2*p-n instead of s-n to avoid underflow.
|
||||
F25519Int r @noinit;
|
||||
|
||||
uint c = (char)~(2 * 19 - 1);
|
||||
for (usz i; i + 1 < F25519Int.len; i++)
|
||||
{
|
||||
c += 0b11111111_00000000 + (*s)[i] - (*n)[i];
|
||||
r[i] = (char)c;
|
||||
c >>= 8;
|
||||
}
|
||||
c += (*s)[^1] - (*n)[^1];
|
||||
r[^1] = (char)c;
|
||||
|
||||
r.reduce_carry(c >> 7);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
<*
|
||||
Negation.
|
||||
|
||||
@param [&in] s
|
||||
*>
|
||||
fn F25519Int F25519Int.neg(&s) @operator(-)
|
||||
{
|
||||
// Compute 2*p-s instead of -s to avoid underflow.
|
||||
F25519Int r @noinit;
|
||||
|
||||
uint c = (char)~(2 * 19 - 1);
|
||||
foreach (i, v : ((char*)s)[:F25519Int.len - 1])
|
||||
{
|
||||
c += 0b11111111_00000000 - v;
|
||||
r[i] = (char)c;
|
||||
c >>= 8;
|
||||
}
|
||||
c -= (*s)[^1];
|
||||
r[^1] = (char)c;
|
||||
|
||||
r.reduce_carry(c >> 7);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
macro F25519Int F25519Int.@mul(&s, F25519Int #n) @operator(*) => s.mul(@addr(#n));
|
||||
|
||||
<*
|
||||
Multiplication.
|
||||
|
||||
@param [&in] s
|
||||
@param [&in] n
|
||||
*>
|
||||
fn F25519Int F25519Int.mul(&s, F25519Int* n) @operator(*)
|
||||
{
|
||||
F25519Int r @noinit;
|
||||
|
||||
uint c;
|
||||
for (usz i = 0; i < F25519Int.len; i++)
|
||||
{
|
||||
c >>= 8;
|
||||
for (usz j; j <= i; j++) c += (*s)[j] * (*n)[i - j];
|
||||
// Reduce using 2^256 = 2*19 mod p
|
||||
for (usz j = i + 1; j < F25519Int.len; j++) c += (*s)[j] * (*n)[^j - i] * 2 * 19;
|
||||
r[i] = (char)c;
|
||||
}
|
||||
|
||||
r.reduce_carry(c >> 7);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
<*
|
||||
Multiplication by a small element.
|
||||
|
||||
@param [&in] s
|
||||
*>
|
||||
fn F25519Int F25519Int.mul_s(&s, uint n)
|
||||
{
|
||||
F25519Int r @noinit;
|
||||
|
||||
uint c;
|
||||
foreach (i, v : *s)
|
||||
{
|
||||
c >>= 8;
|
||||
c += v * n;
|
||||
r[i] = (char)c;
|
||||
}
|
||||
|
||||
r.reduce_carry(c >> 7);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
<*
|
||||
Inverse an element.
|
||||
|
||||
@param [&in] s
|
||||
*>
|
||||
fn F25519Int F25519Int.inv(&s)
|
||||
{
|
||||
//Compute s^(p-2)
|
||||
F25519Int r = *s;
|
||||
|
||||
for (usz i; i < 255 - 1 - 5; i++) r = r * r * *s;
|
||||
|
||||
r *= r;
|
||||
r = r * r * s;
|
||||
r *= r;
|
||||
r = r * r * s;
|
||||
r = r * r * s;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
<*
|
||||
Raise an element to the power of 2^252-3
|
||||
|
||||
@param [&in] s
|
||||
*>
|
||||
fn F25519Int F25519Int.pow_2523(&s) @local
|
||||
{
|
||||
F25519Int r = *s;
|
||||
|
||||
for (usz i; i < 252 - 1 - 2; i++) r = r * r * *s;
|
||||
|
||||
r = r * r;
|
||||
r = r * r * s;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
<*
|
||||
Compute the square root of an element.
|
||||
|
||||
@param [&in] s
|
||||
*>
|
||||
fn F25519Int F25519Int.sqrt(&s)
|
||||
{
|
||||
F25519Int twice = s.mul_s(2);
|
||||
F25519Int pow = twice.pow_2523();
|
||||
|
||||
return ((twice * pow * pow) - ONE) * s * pow;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Modular arithmetic over the prime field F_(2^252+0x14def9dea2f79cd65812631a5cf5d3ed)
|
||||
*/
|
||||
|
||||
module std::crypto::ed25519 @private;
|
||||
import std::math;
|
||||
|
||||
typedef FBaseInt = inline char[32];
|
||||
|
||||
// Order of the field : 2^252+0x14def9dea2f79cd65812631a5cf5d3ed
|
||||
const FBaseInt ORDER = x"edd3f55c1a631258 d69cf7a2def9de14 0000000000000000 0000000000000010";
|
||||
|
||||
<*
|
||||
FBaseInterpret bytes as a normalized element.
|
||||
|
||||
@param [in] bytes
|
||||
*>
|
||||
fn FBaseInt from_bytes(char[] bytes)
|
||||
{
|
||||
FBaseInt r;
|
||||
|
||||
usz bitc = min(252 - 1, bytes.len << 3);
|
||||
usz bytec = bitc >> 3;
|
||||
usz mod = bitc & 7;
|
||||
usz rem = bytes.len << 3 - bitc;
|
||||
|
||||
r[:bytec] = bytes[^bytec..];
|
||||
|
||||
if (mod)
|
||||
{
|
||||
r <<= mod;
|
||||
r[0] |= bytes[^bytec + 1] >> (8 - mod);
|
||||
}
|
||||
|
||||
for (isz i = rem - 1; i >= 0; i--)
|
||||
{
|
||||
r <<= 1;
|
||||
r[0] |= bytes[i >> 3] >> (i & 7) & 1;
|
||||
r = r.sub_l(&ORDER);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
<*
|
||||
Constant-time conditonal selection. Result is undefined if condition is neither 0 nor 1.
|
||||
|
||||
@param [&in] zero : "selected if condition is 0"
|
||||
@param [&in] one : "selected if condition is 1"
|
||||
*>
|
||||
fn FBaseInt fbase_select(FBaseInt* zero, FBaseInt* one, char condition)
|
||||
{
|
||||
FBaseInt r @noinit;
|
||||
|
||||
for (usz i; i < FBaseInt.len; i++) r[i] = (*zero)[i] ^ (-condition & ((*one)[i] ^ (*zero)[i]));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
macro FBaseInt FBaseInt.@add(&s, FBaseInt #n) @operator(+) => s.add(@addr(#n));
|
||||
|
||||
<*
|
||||
Addition.
|
||||
|
||||
@param [&in] s
|
||||
@param [&in] n
|
||||
*>
|
||||
fn FBaseInt FBaseInt.add(&s, FBaseInt* n) @operator(+)
|
||||
{
|
||||
FBaseInt r @noinit;
|
||||
|
||||
ushort c;
|
||||
for (usz i; i < FBaseInt.len; i++)
|
||||
{
|
||||
c += (*s)[i] + (*n)[i];
|
||||
r[i] = (char)c;
|
||||
c >>= 8;
|
||||
}
|
||||
|
||||
return r.sub_l(&ORDER);
|
||||
}
|
||||
|
||||
<*
|
||||
Substraction if RHS is less than LHS else identity.
|
||||
|
||||
@param [&in] s
|
||||
@param [&in] n
|
||||
*>
|
||||
fn FBaseInt FBaseInt.sub_l(&s, FBaseInt* n)
|
||||
{
|
||||
FBaseInt sub @noinit;
|
||||
ushort c;
|
||||
for (usz i; i < FBaseInt.len; i++)
|
||||
{
|
||||
c = (*s)[i] - (*n)[i] - c;
|
||||
sub[i] = (char)c;
|
||||
c = (c >> 8) & 1;
|
||||
}
|
||||
|
||||
return fbase_select(&sub, s, (char)c);
|
||||
}
|
||||
|
||||
<*
|
||||
Left shift.
|
||||
|
||||
@param [&in] s
|
||||
*>
|
||||
fn FBaseInt FBaseInt.shl(&s, usz n) @operator(<<)
|
||||
{
|
||||
FBaseInt r @noinit;
|
||||
|
||||
ushort c;
|
||||
for (usz i; i < FBaseInt.len; i++)
|
||||
{
|
||||
c |= (*s)[i] << n;
|
||||
r[i] = (char)c;
|
||||
c >>= 8;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
macro FBaseInt FBaseInt.@mul(&s, FBaseInt #n) @operator(*) => s.mul(@addr(#n));
|
||||
<*
|
||||
Multiplication.
|
||||
|
||||
@param [&in] s
|
||||
@param [&in] n
|
||||
*>
|
||||
fn FBaseInt FBaseInt.mul(&s, FBaseInt* n) @operator(*)
|
||||
{
|
||||
FBaseInt r;
|
||||
|
||||
for (isz i = 252; i >= 0; i--)
|
||||
{
|
||||
r = (r << 1).sub_l(&ORDER);
|
||||
r = fbase_select(&r, &&(r + s), (*n)[i >> 3] >> (i & 7) & 1);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -63,14 +63,17 @@
|
||||
- `@links` on macros would not be added to calling functions.
|
||||
- Fix `Formatter.print` returning incorrect size.
|
||||
- A distinct type based on an array would yield .len == 0
|
||||
- Overloading addition with a pointer would not work.
|
||||
|
||||
### Stdlib changes
|
||||
- Improve contract for readline. #2280
|
||||
- Added Whirlpool hash.
|
||||
- Added Ed25519.
|
||||
- Added string::bformat.
|
||||
- Virtual memory library.
|
||||
- New virtual emory arena allocator.
|
||||
- Added `WString.len`.
|
||||
- Added `@addr` macro.
|
||||
|
||||
## 0.7.3 Change list
|
||||
|
||||
|
||||
@@ -964,6 +964,7 @@ typedef enum
|
||||
OVERLOADS_COUNT = OVERLOAD_SHR_ASSIGN
|
||||
} OperatorOverload;
|
||||
|
||||
#define OVERLOAD_NONE ((OperatorOverload)0)
|
||||
typedef enum
|
||||
{
|
||||
OS_TYPE_UNKNOWN,
|
||||
|
||||
@@ -208,8 +208,10 @@ static inline bool sema_analyse_maybe_dead_expr(SemaContext *, Expr *expr, bool
|
||||
static inline bool sema_insert_binary_overload(SemaContext *context, Expr *expr, Decl *overload, Expr *lhs, Expr *rhs, bool reverse);
|
||||
static bool sema_replace_with_overload(SemaContext *context, Expr *expr, Expr *left, Expr *right, Type *left_type, OperatorOverload* operator_overload_ref);
|
||||
|
||||
// -- implementations
|
||||
INLINE bool sema_expr_analyse_ptr_add(SemaContext *context, Expr *expr, Expr *left, Expr *right, CanonicalType *left_type, CanonicalType *right_type, Type *cast_to_iptr, bool *failed_ref);
|
||||
static Type *defer_iptr_cast(Expr *maybe_pointer);
|
||||
|
||||
// -- implementations
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -7188,12 +7190,31 @@ static bool sema_binary_arithmetic_promotion(SemaContext *context, Expr *left, E
|
||||
OperatorOverload *operator_overload_ref,
|
||||
bool *failed_ref)
|
||||
{
|
||||
OperatorOverload overload = *operator_overload_ref;
|
||||
if (type_is_user_defined(left_type) || type_is_user_defined(right_type))
|
||||
{
|
||||
if (!sema_replace_with_overload(context, parent, left, right, left_type, operator_overload_ref)) return false;
|
||||
if (!*operator_overload_ref) return true;
|
||||
}
|
||||
|
||||
if (overload == OVERLOAD_PLUS)
|
||||
{
|
||||
Type *cast_to_iptr = defer_iptr_cast(left);
|
||||
if (!cast_to_iptr) cast_to_iptr = defer_iptr_cast(right);
|
||||
left_type = type_no_optional(left->type)->canonical;
|
||||
right_type = type_no_optional(right->type)->canonical;
|
||||
if (type_is_pointer_like(left_type))
|
||||
{
|
||||
*operator_overload_ref = OVERLOAD_NONE;
|
||||
return sema_expr_analyse_ptr_add(context, parent, left, right, left_type, right_type, cast_to_iptr, failed_ref);
|
||||
}
|
||||
if (type_is_pointer_like(right_type))
|
||||
{
|
||||
*operator_overload_ref = OVERLOAD_NONE;
|
||||
return sema_expr_analyse_ptr_add(context, parent, right, left, right_type, left_type, cast_to_iptr, failed_ref);
|
||||
}
|
||||
}
|
||||
|
||||
Type *max = cast_numeric_arithmetic_promotion(type_find_max_type(left_type, right_type));
|
||||
if (!max || (!type_underlying_is_numeric(max) && !(allow_bool_vec && type_flat_is_bool_vector(max))))
|
||||
{
|
||||
@@ -7525,46 +7546,15 @@ static bool sema_expr_analyse_add(SemaContext *context, Expr *expr, Expr *left,
|
||||
// this is safe in the pointer case actually.
|
||||
if (!sema_binary_analyse_subexpr(context, left, right)) return false;
|
||||
|
||||
Type *cast_to_iptr = defer_iptr_cast(left);
|
||||
if (!cast_to_iptr) cast_to_iptr = defer_iptr_cast(right);
|
||||
|
||||
Type *left_type = type_no_optional(left->type)->canonical;
|
||||
Type *right_type = type_no_optional(right->type)->canonical;
|
||||
|
||||
bool right_is_pointer = type_is_pointer_like(right_type);
|
||||
bool left_is_pointer = type_is_pointer_like(left_type);
|
||||
// 2. To detect pointer additions, reorder if needed
|
||||
if (right_is_pointer && !left_is_pointer)
|
||||
{
|
||||
Expr *temp = right;
|
||||
right = left;
|
||||
left = temp;
|
||||
right_type = left_type;
|
||||
left_type = left->type->canonical;
|
||||
left_is_pointer = true;
|
||||
right_is_pointer = false;
|
||||
(void)right_is_pointer;
|
||||
expr->binary_expr.left = exprid(left);
|
||||
expr->binary_expr.right = exprid(right);
|
||||
}
|
||||
|
||||
// 3. The "left" will now always be the pointer.
|
||||
// so check if we want to do the normal pointer add special handling.
|
||||
if (left_is_pointer)
|
||||
{
|
||||
return sema_expr_analyse_ptr_add(context, expr, left, right, left_type, right_type, cast_to_iptr, failed_ref);
|
||||
}
|
||||
|
||||
if (left_type->type_kind == TYPE_ENUM || right_type->type_kind == TYPE_ENUM)
|
||||
{
|
||||
return sema_expr_analyse_enum_add_sub(context, expr, left, right, failed_ref);
|
||||
}
|
||||
|
||||
left_type = type_no_optional(left->type)->canonical;
|
||||
right_type = type_no_optional(right->type)->canonical;
|
||||
|
||||
ASSERT_SPAN(expr, !cast_to_iptr);
|
||||
|
||||
// 4. Do a binary arithmetic promotion
|
||||
OperatorOverload overload = OVERLOAD_PLUS;
|
||||
if (!sema_binary_arithmetic_promotion(context, left, right, left_type, right_type, expr,
|
||||
|
||||
15
test/unit/stdlib/crypto/ed25519.c3
Normal file
15
test/unit/stdlib/crypto/ed25519.c3
Normal file
@@ -0,0 +1,15 @@
|
||||
module std::crypto::ed25519_test @test;
|
||||
import std::crypto::ed25519;
|
||||
|
||||
fn void ed25519_keygen_sign_verify() @test {
|
||||
PrivateKey private_key = x"97b0d0435be3a583df0b77aff277e5fc8f857cae1a827723241bb9bbf7e96018";
|
||||
|
||||
PublicKey public_key = ed25519::public_keygen(&private_key);
|
||||
assert(public_key == x"fd462c5c7044b224f4a6cc0f0e5e16511be881be639b3fbc600f7b5d6da54e2f");
|
||||
|
||||
String message = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
Signature signature = ed25519::sign(message, &private_key, &public_key);
|
||||
assert(signature == x"c03cf7b181583f6353a9434be910a484e1720247a0dc384d601e495294d0202728dda5f31bc62b00158b1588bc2a65f48ccfaa1b1431aff3d8da9271b7de9403");
|
||||
assert(ed25519::verify(message, &signature, &public_key));
|
||||
}
|
||||
Reference in New Issue
Block a user