newton-core 0.4.16

newton protocol core sdk
//! Loopback enclave attestation sentinels.
//!
//! Deterministic, recognizable attestation bytes for dev/test mode.
//! Every consumer of the loopback PCR0 hash imports from here — no copy-paste
//! of magic values. The devnet seed script whitelists `loopback_pcr0_hash()`
//! in `EnclaveVersionRegistry.activateVersion()`.

use alloy::primitives::{keccak256, FixedBytes};

/// 8-byte magic prefix distinguishing loopback attestation docs from real COSE_Sign1.
/// Parser short-circuits on this prefix — no CBOR parsing needed in dev mode.
pub const LOOPBACK_MAGIC: &[u8; 8] = b"NEWT_LBK";

/// All-zero 48-byte PCR0 for loopback enclaves.
/// Real Nitro Enclaves always have non-zero PCR0 (SHA-384 of the EIF).
pub const LOOPBACK_PCR0: [u8; 48] = [0u8; 48];

/// Compute `keccak256(LOOPBACK_PCR0)` — the on-chain hash for loopback sentinels.
///
/// This value is what gets whitelisted in `EnclaveVersionRegistry.activateVersion()`
/// for devnet and E2E tests.
pub fn loopback_pcr0_hash() -> FixedBytes<32> {
    keccak256(LOOPBACK_PCR0)
}

/// Build a loopback attestation document.
///
/// Format: `NEWT_LBK` (8 bytes) + PCR0 (48 bytes, all zeros) + nonce (variable).
/// The nonce occupies the remaining bytes and is copied verbatim from the request.
pub fn build_loopback_attestation(nonce: &[u8]) -> Vec<u8> {
    let mut doc = Vec::with_capacity(LOOPBACK_MAGIC.len() + LOOPBACK_PCR0.len() + nonce.len());
    doc.extend_from_slice(LOOPBACK_MAGIC);
    doc.extend_from_slice(&LOOPBACK_PCR0);
    doc.extend_from_slice(nonce);
    doc
}

/// Check whether raw bytes are a loopback attestation (magic prefix present).
pub fn is_loopback_attestation(bytes: &[u8]) -> bool {
    bytes.len() >= LOOPBACK_MAGIC.len() && bytes[..LOOPBACK_MAGIC.len()] == *LOOPBACK_MAGIC
}

/// Extract the 48-byte PCR0 from a loopback attestation document.
///
/// Returns `None` if the bytes are too short or not a loopback doc.
pub fn parse_loopback_pcr0(bytes: &[u8]) -> Option<&[u8]> {
    let pcr0_start = LOOPBACK_MAGIC.len();
    let pcr0_end = pcr0_start + LOOPBACK_PCR0.len();
    if is_loopback_attestation(bytes) && bytes.len() >= pcr0_end {
        Some(&bytes[pcr0_start..pcr0_end])
    } else {
        None
    }
}

/// Extract the nonce from a loopback attestation document.
///
/// Returns `None` if not a loopback doc; returns empty slice if no nonce was included.
pub fn parse_loopback_nonce(bytes: &[u8]) -> Option<&[u8]> {
    let nonce_start = LOOPBACK_MAGIC.len() + LOOPBACK_PCR0.len();
    if is_loopback_attestation(bytes) && bytes.len() >= nonce_start {
        Some(&bytes[nonce_start..])
    } else {
        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn loopback_pcr0_hash_is_deterministic() {
        let h1 = loopback_pcr0_hash();
        let h2 = loopback_pcr0_hash();
        assert_eq!(h1, h2);
        assert_ne!(h1, FixedBytes::ZERO);
    }

    #[test]
    fn build_and_parse_round_trips() {
        let nonce = b"test-nonce-1234";
        let doc = build_loopback_attestation(nonce);

        assert!(is_loopback_attestation(&doc));
        assert_eq!(parse_loopback_pcr0(&doc).unwrap(), &LOOPBACK_PCR0);
        assert_eq!(parse_loopback_nonce(&doc).unwrap(), nonce);
    }

    #[test]
    fn empty_nonce_round_trips() {
        let doc = build_loopback_attestation(&[]);

        assert!(is_loopback_attestation(&doc));
        assert_eq!(doc.len(), 56);
        assert_eq!(parse_loopback_pcr0(&doc).unwrap(), &LOOPBACK_PCR0);
        assert_eq!(parse_loopback_nonce(&doc).unwrap(), &[] as &[u8]);
    }

    #[test]
    fn real_cose_not_detected_as_loopback() {
        let cose_tag_18 = [0xd2, 0x84, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06];
        assert!(!is_loopback_attestation(&cose_tag_18));
    }

    #[test]
    fn too_short_bytes_not_loopback() {
        assert!(!is_loopback_attestation(&[]));
        assert!(!is_loopback_attestation(b"NEWT"));
        assert!(parse_loopback_pcr0(b"NEWT_LBK").is_none());
    }
}