use curve25519_dalek::edwards::{CompressedEdwardsY, EdwardsPoint};
use curve25519_dalek::scalar::{Scalar, clamp_integer};
use crate::error::{Error, KeyFormatError};
use crate::low;
use crate::mid::sha2::Sha512Context;
impl low::Zeroable for Scalar {}
impl low::Zeroable for EdwardsPoint {}
pub(crate) struct SigningKey {
seed: [u8; 32],
s: Scalar,
prefix: [u8; 32],
verifying_key: VerifyingKey,
}
impl SigningKey {
pub(crate) fn from_seed(seed: &[u8; 32]) -> Self {
low::ct::secret_slice(seed);
let h = ed25519_digest(seed, &[], &[]);
let (lower, upper) = h.split_at(32);
let s = Scalar::from_bytes_mod_order(clamp_integer(lower.try_into().unwrap()));
let prefix = upper.try_into().unwrap();
let point = EdwardsPoint::mul_base(&s);
Self {
seed: *seed,
s,
prefix,
verifying_key: low::ct::into_public(VerifyingKey {
bytes: point.compress().0,
point,
}),
}
}
pub(crate) fn sign(&self, msg: &[u8]) -> [u8; 64] {
let r_hash = ed25519_digest(&self.prefix, msg, &[]);
let r = Scalar::from_bytes_mod_order_wide(&r_hash);
let sig_r = EdwardsPoint::mul_base(&r).compress().0;
let k = ed25519_digest(&sig_r, &self.verifying_key.bytes, msg);
let k = Scalar::from_bytes_mod_order_wide(&k);
let s = k * self.s + r;
let mut sig = [0u8; 64];
sig[..32].copy_from_slice(&sig_r);
sig[32..].copy_from_slice(s.as_bytes());
low::ct::into_public(sig)
}
pub(crate) fn verifying_key(&self) -> &VerifyingKey {
&self.verifying_key
}
pub(crate) fn as_seed_bytes(&self) -> &[u8; 32] {
&self.seed
}
}
impl Drop for SigningKey {
fn drop(&mut self) {
low::zeroise(&mut self.seed);
low::zeroise_value(&mut self.s);
low::zeroise(&mut self.prefix);
low::zeroise_value(&mut self.verifying_key.point);
low::zeroise(&mut self.verifying_key.bytes);
}
}
#[derive(Clone, Debug)]
pub(crate) struct VerifyingKey {
bytes: [u8; 32],
point: EdwardsPoint,
}
impl VerifyingKey {
pub(crate) fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
let bytes: [u8; 32] = bytes.try_into().map_err(|_| KeyFormatError::WrongLength)?;
let mut y = bytes;
y[31] &= 0x7f;
let negative_zero = bytes[31] >> 7 == 1 && (y == ONE || y == P_MINUS_ONE);
if !is_below_p(&y) || negative_zero {
return Err(KeyFormatError::NotOnCurve.into());
}
let point = CompressedEdwardsY(bytes)
.decompress()
.ok_or(KeyFormatError::NotOnCurve)?;
Ok(Self { bytes, point })
}
pub(crate) fn verify(&self, sig: &[u8; 64], msg: &[u8]) -> Result<(), Error> {
let (sig_r, s) = sig.split_at(32);
let s = s.try_into().unwrap();
let s =
Option::<Scalar>::from(Scalar::from_canonical_bytes(s)).ok_or(Error::BadSignature)?;
let k = Scalar::from_bytes_mod_order_wide(&ed25519_digest(sig_r, &self.bytes, msg));
let r = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &-self.point, &s);
if r.compress().0 == sig_r {
Ok(())
} else {
Err(Error::BadSignature)
}
}
pub(crate) fn as_bytes(&self) -> [u8; 32] {
self.bytes
}
}
const ONE: [u8; 32] = {
let mut one = [0u8; 32];
one[0] = 1;
one
};
const P_MINUS_ONE: [u8; 32] = {
let mut p = [0xffu8; 32];
p[0] = 0xec;
p[31] = 0x7f;
p
};
fn is_below_p(y: &[u8; 32]) -> bool {
y.iter().rev().cmp(P_MINUS_ONE.iter().rev()).is_le()
}
fn ed25519_digest(x1: &[u8], x2: &[u8], x3: &[u8]) -> [u8; 64] {
let mut h = Sha512Context::new();
h.update(x1);
h.update(x2);
h.update(x3);
h.finish()
}