use std::sync::atomic::{AtomicU64, Ordering};
use crate::error::PlatformError;
use crate::traits::{DeviceAttestation, DeviceAttestationToken};
const SYNTHETIC_ATTESTATION_PREFIX: &[u8] = b"scp-test-attestation-v1:";
pub struct InMemoryDeviceAttestation {
counter: AtomicU64,
}
impl InMemoryDeviceAttestation {
#[must_use]
pub const fn new() -> Self {
Self {
counter: AtomicU64::new(1),
}
}
}
impl Default for InMemoryDeviceAttestation {
fn default() -> Self {
Self::new()
}
}
#[allow(clippy::manual_async_fn)]
impl DeviceAttestation for InMemoryDeviceAttestation {
fn attest(&self) -> impl Future<Output = Result<DeviceAttestationToken, PlatformError>> + Send {
async move {
let seq = self.counter.fetch_add(1, Ordering::Relaxed);
let mut token_bytes = Vec::from(SYNTHETIC_ATTESTATION_PREFIX);
token_bytes.extend_from_slice(&seq.to_le_bytes());
Ok(DeviceAttestationToken::new(token_bytes))
}
}
fn verify(
&self,
token: &DeviceAttestationToken,
) -> impl Future<Output = Result<bool, PlatformError>> + Send {
let has_prefix = token.as_bytes().starts_with(SYNTHETIC_ATTESTATION_PREFIX);
async move { Ok(has_prefix) }
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
#[tokio::test]
async fn attest_returns_token_with_prefix() {
let attestation = InMemoryDeviceAttestation::new();
let token = attestation.attest().await.unwrap();
assert!(token.as_bytes().starts_with(SYNTHETIC_ATTESTATION_PREFIX));
}
#[tokio::test]
async fn verify_own_attestation_returns_true() {
let attestation = InMemoryDeviceAttestation::new();
let token = attestation.attest().await.unwrap();
assert!(attestation.verify(&token).await.unwrap());
}
#[tokio::test]
async fn verify_foreign_token_returns_false() {
let attestation = InMemoryDeviceAttestation::new();
let foreign = DeviceAttestationToken::new(b"not-our-token".to_vec());
assert!(!attestation.verify(&foreign).await.unwrap());
}
#[tokio::test]
async fn verify_empty_token_returns_false() {
let attestation = InMemoryDeviceAttestation::new();
let empty = DeviceAttestationToken::new(vec![]);
assert!(!attestation.verify(&empty).await.unwrap());
}
#[tokio::test]
async fn sequential_attestations_produce_unique_tokens() {
let attestation = InMemoryDeviceAttestation::new();
let first = attestation.attest().await.unwrap();
let second = attestation.attest().await.unwrap();
assert_ne!(first.as_bytes(), second.as_bytes());
}
#[tokio::test]
async fn multiple_attestation_instances_both_verify_own_tokens() {
let first_att = InMemoryDeviceAttestation::new();
let second_att = InMemoryDeviceAttestation::new();
let first_token = first_att.attest().await.unwrap();
let second_token = second_att.attest().await.unwrap();
assert!(first_att.verify(&first_token).await.unwrap());
assert!(second_att.verify(&second_token).await.unwrap());
assert!(first_att.verify(&second_token).await.unwrap());
assert!(second_att.verify(&first_token).await.unwrap());
}
}