solana-ecvrf 0.0.1

ECVRF-EDWARDS25519-SHA512-TAI (RFC 9381) verification for Solana programs using curve25519 and sha512 syscalls
Documentation
//! Arithmetic on the 16-byte challenge and 32-byte response, mod the group
//! order `L = 2^252 + 27742317777372353535851937790883648493` (RFC 8032 §5.1),
//! as two little-endian `u128` halves. Checked against dalek in the tests.

const L_LO: u128 = 0x14def9dea2f79cd6_5812631a5cf5d3ed;
const L_HI: u128 = 1 << 124;

#[inline(always)]
fn halves(s: &[u8; 32]) -> (u128, u128) {
    let lo = u128::from_le_bytes(s[..16].try_into().unwrap());
    let hi = u128::from_le_bytes(s[16..].try_into().unwrap());
    (hi, lo)
}

/// `s < L`, RFC 9381 §5.4.4 step 8.
#[inline]
pub fn is_canonical(s: &[u8; 32]) -> bool {
    halves(s) < (L_HI, L_LO)
}

/// `-c mod L` as a canonical scalar, so `s·B - c·Y` is the single multiscalar
/// multiplication `s·B + (-c)·Y`. `-0 = 0`; otherwise `L - c`, which is
/// canonical because `0 < c < L`.
#[inline]
pub fn negate_challenge(c: &[u8; 16]) -> [u8; 32] {
    let c = u128::from_le_bytes(*c);
    if c == 0 {
        return [0; 32];
    }
    let (lo, borrow) = L_LO.overflowing_sub(c);
    let hi = L_HI - borrow as u128;
    let mut out = [0u8; 32];
    out[..16].copy_from_slice(&lo.to_le_bytes());
    out[16..].copy_from_slice(&hi.to_le_bytes());
    out
}