use crate::{
log_crypto_operation_complete, log_crypto_operation_error, log_crypto_operation_start,
};
use tracing::debug;
use crate::primitives::ec::ed25519::{Ed25519KeyPair, Ed25519Signature as Ed25519SignatureOps};
use crate::primitives::ec::traits::{EcKeyPair, EcSignature};
use crate::unified_api::CoreConfig;
use crate::unified_api::error::{CoreError, Result};
use crate::unified_api::logging::op;
use crate::unified_api::zero_trust::SecurityMode;
pub(crate) fn sign_ed25519_internal(data: &[u8], ed25519_sk: &[u8]) -> Result<Vec<u8>> {
log_crypto_operation_start!(op::ED25519_SIGN, algorithm = "Ed25519", data_len = data.len());
if ed25519_sk.len() < 32 {
let err = CoreError::InvalidKeyLength { expected: 32, actual: ed25519_sk.len() };
log_crypto_operation_error!(op::ED25519_SIGN, err);
return Err(err);
}
let signing_key_bytes = ed25519_sk.get(..32).ok_or_else(|| {
let err = CoreError::InvalidInput("Private key must be at least 32 bytes".to_string());
log_crypto_operation_error!(op::ED25519_SIGN, err);
err
})?;
let keypair = Ed25519KeyPair::from_secret_key(signing_key_bytes).map_err(|e| {
let err = CoreError::InvalidInput(format!("Invalid Ed25519 secret key: {e}"));
log_crypto_operation_error!(op::ED25519_SIGN, err);
err
})?;
let signature = keypair.sign(data);
let sig_bytes = Ed25519SignatureOps::signature_bytes(&signature);
log_crypto_operation_complete!(
"ed25519_sign",
algorithm = "Ed25519",
signature_len = sig_bytes.len()
);
debug!(algorithm = "Ed25519", "Created Ed25519 signature");
Ok(sig_bytes)
}
pub(crate) fn verify_ed25519_internal(
data: &[u8],
signature_bytes: &[u8],
ed25519_pk: &[u8],
) -> Result<bool> {
log_crypto_operation_start!(op::ED25519_VERIFY, algorithm = "Ed25519", data_len = data.len());
if signature_bytes.len() < 64 {
let err = CoreError::InvalidInput(format!(
"Signature must be at least 64 bytes, got {}",
signature_bytes.len()
));
log_crypto_operation_error!(op::ED25519_VERIFY, err);
return Err(err);
}
if ed25519_pk.len() < 32 {
let err = CoreError::InvalidKeyLength { expected: 32, actual: ed25519_pk.len() };
log_crypto_operation_error!(op::ED25519_VERIFY, err);
return Err(err);
}
let sig_prefix = signature_bytes.get(..64).ok_or_else(|| {
let err = CoreError::InvalidInput("Signature must be at least 64 bytes".to_string());
log_crypto_operation_error!(op::ED25519_VERIFY, err);
err
})?;
let pk_prefix = ed25519_pk.get(..32).ok_or_else(|| {
let err = CoreError::InvalidInput("Public key must be at least 32 bytes".to_string());
log_crypto_operation_error!(op::ED25519_VERIFY, err);
err
})?;
let signature = Ed25519SignatureOps::signature_from_bytes(sig_prefix).map_err(|e| {
let err = CoreError::InvalidInput(format!("Invalid Ed25519 signature bytes: {e}"));
log_crypto_operation_error!(op::ED25519_VERIFY, err);
err
})?;
let result = match Ed25519SignatureOps::verify(pk_prefix, data, &signature) {
Ok(()) => Ok(true),
Err(crate::prelude::error::LatticeArcError::InvalidKey(msg)) => {
let err = CoreError::InvalidInput(format!("Invalid public key: {msg}"));
log_crypto_operation_error!(op::ED25519_VERIFY, err);
return Err(err);
}
Err(e) => {
debug!(error = %e, "Ed25519 verification failed with underlying error");
Err(CoreError::VerificationFailed)
}
};
match &result {
Ok(valid) => {
log_crypto_operation_complete!(
op::ED25519_VERIFY,
algorithm = "Ed25519",
valid = valid
);
debug!(algorithm = "Ed25519", valid = valid, "Ed25519 verification completed");
}
Err(e) => {
log_crypto_operation_error!(op::ED25519_VERIFY, e);
}
}
result
}
#[inline]
pub fn sign_ed25519(data: &[u8], ed25519_sk: &[u8], mode: SecurityMode) -> Result<Vec<u8>> {
mode.validate()?;
sign_ed25519_internal(data, ed25519_sk)
}
#[inline]
pub fn verify_ed25519(
data: &[u8],
signature_bytes: &[u8],
ed25519_pk: &[u8],
mode: SecurityMode,
) -> Result<bool> {
mode.validate()?;
verify_ed25519_internal(data, signature_bytes, ed25519_pk)
}
#[inline]
pub fn sign_ed25519_with_config(
data: &[u8],
ed25519_sk: &[u8],
config: &CoreConfig,
mode: SecurityMode,
) -> Result<Vec<u8>> {
mode.validate()?;
config.validate()?;
sign_ed25519_internal(data, ed25519_sk)
}
#[inline]
pub fn verify_ed25519_with_config(
data: &[u8],
signature_bytes: &[u8],
ed25519_pk: &[u8],
config: &CoreConfig,
mode: SecurityMode,
) -> Result<bool> {
mode.validate()?;
config.validate()?;
verify_ed25519_internal(data, signature_bytes, ed25519_pk)
}
#[inline]
pub fn sign_ed25519_unverified(data: &[u8], ed25519_sk: &[u8]) -> Result<Vec<u8>> {
sign_ed25519(data, ed25519_sk, SecurityMode::Unverified)
}
#[inline]
pub fn verify_ed25519_unverified(
data: &[u8],
signature_bytes: &[u8],
ed25519_pk: &[u8],
) -> Result<bool> {
verify_ed25519(data, signature_bytes, ed25519_pk, SecurityMode::Unverified)
}
#[inline]
pub fn sign_ed25519_with_config_unverified(
data: &[u8],
ed25519_sk: &[u8],
config: &CoreConfig,
) -> Result<Vec<u8>> {
sign_ed25519_with_config(data, ed25519_sk, config, SecurityMode::Unverified)
}
#[inline]
pub fn verify_ed25519_with_config_unverified(
data: &[u8],
signature_bytes: &[u8],
ed25519_pk: &[u8],
config: &CoreConfig,
) -> Result<bool> {
verify_ed25519_with_config(data, signature_bytes, ed25519_pk, config, SecurityMode::Unverified)
}
#[cfg(test)]
#[allow(
clippy::panic,
clippy::unwrap_used,
clippy::expect_used,
clippy::indexing_slicing,
clippy::arithmetic_side_effects,
clippy::panic_in_result_fn,
clippy::unnecessary_wraps,
clippy::redundant_clone,
clippy::useless_vec,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::clone_on_copy,
clippy::len_zero,
clippy::single_match,
clippy::unnested_or_patterns,
clippy::default_constructed_unit_structs,
clippy::redundant_closure_for_method_calls,
clippy::semicolon_if_nothing_returned,
clippy::unnecessary_unwrap,
clippy::redundant_pattern_matching,
clippy::missing_const_for_thread_local,
clippy::get_first,
clippy::float_cmp,
clippy::needless_borrows_for_generic_args,
unused_qualifications
)]
mod tests {
use super::*;
use crate::{SecurityMode, VerifiedSession, generate_keypair};
#[test]
fn test_sign_verify_ed25519_unverified_roundtrip_succeeds() -> Result<()> {
let message = b"Test message for Ed25519";
let (pk, sk) = generate_keypair()?;
let signature = sign_ed25519_unverified(message, sk.as_ref())?;
assert!(!signature.is_empty());
assert_eq!(signature.len(), 64, "Ed25519 signature should be 64 bytes");
let is_valid = verify_ed25519_unverified(message, &signature, pk.as_slice())?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_sign_ed25519_deterministic_produces_same_signature_is_deterministic() -> Result<()> {
let message = b"Same message";
let (_, sk) = generate_keypair()?;
let sig1 = sign_ed25519_unverified(message, sk.as_ref())?;
let sig2 = sign_ed25519_unverified(message, sk.as_ref())?;
assert_eq!(sig1, sig2, "Ed25519 signatures should be deterministic");
Ok(())
}
#[test]
fn test_verify_ed25519_wrong_message_returns_error() {
let message = b"Original message";
let wrong_message = b"Wrong message";
let (pk, sk) = generate_keypair().expect("keygen should succeed");
let signature =
sign_ed25519_unverified(message, sk.as_ref()).expect("signing should succeed");
let result = verify_ed25519_unverified(wrong_message, &signature, pk.as_slice());
assert!(result.is_err(), "Verification should fail for wrong message");
}
#[test]
fn test_verify_ed25519_invalid_signature_returns_error() {
let message = b"Test message";
let (pk, _sk) = generate_keypair().expect("keygen should succeed");
let invalid_signature = vec![0u8; 64];
let result = verify_ed25519_unverified(message, &invalid_signature, pk.as_slice());
assert!(result.is_err(), "Verification should fail for invalid signature");
}
#[test]
fn test_verify_ed25519_wrong_public_key_returns_error() {
let message = b"Test message";
let (_, sk) = generate_keypair().expect("keygen should succeed");
let (wrong_pk, _) = generate_keypair().expect("keygen should succeed");
let signature =
sign_ed25519_unverified(message, sk.as_ref()).expect("signing should succeed");
let result = verify_ed25519_unverified(message, &signature, wrong_pk.as_slice());
assert!(result.is_err(), "Verification should fail with wrong public key");
}
#[test]
fn test_sign_verify_ed25519_with_config_unverified_roundtrip() -> Result<()> {
let message = b"Test with config";
let (pk, sk) = generate_keypair()?;
let config = CoreConfig::default();
let signature = sign_ed25519_with_config_unverified(message, sk.as_ref(), &config)?;
let is_valid =
verify_ed25519_with_config_unverified(message, &signature, pk.as_slice(), &config)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_sign_verify_ed25519_verified_roundtrip_succeeds() -> Result<()> {
let message = b"Test with verified session";
let (pk, sk) = generate_keypair()?;
let (auth_pk, auth_sk) = generate_keypair()?;
let session = VerifiedSession::establish(auth_pk.as_slice(), auth_sk.as_ref())?;
let signature = sign_ed25519(message, sk.as_ref(), SecurityMode::Verified(&session))?;
let is_valid =
verify_ed25519(message, &signature, pk.as_slice(), SecurityMode::Verified(&session))?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_sign_verify_ed25519_unverified_mode_roundtrip_succeeds() -> Result<()> {
let message = b"Test unverified mode";
let (pk, sk) = generate_keypair()?;
let signature = sign_ed25519(message, sk.as_ref(), SecurityMode::Unverified)?;
let is_valid =
verify_ed25519(message, &signature, pk.as_slice(), SecurityMode::Unverified)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_sign_verify_ed25519_with_config_verified_roundtrip() -> Result<()> {
let message = b"Test with config and session";
let (pk, sk) = generate_keypair()?;
let config = CoreConfig::default();
let (auth_pk, auth_sk) = generate_keypair()?;
let session = VerifiedSession::establish(auth_pk.as_slice(), auth_sk.as_ref())?;
let signature = sign_ed25519_with_config(
message,
sk.as_ref(),
&config,
SecurityMode::Verified(&session),
)?;
let is_valid = verify_ed25519_with_config(
message,
&signature,
pk.as_slice(),
&config,
SecurityMode::Verified(&session),
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_sign_verify_ed25519_with_config_unverified_mode_roundtrip() -> Result<()> {
let message = b"Test with config unverified mode";
let (pk, sk) = generate_keypair()?;
let config = CoreConfig::default();
let signature =
sign_ed25519_with_config(message, sk.as_ref(), &config, SecurityMode::Unverified)?;
let is_valid = verify_ed25519_with_config(
message,
&signature,
pk.as_slice(),
&config,
SecurityMode::Unverified,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_ed25519_empty_message_signs_and_verifies_succeeds() -> Result<()> {
let message = b"";
let (pk, sk) = generate_keypair()?;
let signature = sign_ed25519_unverified(message, sk.as_ref())?;
let is_valid = verify_ed25519_unverified(message, &signature, pk.as_slice())?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_ed25519_large_message_signs_and_verifies_succeeds() -> Result<()> {
let message = vec![0xAB; 100000];
let (pk, sk) = generate_keypair()?;
let signature = sign_ed25519_unverified(&message, sk.as_ref())?;
let is_valid = verify_ed25519_unverified(&message, &signature, pk.as_slice())?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_ed25519_signature_length_is_constant_64_bytes_has_correct_size() -> Result<()> {
let (_, sk) = generate_keypair()?;
let short_msg = b"short";
let long_msg = vec![0xFF; 10000];
let sig1 = sign_ed25519_unverified(short_msg, sk.as_ref())?;
let sig2 = sign_ed25519_unverified(&long_msg, sk.as_ref())?;
assert_eq!(sig1.len(), 64, "Signature length should be constant");
assert_eq!(sig2.len(), 64, "Signature length should be constant");
Ok(())
}
#[test]
fn test_ed25519_different_messages_produce_different_signatures_succeeds() -> Result<()> {
let (_, sk) = generate_keypair()?;
let msg1 = b"First message";
let msg2 = b"Second message";
let sig1 = sign_ed25519_unverified(msg1, sk.as_ref())?;
let sig2 = sign_ed25519_unverified(msg2, sk.as_ref())?;
assert_ne!(sig1, sig2, "Different messages should produce different signatures");
Ok(())
}
#[test]
fn test_ed25519_different_keys_produce_different_signatures_succeeds() -> Result<()> {
let message = b"Same message";
let (_, sk1) = generate_keypair()?;
let (_, sk2) = generate_keypair()?;
let sig1 = sign_ed25519_unverified(message, sk1.as_ref())?;
let sig2 = sign_ed25519_unverified(message, sk2.as_ref())?;
assert_ne!(sig1, sig2, "Different keys should produce different signatures");
Ok(())
}
#[test]
fn test_ed25519_invalid_signature_length_returns_error() {
let message = b"Test message";
let (pk, _sk) = generate_keypair().expect("keygen should succeed");
let invalid_sig = vec![0u8; 32];
let result = verify_ed25519_unverified(message, &invalid_sig, pk.as_slice());
assert!(result.is_err(), "Should reject signature with wrong length");
}
#[test]
fn test_ed25519_invalid_public_key_length_returns_error() {
let message = b"Test message";
let (_, sk) = generate_keypair().expect("keygen should succeed");
let invalid_pk = vec![0u8; 16];
let signature =
sign_ed25519_unverified(message, sk.as_ref()).expect("signing should succeed");
let result = verify_ed25519_unverified(message, &signature, &invalid_pk);
assert!(result.is_err(), "Should reject public key with wrong length");
}
#[test]
fn test_ed25519_invalid_secret_key_length_returns_error() {
let message = b"Test message";
let invalid_sk = vec![0u8; 16];
let result = sign_ed25519_unverified(message, &invalid_sk);
assert!(result.is_err(), "Should reject secret key with wrong length");
}
#[test]
fn test_ed25519_empty_key_returns_error() {
let message = b"Test message";
let result = sign_ed25519_unverified(message, &[]);
assert!(result.is_err(), "Empty secret key should fail");
}
#[test]
fn test_ed25519_verify_empty_signature_returns_error() {
let message = b"Test message";
let (pk, _sk) = generate_keypair().expect("keygen should succeed");
let result = verify_ed25519_unverified(message, &[], pk.as_slice());
assert!(result.is_err(), "Empty signature should fail verification");
}
#[test]
fn test_ed25519_verify_empty_public_key_returns_error() {
let message = b"Test message";
let (_, sk) = generate_keypair().expect("keygen should succeed");
let signature = sign_ed25519_unverified(message, sk.as_ref()).unwrap();
let result = verify_ed25519_unverified(message, &signature, &[]);
assert!(result.is_err(), "Empty public key should fail");
}
#[test]
fn test_ed25519_verify_invalid_public_key_format_returns_error() {
let message = b"Test message";
let (_, sk) = generate_keypair().expect("keygen should succeed");
let signature = sign_ed25519_unverified(message, sk.as_ref()).unwrap();
let bad_pk = vec![0xFF; 32];
let result = verify_ed25519_unverified(message, &signature, bad_pk.as_slice());
assert!(result.is_err(), "Invalid Ed25519 point should fail");
}
#[test]
fn test_ed25519_sign_with_config_validation_succeeds() {
let message = b"Test message";
let (_, sk) = generate_keypair().expect("keygen should succeed");
let config = CoreConfig::default();
let result =
sign_ed25519_with_config(message, sk.as_ref(), &config, SecurityMode::Unverified);
assert!(result.is_ok(), "Signing with valid config should succeed");
}
#[test]
fn test_ed25519_verify_with_config_validation_succeeds() {
let message = b"Test message";
let (pk, sk) = generate_keypair().expect("keygen should succeed");
let config = CoreConfig::default();
let signature = sign_ed25519_unverified(message, sk.as_ref()).unwrap();
let result = verify_ed25519_with_config(
message,
&signature,
pk.as_slice(),
&config,
SecurityMode::Unverified,
);
assert!(result.is_ok());
}
#[test]
fn test_ed25519_sign_with_config_unverified_succeeds() {
let message = b"Test message";
let (_, sk) = generate_keypair().expect("keygen should succeed");
let config = CoreConfig::default();
let result = sign_ed25519_with_config_unverified(message, sk.as_ref(), &config);
assert!(result.is_ok());
}
#[test]
fn test_ed25519_verify_with_config_unverified_succeeds() {
let message = b"Test message";
let (pk, sk) = generate_keypair().expect("keygen should succeed");
let config = CoreConfig::default();
let signature = sign_ed25519_unverified(message, sk.as_ref()).unwrap();
let result =
verify_ed25519_with_config_unverified(message, &signature, pk.as_slice(), &config);
assert!(result.is_ok());
}
}