roughtime 0.1.0

A no_std-capable Roughtime secure time-sync client with pluggable crypto backends
Documentation
//! The `aws-lc-rs` backend (also used for `aws-lc-rs-fips`, which merely enables the `fips`
//! feature on the `aws-lc-rs` dependency — the Rust code here is identical either way).
#![allow(clippy::redundant_pub_crate)]

use aws_lc_rs::digest::{Context, SHA512};
use aws_lc_rs::signature::{ED25519, UnparsedPublicKey};

use super::CryptoError;

pub(crate) fn verify_ed25519(
    public_key: &[u8],
    message: &[u8],
    signature: &[u8],
) -> Result<(), CryptoError> {
    let key = UnparsedPublicKey::new(&ED25519, public_key);
    key.verify(message, signature)
        .map_err(|_err| CryptoError::InvalidSignature)
}

pub(crate) fn sha512(data: &[u8]) -> [u8; 64] {
    let mut hasher = Context::new(&SHA512);
    hasher.update(data);
    let digest = hasher.finish();
    let mut out = [0u8; 64];
    out.copy_from_slice(digest.as_ref());
    out
}

#[cfg(feature = "std")]
pub(crate) fn fill_random(buf: &mut [u8]) -> Result<(), CryptoError> {
    aws_lc_rs::rand::fill(buf).map_err(|_err| CryptoError::RandomUnavailable)
}

#[cfg(test)]
mod tests {
    #![allow(clippy::unwrap_used)]

    use aws_lc_rs::signature::{Ed25519KeyPair, KeyPair as _};

    use super::{sha512, verify_ed25519};

    #[test]
    fn verifies_a_valid_signature() {
        let seed = [7u8; 32];
        let key = Ed25519KeyPair::from_seed_unchecked(&seed).unwrap();
        let msg = b"hello roughtime";
        let sig = aws_lc_rs::signature::Ed25519KeyPair::sign(&key, msg);

        verify_ed25519(key.public_key().as_ref(), msg, sig.as_ref()).unwrap();
    }

    #[test]
    fn rejects_a_tampered_message() {
        let seed = [7u8; 32];
        let key = Ed25519KeyPair::from_seed_unchecked(&seed).unwrap();
        let sig = key.sign(b"hello roughtime");

        assert!(verify_ed25519(key.public_key().as_ref(), b"goodbye roughtime", sig.as_ref()).is_err());
    }

    #[test]
    fn rejects_malformed_key_or_signature() {
        assert!(verify_ed25519(&[0u8; 4], b"msg", &[0u8; 64]).is_err());
    }

    #[test]
    fn sha512_matches_known_vector() {
        // NIST/RFC test vector: SHA-512("abc").
        let digest = sha512(b"abc");
        let expected = [
            0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20,
            0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, 0x0a, 0x9e, 0xee, 0xe6,
            0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba,
            0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e,
            0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f,
        ];
        assert_eq!(digest, expected);
    }
}