mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
To quote the language reference,
It is generally better to let the compiler decide when to inline a
function, except for these scenarios:
* To change how many stack frames are in the call stack, for debugging
purposes.
* To force comptime-ness of the arguments to propagate to the return
value of the function, as in the above example.
* Real world performance measurements demand it. Don't guess!
Note that inline actually restricts what the compiler is allowed to do.
This can harm binary size, compilation speed, and even runtime
performance.
`zig run lib/std/crypto/benchmark.zig -OReleaseFast`
[-before-] vs {+after+}
md5: [-990-] {+998+} MiB/s
sha1: [-1144-] {+1140+} MiB/s
sha256: [-2267-] {+2275+} MiB/s
sha512: [-762-] {+767+} MiB/s
sha3-256: [-680-] {+683+} MiB/s
sha3-512: [-362-] {+363+} MiB/s
shake-128: [-835-] {+839+} MiB/s
shake-256: [-680-] {+681+} MiB/s
turboshake-128: [-1567-] {+1570+} MiB/s
turboshake-256: [-1276-] {+1282+} MiB/s
blake2s: [-778-] {+789+} MiB/s
blake2b: [-1071-] {+1086+} MiB/s
blake3: [-1148-] {+1137+} MiB/s
ghash: [-10044-] {+10033+} MiB/s
polyval: [-9726-] {+10033+} MiB/s
poly1305: [-2486-] {+2703+} MiB/s
hmac-md5: [-991-] {+998+} MiB/s
hmac-sha1: [-1134-] {+1137+} MiB/s
hmac-sha256: [-2265-] {+2288+} MiB/s
hmac-sha512: [-765-] {+764+} MiB/s
siphash-2-4: [-4410-] {+4438+} MiB/s
siphash-1-3: [-7144-] {+7225+} MiB/s
siphash128-2-4: [-4397-] {+4449+} MiB/s
siphash128-1-3: [-7281-] {+7374+} MiB/s
aegis-128x4 mac: [-73385-] {+74523+} MiB/s
aegis-256x4 mac: [-30160-] {+30539+} MiB/s
aegis-128x2 mac: [-66662-] {+67267+} MiB/s
aegis-256x2 mac: [-16812-] {+16806+} MiB/s
aegis-128l mac: [-33876-] {+34055+} MiB/s
aegis-256 mac: [-8993-] {+9087+} MiB/s
aes-cmac: 2036 MiB/s
x25519: [-20670-] {+16844+} exchanges/s
ed25519: [-29763-] {+29576+} signatures/s
ecdsa-p256: [-4762-] {+4900+} signatures/s
ecdsa-p384: [-1465-] {+1500+} signatures/s
ecdsa-secp256k1: [-5643-] {+5769+} signatures/s
ed25519: [-21926-] {+21721+} verifications/s
ed25519: [-51200-] {+50880+} verifications/s (batch)
chacha20Poly1305: [-1189-] {+1109+} MiB/s
xchacha20Poly1305: [-1196-] {+1107+} MiB/s
xchacha8Poly1305: [-1466-] {+1555+} MiB/s
xsalsa20Poly1305: [-660-] {+620+} MiB/s
aegis-128x4: [-76389-] {+78181+} MiB/s
aegis-128x2: [-53946-] {+53495+} MiB/s
aegis-128l: [-27219-] {+25621+} MiB/s
aegis-256x4: [-49351-] {+49542+} MiB/s
aegis-256x2: [-32390-] {+32366+} MiB/s
aegis-256: [-8881-] {+8944+} MiB/s
aes128-gcm: [-6095-] {+6205+} MiB/s
aes256-gcm: [-5306-] {+5427+} MiB/s
aes128-ocb: [-8529-] {+13974+} MiB/s
aes256-ocb: [-7241-] {+9442+} MiB/s
isapa128a: [-204-] {+214+} MiB/s
aes128-single: [-133857882-] {+134170944+} ops/s
aes256-single: [-96306962-] {+96408639+} ops/s
aes128-8: [-1083210101-] {+1073727253+} ops/s
aes256-8: [-762042466-] {+767091778+} ops/s
bcrypt: 0.009 s/ops
scrypt: [-0.018-] {+0.017+} s/ops
argon2: [-0.037-] {+0.060+} s/ops
kyber512d00: [-206057-] {+205779+} encaps/s
kyber768d00: [-156074-] {+150711+} encaps/s
kyber1024d00: [-116626-] {+115469+} encaps/s
kyber512d00: [-181149-] {+182046+} decaps/s
kyber768d00: [-136965-] {+135676+} decaps/s
kyber1024d00: [-101307-] {+100643+} decaps/s
kyber512d00: [-123624-] {+123375+} keygen/s
kyber768d00: [-69465-] {+70828+} keygen/s
kyber1024d00: [-43117-] {+43208+} keygen/s
195 lines
7.8 KiB
Zig
195 lines
7.8 KiB
Zig
const std = @import("std");
|
|
const fmt = std.fmt;
|
|
|
|
const EncodingError = std.crypto.errors.EncodingError;
|
|
const IdentityElementError = std.crypto.errors.IdentityElementError;
|
|
const NonCanonicalError = std.crypto.errors.NonCanonicalError;
|
|
const WeakPublicKeyError = std.crypto.errors.WeakPublicKeyError;
|
|
|
|
/// Group operations over Edwards25519.
|
|
pub const Ristretto255 = struct {
|
|
/// The underlying elliptic curve.
|
|
pub const Curve = @import("edwards25519.zig").Edwards25519;
|
|
/// The underlying prime field.
|
|
pub const Fe = Curve.Fe;
|
|
/// Field arithmetic mod the order of the main subgroup.
|
|
pub const scalar = Curve.scalar;
|
|
/// Length in byte of an encoded element.
|
|
pub const encoded_length: usize = 32;
|
|
|
|
p: Curve,
|
|
|
|
fn sqrtRatioM1(u: Fe, v: Fe) struct { ratio_is_square: u32, root: Fe } {
|
|
const v3 = v.sq().mul(v); // v^3
|
|
var x = v3.sq().mul(u).mul(v).pow2523().mul(v3).mul(u); // uv^3(uv^7)^((q-5)/8)
|
|
const vxx = x.sq().mul(v); // vx^2
|
|
const m_root_check = vxx.sub(u); // vx^2-u
|
|
const p_root_check = vxx.add(u); // vx^2+u
|
|
const f_root_check = u.mul(Fe.sqrtm1).add(vxx); // vx^2+u*sqrt(-1)
|
|
const has_m_root = m_root_check.isZero();
|
|
const has_p_root = p_root_check.isZero();
|
|
const has_f_root = f_root_check.isZero();
|
|
const x_sqrtm1 = x.mul(Fe.sqrtm1); // x*sqrt(-1)
|
|
x.cMov(x_sqrtm1, @intFromBool(has_p_root) | @intFromBool(has_f_root));
|
|
return .{ .ratio_is_square = @intFromBool(has_m_root) | @intFromBool(has_p_root), .root = x.abs() };
|
|
}
|
|
|
|
fn rejectNonCanonical(s: [encoded_length]u8) NonCanonicalError!void {
|
|
if ((s[0] & 1) != 0) {
|
|
return error.NonCanonical;
|
|
}
|
|
try Fe.rejectNonCanonical(s, false);
|
|
}
|
|
|
|
/// Reject the neutral element.
|
|
pub fn rejectIdentity(p: Ristretto255) IdentityElementError!void {
|
|
return p.p.rejectIdentity();
|
|
}
|
|
|
|
/// The base point (Ristretto is a curve in desguise).
|
|
pub const basePoint = Ristretto255{ .p = Curve.basePoint };
|
|
|
|
/// Decode a Ristretto255 representative.
|
|
pub fn fromBytes(s: [encoded_length]u8) (NonCanonicalError || EncodingError)!Ristretto255 {
|
|
try rejectNonCanonical(s);
|
|
const s_ = Fe.fromBytes(s);
|
|
const ss = s_.sq(); // s^2
|
|
const u1_ = Fe.one.sub(ss); // (1-s^2)
|
|
const u1u1 = u1_.sq(); // (1-s^2)^2
|
|
const u2_ = Fe.one.add(ss); // (1+s^2)
|
|
const u2u2 = u2_.sq(); // (1+s^2)^2
|
|
const v = Fe.edwards25519d.mul(u1u1).neg().sub(u2u2); // -(d*u1^2)-u2^2
|
|
const v_u2u2 = v.mul(u2u2); // v*u2^2
|
|
|
|
const inv_sqrt = sqrtRatioM1(Fe.one, v_u2u2);
|
|
var x = inv_sqrt.root.mul(u2_);
|
|
const y = inv_sqrt.root.mul(x).mul(v).mul(u1_);
|
|
x = x.mul(s_);
|
|
x = x.add(x).abs();
|
|
const t = x.mul(y);
|
|
if ((1 - inv_sqrt.ratio_is_square) | @intFromBool(t.isNegative()) | @intFromBool(y.isZero()) != 0) {
|
|
return error.InvalidEncoding;
|
|
}
|
|
const p: Curve = .{
|
|
.x = x,
|
|
.y = y,
|
|
.z = Fe.one,
|
|
.t = t,
|
|
};
|
|
return Ristretto255{ .p = p };
|
|
}
|
|
|
|
/// Encode to a Ristretto255 representative.
|
|
pub fn toBytes(e: Ristretto255) [encoded_length]u8 {
|
|
const p = &e.p;
|
|
var u1_ = p.z.add(p.y); // Z+Y
|
|
const zmy = p.z.sub(p.y); // Z-Y
|
|
u1_ = u1_.mul(zmy); // (Z+Y)*(Z-Y)
|
|
const u2_ = p.x.mul(p.y); // X*Y
|
|
const u1_u2u2 = u2_.sq().mul(u1_); // u1*u2^2
|
|
const inv_sqrt = sqrtRatioM1(Fe.one, u1_u2u2);
|
|
const den1 = inv_sqrt.root.mul(u1_);
|
|
const den2 = inv_sqrt.root.mul(u2_);
|
|
const z_inv = den1.mul(den2).mul(p.t); // den1*den2*T
|
|
const ix = p.x.mul(Fe.sqrtm1); // X*sqrt(-1)
|
|
const iy = p.y.mul(Fe.sqrtm1); // Y*sqrt(-1)
|
|
const eden = den1.mul(Fe.edwards25519sqrtamd); // den1/sqrt(a-d)
|
|
const t_z_inv = p.t.mul(z_inv); // T*z_inv
|
|
|
|
const rotate = @intFromBool(t_z_inv.isNegative());
|
|
var x = p.x;
|
|
var y = p.y;
|
|
var den_inv = den2;
|
|
x.cMov(iy, rotate);
|
|
y.cMov(ix, rotate);
|
|
den_inv.cMov(eden, rotate);
|
|
|
|
const x_z_inv = x.mul(z_inv);
|
|
const yneg = y.neg();
|
|
y.cMov(yneg, @intFromBool(x_z_inv.isNegative()));
|
|
|
|
return p.z.sub(y).mul(den_inv).abs().toBytes();
|
|
}
|
|
|
|
fn elligator(t: Fe) Curve {
|
|
const r = t.sq().mul(Fe.sqrtm1); // sqrt(-1)*t^2
|
|
const u = r.add(Fe.one).mul(Fe.edwards25519eonemsqd); // (r+1)*(1-d^2)
|
|
var c = comptime Fe.one.neg(); // -1
|
|
const v = c.sub(r.mul(Fe.edwards25519d)).mul(r.add(Fe.edwards25519d)); // (c-r*d)*(r+d)
|
|
const ratio_sqrt = sqrtRatioM1(u, v);
|
|
const wasnt_square = 1 - ratio_sqrt.ratio_is_square;
|
|
var s = ratio_sqrt.root;
|
|
const s_prime = s.mul(t).abs().neg(); // -|s*t|
|
|
s.cMov(s_prime, wasnt_square);
|
|
c.cMov(r, wasnt_square);
|
|
|
|
const n = r.sub(Fe.one).mul(c).mul(Fe.edwards25519sqdmone).sub(v); // c*(r-1)*(d-1)^2-v
|
|
const w0 = s.add(s).mul(v); // 2s*v
|
|
const w1 = n.mul(Fe.edwards25519sqrtadm1); // n*sqrt(ad-1)
|
|
const ss = s.sq(); // s^2
|
|
const w2 = Fe.one.sub(ss); // 1-s^2
|
|
const w3 = Fe.one.add(ss); // 1+s^2
|
|
|
|
return .{ .x = w0.mul(w3), .y = w2.mul(w1), .z = w1.mul(w3), .t = w0.mul(w2) };
|
|
}
|
|
|
|
/// Map a 64-bit string into a Ristretto255 group element
|
|
pub fn fromUniform(h: [64]u8) Ristretto255 {
|
|
const p0 = elligator(Fe.fromBytes(h[0..32].*));
|
|
const p1 = elligator(Fe.fromBytes(h[32..64].*));
|
|
return Ristretto255{ .p = p0.add(p1) };
|
|
}
|
|
|
|
/// Double a Ristretto255 element.
|
|
pub fn dbl(p: Ristretto255) Ristretto255 {
|
|
return .{ .p = p.p.dbl() };
|
|
}
|
|
|
|
/// Add two Ristretto255 elements.
|
|
pub fn add(p: Ristretto255, q: Ristretto255) Ristretto255 {
|
|
return .{ .p = p.p.add(q.p) };
|
|
}
|
|
|
|
/// Subtract two Ristretto255 elements.
|
|
pub fn sub(p: Ristretto255, q: Ristretto255) Ristretto255 {
|
|
return .{ .p = p.p.sub(q.p) };
|
|
}
|
|
|
|
/// Multiply a Ristretto255 element with a scalar.
|
|
/// Return error.WeakPublicKey if the resulting element is
|
|
/// the identity element.
|
|
pub fn mul(p: Ristretto255, s: [encoded_length]u8) (IdentityElementError || WeakPublicKeyError)!Ristretto255 {
|
|
return .{ .p = try p.p.mul(s) };
|
|
}
|
|
|
|
/// Return true if two Ristretto255 elements are equivalent
|
|
pub fn equivalent(p: Ristretto255, q: Ristretto255) bool {
|
|
const p_ = &p.p;
|
|
const q_ = &q.p;
|
|
const a = p_.x.mul(q_.y).equivalent(p_.y.mul(q_.x));
|
|
const b = p_.y.mul(q_.y).equivalent(p_.x.mul(q_.x));
|
|
return (@intFromBool(a) | @intFromBool(b)) != 0;
|
|
}
|
|
};
|
|
|
|
test "ristretto255" {
|
|
const p = Ristretto255.basePoint;
|
|
var buf: [256]u8 = undefined;
|
|
try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{&p.toBytes()}), "E2F2AE0A6ABC4E71A884A961C500515F58E30B6AA582DD8DB6A65945E08D2D76");
|
|
|
|
var r: [Ristretto255.encoded_length]u8 = undefined;
|
|
_ = try fmt.hexToBytes(r[0..], "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919");
|
|
var q = try Ristretto255.fromBytes(r);
|
|
q = q.dbl().add(p);
|
|
try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{&q.toBytes()}), "E882B131016B52C1D3337080187CF768423EFCCBB517BB495AB812C4160FF44E");
|
|
|
|
const s = [_]u8{15} ++ [_]u8{0} ** 31;
|
|
const w = try p.mul(s);
|
|
try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{&w.toBytes()}), "E0C418F7C8D9C4CDD7395B93EA124F3AD99021BB681DFC3302A9D99A2E53E64E");
|
|
|
|
try std.testing.expect(p.dbl().dbl().dbl().dbl().equivalent(w.add(p)));
|
|
|
|
const h = [_]u8{69} ** 32 ++ [_]u8{42} ** 32;
|
|
const ph = Ristretto255.fromUniform(h);
|
|
try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{&ph.toBytes()}), "DCCA54E037A4311EFBEEF413ACD21D35276518970B7A61DC88F8587B493D5E19");
|
|
}
|