#![cfg_attr(not(feature = "std"), no_std)]
#![forbid(unsafe_code)]
#![deny(missing_docs)]
#![warn(clippy::all)]
#[cfg(feature = "std")]
extern crate std;
pub mod profile;
pub mod query;
pub mod registry;
pub mod rules;
pub mod select;
pub mod standards;
pub mod types;
#[cfg(feature = "std")]
pub mod export;
pub mod frameworks;
pub mod runtime;
pub use query::{all, get, related, Query};
pub use registry::REGISTRY;
pub use rules::{Rule, RULES};
pub use select::{recommend, Intent, NoRecommendation, Policy, Recommendation};
pub use types::{
Class, Constraint, Edge, Entry, FipsStatus, ImplStatus, Param, Performance, Purpose, Relation,
Severity, Strength, Unit,
};
pub const ONTOLOGY_VERSION: &str = "1.0";
pub mod errors {
use ic_core::ErrorKind;
#[derive(Debug, Clone, Copy)]
pub struct ErrorDoc {
pub id: &'static str,
pub meaning: &'static str,
pub recovery: &'static str,
pub retryable: bool,
pub caller_correctable: bool,
}
const KINDS: &[(ErrorKind, &str, &str)] = &[
(
ErrorKind::InvalidLength,
"A buffer was the wrong size for the algorithm's contract.",
"Read the parameter bounds from the ontology entry and resize the buffer.",
),
(
ErrorKind::InvalidParameter,
"A key, nonce, or parameter was structurally unacceptable.",
"Check the entry's parameters and constraints; do not retry unchanged.",
),
(
ErrorKind::AuthenticationFailed,
"A MAC, AEAD tag, or signature did not verify.",
"Treat the data as hostile. Do not use any plaintext produced alongside it, and do not \
report which byte differed.",
),
(
ErrorKind::Unsupported,
"The algorithm is described by the ontology but not implemented in this build.",
"Query the ontology for an available alternative, or obtain the algorithm elsewhere.",
),
(
ErrorKind::NotApprovedInFipsMode,
"The operation is not permitted while the module is in approved mode.",
"Select an approved algorithm, or leave approved mode deliberately and record that \
decision.",
),
(
ErrorKind::SelfTestFailed,
"A known-answer test failed; the module has entered its error state.",
"Do not retry. The binary is corrupt or the build is broken; stop and investigate.",
),
(
ErrorKind::ModuleErrorState,
"The module is latched in its error state and refuses all service.",
"Restart the process. A latched error state is not clearable at runtime by design.",
),
(
ErrorKind::EntropyFailure,
"The entropy source failed or was unavailable.",
"Retry; if it persists, seed the DRBG from a source you supply.",
),
(
ErrorKind::CounterExhausted,
"A DRBG reseed interval, GCM invocation limit, or sequence number ran out.",
"Reseed or rekey. Continuing past the limit voids the security claim.",
),
(
ErrorKind::MalformedEncoding,
"Input could not be decoded, or was encoded non-canonically.",
"Fix the encoding. A non-canonical encoding is often an attack, not a bug.",
),
(
ErrorKind::ProviderUnavailable,
"Whatever holds the key -- a token, a TPM, a key service -- could not be reached or \
could not answer. The inputs were not at fault.",
"Retry, with a delay and a limit. If it persists, the device or the service needs \
attention; changing the inputs will not help.",
),
(
ErrorKind::ProviderRefused,
"Whatever holds the key answered, and refused: a PIN expired or locked, a policy, a \
missing permission. The inputs were not at fault.",
"Do not retry: a repeated attempt can lock a token. Report it to whoever administers \
the key.",
),
(
ErrorKind::Internal,
"A library invariant was violated.",
"This is a bug in IronCrypto. Report it with the context string.",
),
];
pub fn catalog() -> impl Iterator<Item = ErrorDoc> {
KINDS.iter().map(|(kind, meaning, recovery)| ErrorDoc {
id: kind.id(),
meaning,
recovery,
retryable: kind.retryable(),
caller_correctable: kind.caller_correctable(),
})
}
pub fn get(id: &str) -> Option<ErrorDoc> {
catalog().find(|d| d.id == id)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_error_kind_is_documented() {
for kind in ErrorKind::ALL {
assert!(
get(kind.id()).is_some(),
"{} has no catalog entry, so an agent that hits it gets an identifier and no guidance",
kind.id()
);
}
assert_eq!(
catalog().count(),
ErrorKind::ALL.len(),
"the catalog and the variant list disagree on how many kinds there are"
);
}
#[test]
fn catalog_is_complete_and_unique() {
let docs: std::vec::Vec<_> = catalog().collect();
assert_eq!(docs.len(), KINDS.len());
for (i, a) in docs.iter().enumerate() {
for b in docs.iter().skip(i + 1) {
assert_ne!(a.id, b.id);
}
assert!(!a.meaning.is_empty());
assert!(!a.recovery.is_empty());
}
}
#[test]
fn authentication_failure_is_not_retryable_or_correctable() {
let d = get("authentication-failed").unwrap();
assert!(!d.retryable);
assert!(!d.caller_correctable);
}
#[test]
fn what_is_retryable_is_what_may_pass_next_time() {
assert!(get("entropy-failure").unwrap().retryable);
assert!(get("provider-unavailable").unwrap().retryable);
assert!(!get("provider-refused").unwrap().retryable);
assert!(!get("internal").unwrap().retryable);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_available_entry_names_a_real_module_path() {
let prefixes = [
"ic_hash::",
"ic_mac::",
"ic_cipher::",
"ic_kdf::",
"ic_drbg::",
"ic_ec::",
"ic_rsa::",
"ic_mlkem::",
"ic_mldsa::",
"ic_hpke::",
"ic_lms::",
"ic_slhdsa::",
];
for e in REGISTRY
.iter()
.filter(|e| matches!(e.status, ImplStatus::Available | ImplStatus::Experimental))
{
assert!(
prefixes.iter().any(|p| e.rust_path.starts_with(p)),
"{} points at {}, which is not a workspace crate",
e.id,
e.rust_path
);
}
}
#[test]
fn ontology_version_is_set() {
assert!(!ONTOLOGY_VERSION.is_empty());
}
}