use crate::SodiumError;
use crate::Result;
pub const BYTES: usize = libsodium_sys::crypto_core_ed25519_BYTES as usize;
pub const SCALARBYTES: usize = libsodium_sys::crypto_core_ed25519_SCALARBYTES as usize;
pub const NONREDUCEDSCALARBYTES: usize = libsodium_sys::crypto_core_ed25519_NONREDUCEDSCALARBYTES as usize;
pub const UNIFORMBYTES: usize = libsodium_sys::crypto_core_ed25519_UNIFORMBYTES as usize;
#[must_use = "This function returns a constant value that should be used for buffer sizing"]
pub const fn bytes() -> usize {
BYTES
}
#[must_use = "This function returns a constant value that should be used for buffer sizing"]
pub const fn scalarbytes() -> usize {
SCALARBYTES
}
#[must_use = "This function returns a constant value that should be used for buffer sizing"]
pub const fn nonreducedscalarbytes() -> usize {
NONREDUCEDSCALARBYTES
}
#[must_use = "This function returns a constant value that should be used for buffer sizing"]
pub const fn uniformbytes() -> usize {
UNIFORMBYTES
}
#[must_use = "This function returns a validation result that should be checked"]
pub fn is_valid_point(p: &[u8]) -> Result<bool> {
if p.len() != BYTES {
return Err(SodiumError::InvalidInput(format!("invalid point length: expected {}, got {}", BYTES, p.len())));
}
let result = unsafe { libsodium_sys::crypto_core_ed25519_is_valid_point(p.as_ptr()) };
Ok(result == 1)
}
#[must_use = "This function returns a cryptographic point that should be used"]
pub fn add(p: &[u8], q: &[u8]) -> Vec<u8> {
if p.len() != BYTES || q.len() != BYTES {
return Vec::new();
}
let mut r = vec![0u8; BYTES];
let result = unsafe {
libsodium_sys::crypto_core_ed25519_add(r.as_mut_ptr(), p.as_ptr(), q.as_ptr())
};
if result != 0 {
return Vec::new();
}
r
}
#[must_use = "This function returns a cryptographic point that should be used"]
pub fn sub(p: &[u8], q: &[u8]) -> Vec<u8> {
if p.len() != BYTES || q.len() != BYTES {
return Vec::new();
}
let mut r = vec![0u8; BYTES];
let result = unsafe {
libsodium_sys::crypto_core_ed25519_sub(r.as_mut_ptr(), p.as_ptr(), q.as_ptr())
};
if result != 0 {
return Vec::new();
}
r
}
#[must_use = "This function returns a cryptographic point that should be used"]
pub fn from_uniform(r: &[u8]) -> Vec<u8> {
if r.len() != UNIFORMBYTES {
return Vec::new();
}
let mut p = vec![0u8; BYTES];
let result = unsafe {
libsodium_sys::crypto_core_ed25519_from_uniform(p.as_mut_ptr(), r.as_ptr())
};
if result != 0 {
return Vec::new();
}
p
}
#[must_use = "This function returns a random cryptographic point that should be used"]
pub fn random() -> Vec<u8> {
let mut p = vec![0u8; BYTES];
unsafe {
libsodium_sys::crypto_core_ed25519_random(p.as_mut_ptr());
}
p
}
#[must_use = "This function returns a random cryptographic scalar that should be used"]
pub fn scalar_random() -> Result<[u8; SCALARBYTES]> {
let mut r = [0u8; SCALARBYTES];
unsafe {
libsodium_sys::crypto_core_ed25519_scalar_random(r.as_mut_ptr());
}
Ok(r)
}
#[must_use = "This function returns an inverted cryptographic scalar that should be used"]
pub fn scalar_invert(s: &[u8]) -> Result<[u8; SCALARBYTES]> {
if s.len() != SCALARBYTES {
return Err(SodiumError::InvalidInput(format!(
"invalid scalar length: expected {}, got {}",
SCALARBYTES,
s.len()
)));
}
let mut recip = [0u8; SCALARBYTES];
let result = unsafe {
libsodium_sys::crypto_core_ed25519_scalar_invert(recip.as_mut_ptr(), s.as_ptr())
};
if result != 0 {
return Err(SodiumError::OperationError(
"scalar inversion failed (scalar may be zero)".into(),
));
}
Ok(recip)
}
#[must_use = "This function returns a negated cryptographic scalar that should be used"]
pub fn scalar_negate(s: &[u8]) -> Result<[u8; SCALARBYTES]> {
if s.len() != SCALARBYTES {
return Err(SodiumError::InvalidInput(format!(
"invalid scalar length: expected {}, got {}",
SCALARBYTES,
s.len()
)));
}
let mut neg = [0u8; SCALARBYTES];
unsafe {
libsodium_sys::crypto_core_ed25519_scalar_negate(neg.as_mut_ptr(), s.as_ptr());
}
Ok(neg)
}
#[must_use = "This function returns a complemented cryptographic scalar that should be used"]
pub fn scalar_complement(s: &[u8]) -> Result<[u8; SCALARBYTES]> {
if s.len() != SCALARBYTES {
return Err(SodiumError::InvalidInput(format!(
"invalid scalar length: expected {}, got {}",
SCALARBYTES,
s.len()
)));
}
let mut comp = [0u8; SCALARBYTES];
unsafe {
libsodium_sys::crypto_core_ed25519_scalar_complement(comp.as_mut_ptr(), s.as_ptr());
}
Ok(comp)
}
#[must_use = "This function returns a cryptographic scalar sum that should be used"]
pub fn scalar_add(x: &[u8], y: &[u8]) -> Result<[u8; SCALARBYTES]> {
if x.len() != SCALARBYTES || y.len() != SCALARBYTES {
return Err(SodiumError::InvalidInput(format!(
"invalid scalar length: expected {}, got {} and {}",
SCALARBYTES,
x.len(),
y.len()
)));
}
let mut z = [0u8; SCALARBYTES];
unsafe {
libsodium_sys::crypto_core_ed25519_scalar_add(z.as_mut_ptr(), x.as_ptr(), y.as_ptr());
}
Ok(z)
}
#[must_use = "This function returns a cryptographic scalar difference that should be used"]
pub fn scalar_sub(x: &[u8], y: &[u8]) -> Result<[u8; SCALARBYTES]> {
if x.len() != SCALARBYTES || y.len() != SCALARBYTES {
return Err(SodiumError::InvalidInput(format!(
"invalid scalar length: expected {}, got {} and {}",
SCALARBYTES,
x.len(),
y.len()
)));
}
let mut z = [0u8; SCALARBYTES];
unsafe {
libsodium_sys::crypto_core_ed25519_scalar_sub(z.as_mut_ptr(), x.as_ptr(), y.as_ptr());
}
Ok(z)
}
#[must_use = "This function returns a cryptographic scalar product that should be used"]
pub fn scalar_mul(x: &[u8], y: &[u8]) -> Result<[u8; SCALARBYTES]> {
if x.len() != SCALARBYTES || y.len() != SCALARBYTES {
return Err(SodiumError::InvalidInput(format!(
"invalid scalar length: expected {}, got {} and {}",
SCALARBYTES,
x.len(),
y.len()
)));
}
let mut z = [0u8; SCALARBYTES];
unsafe {
libsodium_sys::crypto_core_ed25519_scalar_mul(z.as_mut_ptr(), x.as_ptr(), y.as_ptr());
}
Ok(z)
}
#[must_use = "This function returns a reduced cryptographic scalar that should be used to prevent information leaks"]
pub fn scalar_reduce(s: &[u8]) -> Result<[u8; SCALARBYTES]> {
if s.len() != NONREDUCEDSCALARBYTES {
return Err(SodiumError::InvalidInput(format!(
"invalid non-reduced scalar length: expected {}, got {}",
NONREDUCEDSCALARBYTES,
s.len()
)));
}
let mut r = [0u8; SCALARBYTES];
unsafe {
libsodium_sys::crypto_core_ed25519_scalar_reduce(r.as_mut_ptr(), s.as_ptr());
}
Ok(r)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::random;
#[test]
fn test_constants() {
assert_eq!(BYTES, 32);
assert_eq!(SCALARBYTES, 32);
assert_eq!(NONREDUCEDSCALARBYTES, 64);
assert_eq!(UNIFORMBYTES, 32);
assert_eq!(bytes(), 32);
assert_eq!(scalarbytes(), 32);
assert_eq!(nonreducedscalarbytes(), 64);
assert_eq!(uniformbytes(), 32);
}
#[test]
fn test_is_valid_point() {
let p = random();
assert!(is_valid_point(&p[0..31]).is_err());
let base_point = random();
assert!(is_valid_point(&base_point).unwrap());
}
#[test]
fn test_add_sub() {
let p = random();
let q = random();
let r = add(&p, &q);
assert_eq!(r.len(), BYTES);
let s = sub(&r, &q);
assert!(is_valid_point(&s).unwrap());
}
#[test]
fn test_from_uniform() {
let r = random::bytes(UNIFORMBYTES);
let p = from_uniform(&r);
assert_eq!(p.len(), BYTES);
assert!(is_valid_point(&p).unwrap());
}
#[test]
fn test_random() {
let p = random();
assert_eq!(p.len(), BYTES);
assert!(is_valid_point(&p).unwrap());
}
#[test]
fn test_scalar_random() {
let s = scalar_random().unwrap();
assert_eq!(s.len(), SCALARBYTES);
}
#[test]
fn test_scalar_invert() {
let mut s = scalar_random().unwrap();
s[0] |= 1;
let recip = scalar_invert(&s).unwrap();
assert_eq!(recip.len(), SCALARBYTES);
let product = scalar_mul(&s, &recip).unwrap();
let check = scalar_mul(&product, &s).unwrap();
assert_eq!(check, s);
}
#[test]
fn test_scalar_negate() {
let s = scalar_random().unwrap();
let neg = scalar_negate(&s).unwrap();
assert_eq!(neg.len(), SCALARBYTES);
let sum = scalar_add(&s, &neg).unwrap();
let zero_check = scalar_mul(&sum, &s).unwrap();
assert_ne!(zero_check, s);
}
#[test]
fn test_scalar_complement() {
let s = scalar_random().unwrap();
let comp = scalar_complement(&s).unwrap();
assert_eq!(comp.len(), SCALARBYTES);
assert_ne!(comp, s);
}
#[test]
fn test_scalar_add() {
let x = scalar_random().unwrap();
let y = scalar_random().unwrap();
let z = scalar_add(&x, &y).unwrap();
assert_eq!(z.len(), SCALARBYTES);
let check = scalar_sub(&z, &y).unwrap();
assert_eq!(check, x);
}
#[test]
fn test_scalar_sub() {
let x = scalar_random().unwrap();
let y = scalar_random().unwrap();
let z = scalar_sub(&x, &y).unwrap();
assert_eq!(z.len(), SCALARBYTES);
let check = scalar_add(&z, &y).unwrap();
assert_eq!(check, x);
}
#[test]
fn test_scalar_mul() {
let x = scalar_random().unwrap();
let y = scalar_random().unwrap();
let z = scalar_mul(&x, &y).unwrap();
assert_eq!(z.len(), SCALARBYTES);
let z2 = scalar_random().unwrap();
let left = scalar_mul(&z, &z2).unwrap();
let right_temp = scalar_mul(&y, &z2).unwrap();
let right = scalar_mul(&x, &right_temp).unwrap();
assert_eq!(left, right);
}
#[test]
fn test_scalar_reduce() {
let s = random::bytes(NONREDUCEDSCALARBYTES);
let r = scalar_reduce(&s).unwrap();
assert_eq!(r.len(), SCALARBYTES);
}
}