arcium-primitives 0.4.3

Arcium primitives
Documentation
pub use ed25519_dalek::{Signature, SigningKey, VerifyingKey};
use ed25519_dalek::{Signer as DalekSigner, Verifier as DalekVerifier};
use itertools::chain;

use crate::{errors::PrimitiveError, types::SessionId};

/// Message identifier type: (SessionId, round, protocol tag)
pub type MessageId = (SessionId, u16, &'static [u8]);

// ------------------------------ Defaults ---------------------------------- //
// Set Ed25519 as the default signing scheme
pub trait Signer: UCSigner<Signature> {}
impl<SR: UCSigner<Signature>> Signer for SR {}

pub trait Verifier: UCVerifier<Signature> + Sync + Send {}
impl<VR: UCVerifier<Signature>> Verifier for VR {}

// -------------------------------- Traits ---------------------------------- //
/// Signer that signs messages alongside metadata (round, protocol tag and session ID)
/// to avoid replay attacks in UC protocols.
pub trait UCSigner<S>: DalekSigner<S> + Sync + Send {
    /// Signs a message for a protocol, uniquely identified by the triple (round, protocol tag,
    /// session ID) to avoid replay attacks.
    fn sign_with(&self, message: &[u8], id: &MessageId) -> Result<S, PrimitiveError> {
        let round = id.1.to_le_bytes();
        let message_with_id: Vec<u8> = chain!(message, &id.0, &round, id.2).copied().collect();
        let signature = self
            .try_sign(&message_with_id)
            .map_err(|e| PrimitiveError::WrongSignature(e.to_string()))?;
        Ok(signature)
    }
}

/// Verifier that verifies message signatures with metadata (round, protocol tag and session ID)
/// to avoid replay attacks in UC protocols.
pub trait UCVerifier<S>: DalekVerifier<S> + Sync + Send {
    /// Verifies a signature for a protocol, uniquely identified by the triple (round, protocol tag,
    /// session ID) to avoid replay attacks.
    fn verify_with(
        &self,
        message: &[u8],
        signature: &S,
        &id: &MessageId,
    ) -> Result<(), PrimitiveError> {
        let round = id.1.to_le_bytes();
        let message_with_id: Vec<u8> = chain!(message, &id.0, &round, id.2).copied().collect();
        self.verify(&message_with_id, signature)
            .map_err(|e| PrimitiveError::WrongSignature(e.to_string()))?;
        Ok(())
    }
}

impl<S, T: DalekSigner<S> + Sync + Send> UCSigner<S> for T {}
impl<S, T: DalekVerifier<S> + Sync + Send> UCVerifier<S> for T {}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        random::{self, Random},
        types::SessionId,
    };

    #[test]
    fn test_sign_protocol_message() {
        let mut rng = random::test_rng();
        let signing_key = SigningKey::generate(&mut rng);
        let message = b"Hello, protocol!";
        let protocol_id: &'static [u8] = &[2, 1];
        let message_id = (SessionId::random(&mut rng), 1, protocol_id);

        let signature = signing_key.sign_with(message, &message_id).unwrap();
        assert!(signing_key
            .verify_with(message, &signature, &message_id)
            .is_ok());
    }
}