use crate::error::{Error, Result};
use zeroize::Zeroize;
pub const SIGNATURE_SIZE: usize = 64;
pub const PUBLIC_KEY_SIZE: usize = 32;
pub const SECRET_KEY_SIZE: usize = 32;
#[must_use = "dropping the keypair loses secret key material without zeroization"]
pub fn keygen() -> (ed25519_dalek::VerifyingKey, ed25519_dalek::SigningKey) {
let mut seed: [u8; 32] = super::random::random_array();
let sk = ed25519_dalek::SigningKey::from_bytes(&seed);
let vk = sk.verifying_key();
seed.zeroize();
(vk, sk)
}
#[must_use = "signature must not be discarded"]
pub fn sign(sk: &ed25519_dalek::SigningKey, message: &[u8]) -> [u8; 64] {
use ed25519_dalek::Signer;
sk.sign(message).to_bytes()
}
pub fn verify(vk: &ed25519_dalek::VerifyingKey, message: &[u8], sig: &[u8; 64]) -> Result<()> {
let signature = ed25519_dalek::Signature::from_bytes(sig);
vk.verify_strict(message, &signature)
.map_err(|_| Error::VerificationFailed)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::Error;
use proptest::prelude::*;
#[test]
fn sign_verify_round_trip() {
let (vk, sk) = keygen();
let msg = b"hello ed25519";
let sig = sign(&sk, msg);
assert!(verify(&vk, msg, &sig).is_ok());
}
#[test]
fn sign_verify_different_messages() {
let (vk, sk) = keygen();
let sig = sign(&sk, b"message one");
assert!(matches!(
verify(&vk, b"message two", &sig),
Err(Error::VerificationFailed)
));
}
#[test]
fn sign_verify_different_keys() {
let (_vk1, sk1) = keygen();
let (vk2, _sk2) = keygen();
let sig = sign(&sk1, b"test message");
assert!(matches!(
verify(&vk2, b"test message", &sig),
Err(Error::VerificationFailed)
));
}
#[test]
fn signature_is_64_bytes() {
let (_vk, sk) = keygen();
let sig = sign(&sk, b"size check");
assert!(sig.iter().any(|&b| b != 0));
}
#[test]
fn sign_is_deterministic() {
let (_vk, sk) = keygen();
let msg = b"same message both times";
let sig1 = sign(&sk, msg);
let sig2 = sign(&sk, msg);
assert_eq!(sig1, sig2);
}
#[test]
fn verify_empty_message() {
let (vk, sk) = keygen();
let msg: &[u8] = &[];
let sig = sign(&sk, msg);
assert!(verify(&vk, msg, &sig).is_ok());
}
#[test]
fn verify_large_message() {
let (vk, sk) = keygen();
let msg = vec![0xABu8; 65536]; let sig = sign(&sk, &msg);
assert!(verify(&vk, &msg, &sig).is_ok());
}
#[test]
fn verify_rejects_non_canonical_s() {
let (vk, sk) = keygen();
let msg = b"non-canonical S test";
let mut sig = sign(&sk, msg);
assert!(verify(&vk, msg, &sig).is_ok());
let l_bytes: [u8; 32] = [
0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9,
0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10,
];
sig[32..64].copy_from_slice(&l_bytes);
assert!(matches!(
verify(&vk, msg, &sig),
Err(Error::VerificationFailed)
));
}
#[test]
fn regression_vector_rfc8032() {
use hex_literal::hex;
let sk_bytes = hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
let pk_bytes = hex!("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a");
let expected_sig = hex!(
"e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155"
"5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"
);
let sk = ed25519_dalek::SigningKey::from_bytes(&sk_bytes);
let vk = sk.verifying_key();
assert_eq!(vk.as_bytes(), &pk_bytes);
let sig = sign(&sk, b"");
assert_eq!(sig, expected_sig);
assert!(verify(&vk, b"", &sig).is_ok());
}
proptest! {
#![proptest_config(proptest::prelude::ProptestConfig::with_cases(1000))]
#[test]
#[allow(clippy::cast_possible_truncation)]
fn proptest_round_trip(
sk_bytes in prop::array::uniform32(any::<u8>()),
msg_len in 0..4096usize,
flip_byte in 0..64usize,
) {
let sk = ed25519_dalek::SigningKey::from_bytes(&sk_bytes);
let vk = sk.verifying_key();
let msg: Vec<u8> = (0..msg_len).map(|i| (i % 256) as u8).collect();
let sig = sign(&sk, &msg);
prop_assert!(verify(&vk, &msg, &sig).is_ok());
let mut bad_sig = sig;
bad_sig[flip_byte] ^= 0x01;
prop_assert!(verify(&vk, &msg, &bad_sig).is_err());
if !msg.is_empty() {
let mut bad_msg = msg.clone();
bad_msg[0] ^= 0x01;
prop_assert!(verify(&vk, &bad_msg, &sig).is_err());
}
}
}
}