|
| 1 | +//! SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | +//! |
| 3 | +//! Startup diagnostic (microbench) for MLS on constrained hardware (right now, testing the Pi Zero W, ARMv6) |
| 4 | +//! Runs the exact crypto the camera hub uses in a tight hot loop in an (otherwise) idle process. |
| 5 | +//! Reports wall time, thread CPU time, throughput |
| 6 | +
|
| 7 | +use crate::openmls_rust_persistent_crypto::OpenMlsRustPersistentCrypto; |
| 8 | +use anyhow::{anyhow, Context}; |
| 9 | +use openmls::prelude::*; |
| 10 | +use openmls_basic_credential::SignatureKeyPair; |
| 11 | +use openmls_traits::crypto::OpenMlsCrypto; |
| 12 | +use openmls_traits::signatures::Signer; |
| 13 | +use openmls_traits::types::AeadType; |
| 14 | +use openmls_traits::OpenMlsProvider; |
| 15 | +use std::time::{Duration, Instant}; |
| 16 | + |
| 17 | +// Matches mls_client::CIPHERSUITE |
| 18 | +const CIPHERSUITE: Ciphersuite = Ciphersuite::MLS_256_XWING_CHACHA20POLY1305_SHA256_Ed25519; |
| 19 | +const PAYLOAD_BYTES: usize = 64 * 1024; |
| 20 | +const ITERS: usize = 20; |
| 21 | + |
| 22 | +/// Thread CPU time consumed so far (not wall time). |
| 23 | +/// Get the on-core cost, excluding time the scheduler gave to other threads. |
| 24 | +pub fn thread_cpu_time() -> anyhow::Result<Duration> { |
| 25 | + let mut ts = libc::timespec { |
| 26 | + tv_sec: 0, |
| 27 | + tv_nsec: 0, |
| 28 | + }; |
| 29 | + let rc = unsafe { libc::clock_gettime(libc::CLOCK_THREAD_CPUTIME_ID, &raw mut ts) }; |
| 30 | + if rc != 0 { |
| 31 | + return Ok(Duration::ZERO); |
| 32 | + } |
| 33 | + Ok(Duration::new(ts.tv_sec.cast_unsigned(), u32::try_from(ts.tv_nsec)?)) |
| 34 | +} |
| 35 | + |
| 36 | +fn report(name: &str, wall: Duration, cpu: Duration, bytes: usize) -> anyhow::Result<()> { |
| 37 | + let wall_ms = wall.as_secs_f64() * 1000.0f64 / f64::from(u32::try_from(ITERS)?); |
| 38 | + let cpu_ms = cpu.as_secs_f64() * 1000.0f64 / f64::from(u32::try_from(ITERS)?); |
| 39 | + let mb = f64::from(u32::try_from(bytes)?) / (1024.0f64 * 1024.0f64); |
| 40 | + let mbps = mb / (wall_ms / 1000.0f64); |
| 41 | + log::info!("{name} wall={wall_ms}ms cpu={cpu_ms}ms {mbps} MB/s"); |
| 42 | + Ok(()) |
| 43 | +} |
| 44 | + |
| 45 | +/// Run the diagnostic at process startup if the caller passes the env var. |
| 46 | +pub fn run() -> anyhow::Result<()> { |
| 47 | + log::info!("payload={} kb, iters={}", PAYLOAD_BYTES / 1024, ITERS); |
| 48 | + |
| 49 | + let provider = OpenMlsRustPersistentCrypto::default(); |
| 50 | + let signer = SignatureKeyPair::new(CIPHERSUITE.signature_algorithm())?; |
| 51 | + signer.store(provider.storage())?; |
| 52 | + let credential = BasicCredential::new(b"secluso-selftest".to_vec()); |
| 53 | + let credential_with_key = CredentialWithKey { |
| 54 | + credential: credential.into(), |
| 55 | + signature_key: signer.to_public_vec().into(), |
| 56 | + }; |
| 57 | + let group_config = MlsGroupCreateConfig::builder() |
| 58 | + .ciphersuite(CIPHERSUITE) |
| 59 | + .use_ratchet_tree_extension(true) |
| 60 | + .build(); |
| 61 | + let mut group = MlsGroup::new(&provider, &signer, &group_config, credential_with_key)?; |
| 62 | + |
| 63 | + let payload = vec![0xABu8; PAYLOAD_BYTES]; |
| 64 | + |
| 65 | + // Raw ChaCha20Poly1305 seal |
| 66 | + { |
| 67 | + let key = vec![0u8; 32]; |
| 68 | + let nonce = vec![0u8; 12]; |
| 69 | + let aad = b"secluso-selftest"; |
| 70 | + let _ = |
| 71 | + provider |
| 72 | + .crypto() |
| 73 | + .aead_encrypt(AeadType::ChaCha20Poly1305, &key, &payload, &nonce, aad); |
| 74 | + let w = Instant::now(); |
| 75 | + let c = thread_cpu_time()?; |
| 76 | + for _ in 0..ITERS { |
| 77 | + let _ct = provider.crypto().aead_encrypt( |
| 78 | + AeadType::ChaCha20Poly1305, |
| 79 | + &key, |
| 80 | + &payload, |
| 81 | + &nonce, |
| 82 | + aad, |
| 83 | + )?; |
| 84 | + } |
| 85 | + report( |
| 86 | + "aead_seal", |
| 87 | + w.elapsed(), |
| 88 | + thread_cpu_time()?.checked_sub(c).context("failed to subtract s from thread cpu time")?, |
| 89 | + PAYLOAD_BYTES, |
| 90 | + )?; |
| 91 | + } |
| 92 | + |
| 93 | + // Ed25519 signature over the full payload |
| 94 | + { |
| 95 | + let _ = signer.sign(&payload); |
| 96 | + let w = Instant::now(); |
| 97 | + let c = thread_cpu_time()?; |
| 98 | + for _ in 0..ITERS { |
| 99 | + // SignerError does not have StdError trait |
| 100 | + if let Err(e) = signer.sign(&payload) { |
| 101 | + println!("{e:?}"); |
| 102 | + return Err(anyhow!("Signer error.")); |
| 103 | + } |
| 104 | + } |
| 105 | + report( |
| 106 | + "ed25519_sign", |
| 107 | + w.elapsed(), |
| 108 | + thread_cpu_time()?.checked_sub(c).context("failed to subtract s from thread cpu time")?, |
| 109 | + PAYLOAD_BYTES, |
| 110 | + )?; |
| 111 | + } |
| 112 | + |
| 113 | + // MlsGroup::create_message |
| 114 | + { |
| 115 | + let _ = group.create_message(&provider, &signer, &payload); |
| 116 | + let w = Instant::now(); |
| 117 | + let c = thread_cpu_time()?; |
| 118 | + for _ in 0..ITERS { |
| 119 | + let _m = group.create_message(&provider, &signer, &payload)?; |
| 120 | + } |
| 121 | + report( |
| 122 | + "create_message", |
| 123 | + w.elapsed(), |
| 124 | + thread_cpu_time()?.checked_sub(c).context("failed to subtract s from thread cpu time")?, |
| 125 | + PAYLOAD_BYTES, |
| 126 | + )?; |
| 127 | + } |
| 128 | + |
| 129 | + log::info!("done"); |
| 130 | + Ok(()) |
| 131 | +} |
0 commit comments