use super::{Ciphersuite, Scalar};
use purecrypto::ec::edwards25519::hazmat::EdwardsPoint;
use purecrypto::hash::sha512;
const CONTEXT_STRING: &[u8] = b"FROST-ED25519-SHA512-v1";
pub struct Ed25519;
impl Ciphersuite for Ed25519 {
type Point = EdwardsPoint;
const NAME: &'static str = "ed25519";
fn context_string() -> &'static [u8] {
CONTEXT_STRING
}
fn generator() -> EdwardsPoint {
EdwardsPoint::generator()
}
fn identity() -> EdwardsPoint {
EdwardsPoint::identity()
}
fn add(a: &EdwardsPoint, b: &EdwardsPoint) -> EdwardsPoint {
a.add(b)
}
fn negate(a: &EdwardsPoint) -> EdwardsPoint {
a.negate()
}
fn scalar_mul(p: &EdwardsPoint, s: &Scalar) -> EdwardsPoint {
p.mul(s)
}
fn mul_base(s: &Scalar) -> EdwardsPoint {
EdwardsPoint::mul_base(s)
}
fn eq(a: &EdwardsPoint, b: &EdwardsPoint) -> bool {
bool::from(a.ct_eq(b))
}
fn is_identity(p: &EdwardsPoint) -> bool {
bool::from(p.ct_eq(&EdwardsPoint::identity()))
}
fn encode_point(p: &EdwardsPoint) -> [u8; 32] {
p.compress()
}
fn decode_point(b: &[u8; 32]) -> Option<EdwardsPoint> {
let p = EdwardsPoint::decompress(b)?;
Some(p.mul_by_cofactor().mul(&eight_inv()))
}
fn h1(msg: &[u8]) -> Scalar {
hash_to_scalar(&[CONTEXT_STRING, b"rho", msg])
}
fn h2(msg: &[u8]) -> Scalar {
Scalar::from_bytes_mod_order(&sha512(msg))
}
fn h3(msg: &[u8]) -> Scalar {
hash_to_scalar(&[CONTEXT_STRING, b"nonce", msg])
}
fn h4(msg: &[u8]) -> [u8; 64] {
sha512(&concat(&[CONTEXT_STRING, b"msg", msg]))
}
fn h5(msg: &[u8]) -> [u8; 64] {
sha512(&concat(&[CONTEXT_STRING, b"com", msg]))
}
}
fn hash_to_scalar(parts: &[&[u8]]) -> Scalar {
Scalar::from_bytes_mod_order(&sha512(&concat(parts)))
}
fn concat(parts: &[&[u8]]) -> Vec<u8> {
let mut buf = Vec::with_capacity(parts.iter().map(|p| p.len()).sum());
for p in parts {
buf.extend_from_slice(p);
}
buf
}
fn eight_inv() -> Scalar {
let mut eight = [0u8; 32];
eight[0] = 8;
Scalar::from_bytes_canonical(&eight)
.expect("8 < L")
.invert()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::frost::Ciphersuite;
#[test]
fn h2_is_bare_sha512_reduced() {
let msg = b"FROST H2 test input";
let got = Ed25519::h2(msg);
let want = Scalar::from_bytes_mod_order(&sha512(msg));
assert!(bool::from(got.ct_eq(&want)));
}
#[test]
fn generator_roundtrips_through_encoding() {
let g = Ed25519::generator();
let enc = Ed25519::encode_point(&g);
let dec = Ed25519::decode_point(&enc).unwrap();
assert!(Ed25519::eq(&g, &dec));
}
#[test]
fn scalar_base_mult_matches_repeated_add() {
let mut three = [0u8; 32];
three[0] = 3;
let s = Scalar::from_bytes_canonical(&three).unwrap();
let lhs = Ed25519::mul_base(&s);
let g = Ed25519::generator();
let rhs = Ed25519::add(&Ed25519::add(&g, &g), &g);
assert!(Ed25519::eq(&lhs, &rhs));
}
#[test]
fn identity_is_detected() {
assert!(Ed25519::is_identity(&Ed25519::identity()));
assert!(!Ed25519::is_identity(&Ed25519::generator()));
}
#[test]
fn negate_then_add_is_identity() {
let g = Ed25519::generator();
let sum = Ed25519::add(&g, &Ed25519::negate(&g));
assert!(Ed25519::is_identity(&sum));
}
}