native_neural_network 0.3.1

Lib no_std Rust for native neural network (.rnn)
Documentation
use crate::crypto::ASYM_PUBLIC_KEY_LEN;

pub const ACTIVATION_TOKEN_MAX_LEN: usize = 256;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TrustServiceError {
    InvalidChallenge,
    ServiceUnavailable,
    TransportNotConfigured,
    Unauthorized,
    InvalidDecision,
    TokenExpired,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ActivationChallenge<'a> {
    pub model_sha256: [u8; 32],
    pub model_signing_pubkey: [u8; ASYM_PUBLIC_KEY_LEN],
    pub distribution_policy: u8,
    pub machine_fingerprint: &'a [u8],
    pub device_id: &'a [u8],
    pub issued_at_unix: u64,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ActivationDecision {
    pub allowed: bool,
    pub expires_unix: u64,
    pub activation_token: [u8; ACTIVATION_TOKEN_MAX_LEN],
    pub activation_token_len: usize,
}

pub trait PublisherTrustService {
    fn request_activation(
        &self,
        challenge: &ActivationChallenge<'_>,
    ) -> Result<ActivationDecision, TrustServiceError>;
}

pub fn encode_activation_challenge_binary(
    challenge: &ActivationChallenge<'_>,
    out: &mut [u8],
) -> Result<usize, TrustServiceError> {
    if challenge.machine_fingerprint.len() > u16::MAX as usize
        || challenge.device_id.len() > u16::MAX as usize
    {
        return Err(TrustServiceError::InvalidChallenge);
    }

    let needed = 4usize
        .checked_add(1)
        .and_then(|v| v.checked_add(32))
        .and_then(|v| v.checked_add(ASYM_PUBLIC_KEY_LEN))
        .and_then(|v| v.checked_add(1))
        .and_then(|v| v.checked_add(2))
        .and_then(|v| v.checked_add(challenge.machine_fingerprint.len()))
        .and_then(|v| v.checked_add(2))
        .and_then(|v| v.checked_add(challenge.device_id.len()))
        .and_then(|v| v.checked_add(8))
        .ok_or(TrustServiceError::InvalidChallenge)?;

    if out.len() < needed {
        return Err(TrustServiceError::InvalidChallenge);
    }

    let mut cursor = 0usize;
    out[cursor..cursor + 4].copy_from_slice(b"ATS0");
    cursor += 4;
    out[cursor] = 1;
    cursor += 1;

    out[cursor..cursor + 32].copy_from_slice(&challenge.model_sha256);
    cursor += 32;
    out[cursor..cursor + ASYM_PUBLIC_KEY_LEN].copy_from_slice(&challenge.model_signing_pubkey);
    cursor += ASYM_PUBLIC_KEY_LEN;
    out[cursor] = challenge.distribution_policy;
    cursor += 1;

    let mlen = challenge.machine_fingerprint.len() as u16;
    out[cursor..cursor + 2].copy_from_slice(&mlen.to_le_bytes());
    cursor += 2;
    out[cursor..cursor + challenge.machine_fingerprint.len()]
        .copy_from_slice(challenge.machine_fingerprint);
    cursor += challenge.machine_fingerprint.len();

    let dlen = challenge.device_id.len() as u16;
    out[cursor..cursor + 2].copy_from_slice(&dlen.to_le_bytes());
    cursor += 2;
    out[cursor..cursor + challenge.device_id.len()].copy_from_slice(challenge.device_id);
    cursor += challenge.device_id.len();

    out[cursor..cursor + 8].copy_from_slice(&challenge.issued_at_unix.to_le_bytes());
    cursor += 8;

    Ok(cursor)
}

pub fn validate_activation_decision_placeholder(
    decision: &ActivationDecision,
    now_unix: u64,
) -> Result<(), TrustServiceError> {
    if !decision.allowed {
        return Err(TrustServiceError::Unauthorized);
    }
    if decision.activation_token_len == 0
        || decision.activation_token_len > ACTIVATION_TOKEN_MAX_LEN
    {
        return Err(TrustServiceError::InvalidDecision);
    }
    if decision.expires_unix < now_unix {
        return Err(TrustServiceError::TokenExpired);
    }
    Ok(())
}