Skip to content

Instantly share code, notes, and snippets.

@Rexicon226
Created October 7, 2025 21:59
Show Gist options
  • Save Rexicon226/6d87daeaa840d9bea014691635e93aa4 to your computer and use it in GitHub Desktop.
Save Rexicon226/6d87daeaa840d9bea014691635e93aa4 to your computer and use it in GitHub Desktop.
const std = @import("std");
const Edwards25519 = std.crypto.ecc.Edwards25519;
export fn slow(s: *const [32]u8) bool {
Edwards25519.scalar.rejectNonCanonical(s.*) catch return false;
return true;
}
export fn fast(s: *const [32]u8) bool {
// If none of the top 4 bits are set, then the scalar fits into S \in [0, 2^252),
// which is a tighter range than [0, L), about [0, 2^252.5). In this case
// we can "succeed-fast" and skip the full canonical check
if (s[31] & 0b11110000 != 0) {
// Assuming canonical and IID scalars, the chance of the 252nd bit being set is roughly 1/2^128.
@branchHint(.unlikely);
// If any of the top 3 bits are set, the scalar representation must be invalid.
if (s[31] & 0b11100000 != 0) return true;
// The only thing left to do here is the full rejection.
Edwards25519.scalar.rejectNonCanonical(s.*) catch return false;
}
return true;
}
pub fn main() !void {
const cpu0001: std.os.linux.cpu_set_t = [1]usize{0b0001} ++ ([_]usize{0} ** (16 - 1));
try std.os.linux.sched_setaffinity(0, &cpu0001);
const N = 1_000_000;
var scalars: [N][32]u8 = undefined;
for (&scalars) |*scalar| scalar.* = Edwards25519.scalar.random();
{
var cycles: u64 = 0;
for (&scalars) |*scalar| {
const start = rdtsc();
std.mem.doNotOptimizeAway(slow(scalar));
const end = rdtsc();
cycles += (end - start);
}
const cycles_per_reject = cycles / N;
std.debug.print("slow: {}\n", .{cycles_per_reject});
}
{
var cycles: u64 = 0;
for (&scalars) |*scalar| {
const start = rdtsc();
std.mem.doNotOptimizeAway(fast(scalar));
const end = rdtsc();
cycles += (end - start);
}
const cycles_per_reject = cycles / N;
std.debug.print("fast: {}\n", .{cycles_per_reject});
}
}
inline fn rdtsc() usize {
var a: u32 = undefined;
var b: u32 = undefined;
asm volatile ("rdtscp"
: [a] "={edx}" (a),
[b] "={eax}" (b),
:
: .{ .ecx = true });
return (@as(u64, a) << 32) | b;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment