use super::curve25519::{ge_scalarmult_base, is_identity, sc_muladd, sc_reduce, GeP2, GeP3};
use super::sha512;
static L: [u8; 32] = [
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x14, 0xde, 0xf9, 0xde, 0xa2, 0xf7, 0x9c, 0xd6, 0x58, 0x12, 0x63, 0x1a, 0x5c, 0xf5, 0xd3, 0xed,
];
fn check_s_lt_l(s: &[u8]) -> bool {
let mut c: u8 = 0;
let mut n: u8 = 1;
let mut i = 31;
loop {
c |= ((((s[i] as i32) - (L[i] as i32)) >> 8) as u8) & n;
n &= ((((s[i] ^ L[i]) as i32) - 1) >> 8) as u8;
if i == 0 {
break;
} else {
i -= 1;
}
}
c == 0
}
pub fn verify(message: &[u8], public_key: &[u8], signature: &[u8]) -> bool {
if check_s_lt_l(&signature[32..64]) || is_identity(public_key) {
return false;
}
let a = match GeP3::from_bytes_negate_vartime(public_key) {
Some(g) => g,
None => {
return false;
}
};
if public_key.iter().fold(0, |acc, x| acc | x) == 0 {
return false;
}
let mut hasher = sha512::Hash::new();
hasher.update(&signature[0..32]);
hasher.update(public_key);
hasher.update(message);
let mut hash = hasher.finalize();
sc_reduce(&mut hash);
let r = GeP2::double_scalarmult_vartime(hash.as_ref(), a, &signature[32..64]);
r.to_bytes()
.as_ref()
.iter()
.zip(signature.iter())
.fold(0, |acc, (x, y)| acc | (x ^ y))
== 0
}
pub fn keypair(seed: &[u8]) -> ([u8; 64], [u8; 32]) {
let mut secret: [u8; 64] = {
let mut hash_output = sha512::Hash::hash(seed);
hash_output[0] &= 248;
hash_output[31] &= 63;
hash_output[31] |= 64;
hash_output
};
let a = ge_scalarmult_base(&secret[0..32]);
let public_key = a.to_bytes();
for (dest, src) in secret[32..64].iter_mut().zip(public_key.iter()) {
*dest = *src;
}
for (dest, src) in secret[0..32].iter_mut().zip(seed.iter()) {
*dest = *src;
}
(secret, public_key)
}
pub fn signature(message: &[u8], secret_key: &[u8], z: Option<&[u8]>) -> [u8; 64] {
let seed = &secret_key[0..32];
let public_key = &secret_key[32..64];
let az: [u8; 64] = {
let mut hash_output = sha512::Hash::hash(seed);
hash_output[0] &= 248;
hash_output[31] &= 63;
hash_output[31] |= 64;
hash_output
};
let nonce = {
let mut hasher = sha512::Hash::new();
if let Some(z) = z {
hasher.update(z);
hasher.update(&az[..]);
} else {
hasher.update(&az[32..64]);
}
hasher.update(message);
let mut hash_output = hasher.finalize();
sc_reduce(&mut hash_output[0..64]);
hash_output
};
let mut signature: [u8; 64] = [0; 64];
let r: GeP3 = ge_scalarmult_base(&nonce[0..32]);
for (result_byte, source_byte) in signature[0..32].iter_mut().zip(r.to_bytes().iter()) {
*result_byte = *source_byte;
}
for (result_byte, source_byte) in signature[32..64].iter_mut().zip(public_key.iter()) {
*result_byte = *source_byte;
}
{
let mut hasher = sha512::Hash::new();
hasher.update(signature.as_ref());
hasher.update(message);
let mut hram = hasher.finalize();
sc_reduce(&mut hram);
sc_muladd(
&mut signature[32..64],
&hram[0..32],
&az[0..32],
&nonce[0..32],
);
}
signature
}