#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Backend {
PortableConstantTime,
HardwareAccelerated,
}
impl Backend {
pub const fn id(self) -> &'static str {
match self {
Self::PortableConstantTime => "portable-constant-time",
Self::HardwareAccelerated => "hardware-accelerated",
}
}
pub const fn fast_bulk_symmetric(self) -> bool {
matches!(self, Self::HardwareAccelerated)
}
}
pub fn backend() -> Backend {
if ic_core::cpu::has_aes() && ic_core::cpu::has_ghash_clmul() {
Backend::HardwareAccelerated
} else {
Backend::PortableConstantTime
}
}
#[derive(Debug, Clone, Copy)]
pub struct Capability {
pub id: &'static str,
pub present: bool,
pub note: &'static str,
}
pub fn capabilities() -> impl Iterator<Item = Capability> {
[
Capability {
id: "no-std",
present: true,
note: "Every crate builds without the standard library; only the CLI and the string \
codec helpers require std.",
},
Capability {
id: "zero-dependencies",
present: true,
note: "No third-party crates, no C, no build scripts: for every crate that implements \
an algorithm, the dependency graph is the workspace itself, and a per-crate \
check enforces that on each build. One crate is outside it -- ic-rustls \
implements rustls's traits and so depends on rustls -- and what rustls may \
bring is listed by name, so it cannot grow unnoticed. Depend on ic-rustls and \
you inherit that; depend on anything else here and you do not.",
},
Capability {
id: "constant-time-symmetric",
present: true,
note:
"AES and GHASH avoid secret-dependent memory addressing, closing the cache-timing \
channel that table-driven implementations leave open.",
},
Capability {
id: "hardware-acceleration",
present: backend().fast_bulk_symmetric(),
note: "x86-64 AES-NI and a PCLMULQDQ GHASH, selected at runtime and validated \
against the portable backend. Absent on other targets, including 32-bit \
x86 and ARM where AES itself may run in hardware: GHASH is portable \
there, and AES-GCM runs far slower than ChaCha20-Poly1305.",
},
Capability {
id: "fips-validated",
present: false,
note: "This module implements the FIPS 140-3 discipline — approved-mode policy, \
power-on self-tests, service indicators — but it holds no CMVP certificate. Do \
not represent it as validated.",
},
Capability {
id: "post-quantum",
present: true,
note: "All three parameter sets of ML-KEM and of ML-DSA, each checked against \
NIST's published ACVP vectors -- every case in each parameter set, not a \
selection. Deploy the KEM in a hybrid with X25519 rather than alone: \
lattice cryptanalysis is young, and that is a judgement about the scheme's age \
rather than about this implementation.",
},
Capability {
id: "approved-asymmetric",
present: true,
note: "ECDSA and ECDH over P-256, P-384 and P-521, and RSA signatures in both \
PKCS#1 v1.5 and PSS, with CRT private operations.",
},
Capability {
id: "tls-provider",
present: true,
note: "AES-GCM, SHA-2, HMAC, HKDF, ECDSA verification and ECDH, wired to rustls as a \
CryptoProvider by the ic-rustls crate, for TLS 1.3 and TLS 1.2. That crate is \
the one here that depends on anything outside the workspace, because a rustls \
provider must depend on rustls. It verifies signatures and does not make them, \
so it has no signing key provider and cannot present a certificate.",
},
Capability {
id: "key-encoding",
present: true,
note: "Keys and signatures read and write as DER and PEM: SubjectPublicKeyInfo, \
PKCS#8, SEC1, and Ecdsa-Sig-Value, through ic_pkix. X.509 certificate parsing \
is not included.",
},
]
.into_iter()
}
pub fn capability(id: &str) -> Option<Capability> {
capabilities().find(|c| c.id == id)
}
pub fn has(id: &str) -> bool {
capability(id).map(|c| c.present).unwrap_or(false)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn backend_matches_the_cpu() {
let accelerated = ic_core::cpu::has_aes() && ic_core::cpu::has_ghash_clmul();
assert_eq!(
backend(),
if accelerated {
Backend::HardwareAccelerated
} else {
Backend::PortableConstantTime
}
);
assert_eq!(backend().fast_bulk_symmetric(), accelerated);
assert_eq!(has("hardware-acceleration"), accelerated);
}
#[test]
fn capabilities_are_unique_and_documented() {
let caps: std::vec::Vec<_> = capabilities().collect();
assert!(!caps.is_empty());
for (i, a) in caps.iter().enumerate() {
for b in caps.iter().skip(i + 1) {
assert_ne!(a.id, b.id);
}
assert!(!a.note.is_empty(), "{} needs a note", a.id);
}
}
#[test]
fn unimplemented_capabilities_report_false() {
assert!(!has("fips-validated"));
}
#[test]
fn capability_notes_read_as_prose() {
let mut checked = 0;
for c in capabilities() {
assert!(
!c.note.contains(" "),
"{}: the note has a run of spaces, so a line continuation is missing: {:?}",
c.id,
c.note
);
assert!(
!c.note.contains('\n') && !c.note.contains('\t'),
"{}: the note has a literal newline or tab",
c.id
);
assert!(
c.note.trim() == c.note && c.note.len() > 20,
"{}: the note is padded, or too short to say anything",
c.id
);
checked += 1;
}
assert!(checked >= 8, "only {checked} capabilities examined");
}
#[test]
fn implemented_capabilities_report_true() {
assert!(has("post-quantum"));
assert!(has("no-std"));
assert!(has("zero-dependencies"));
assert!(has("constant-time-symmetric"));
assert!(has("approved-asymmetric"));
assert!(has("key-encoding"));
assert!(has("tls-provider"));
}
#[test]
fn unknown_capability_is_false_not_a_panic() {
assert!(!has("teleportation"));
assert!(capability("teleportation").is_none());
}
}