Announcing IronCrypto: agentic-first cryptography in pure Rust
Most cryptographic failures aren't caused by somebody breaking AES.
They're caused by perfectly good cryptography being used incorrectly: a GCM nonce gets reused, a password is fed into a fast hash, an unauthenticated cipher mode gets treated like an AEAD, a MAC gets compared with ==, or an application silently substitutes the algorithm it has for the one its security policy actually requires.
Humans are supposed to learn those rules from documentation, standards, code review, and experience.
AI agents don't work that way.
Give an autonomous coding agent a conventional cryptography library and it gets a namespace full of primitives. The knowledge required to use them safely lives somewhere else — documentation pages, RFCs, NIST publications, security advisories, tribal knowledge, and hopefully the model's training data. The agent is expected to connect those pieces correctly every time.
That is not a good security architecture.
Today we're announcing IronCrypto: an agentic-first cryptography library written in pure Rust, with the rules surrounding cryptography represented alongside the implementation as a machine-readable ontology.
It is no_std from the ground up, has zero third-party dependencies in every algorithm crate, requires no C toolchain or build scripts, includes post-quantum cryptography, provides a rustls CryptoProvider, and exposes its capabilities and security constraints directly to AI agents through MCP.
IronCrypto is dual-licensed under AGPL-3.0-or-later, with a commercial license available from NERVOSYS.
The ontology is part of the cryptography
A traditional crypto API answers questions like:
Can you instantiate AES-256-GCM?
IronCrypto can answer a more useful question:
What should I use to encrypt a message under these constraints, and what rules must I obey when I use it?
$ ic recommend encrypt-message --fips
use: aes-256-gcm
AES-256-GCM is the approved authenticated cipher and retains 128-bit strength
against a quantum adversary.
call: ic_cipher::Aes256Gcm
must observe:
[critical] Never reuse a (key, nonce) pair.
[serious] Derive the nonce from a strictly increasing counter,
or draw 96 random bits and bound the number of
messages per key.
considered and rejected:
chacha20-poly1305:
Not approved for the FIPS approved mode of operation.
aes-ctr:
Unauthenticated; it is the confidentiality half of GCM.
Those constraints aren't prose scraped out of a README. They're structured data:
$ ic ontology show aes-256-gcm --json | jq '.constraints[0]'
{
"consequence": "Reuse leaks the authentication subkey, allowing forgery of arbitrary messages, and XORs the two plaintexts together.",
"id": "unique-nonce-per-key",
"requirement": "Never reuse a (key, nonce) pair.",
"severity": "critical"
}
That changes what an autonomous system can do.
An agent can refuse to generate code that violates a critical constraint. A CI pipeline can reject use of algorithms marked excluded. A reviewer can diff a policy change. Another agent can ask why an algorithm was rejected instead of reverse-engineering that decision from source code.
Security knowledge becomes executable infrastructure rather than documentation somebody is expected to remember.
"No" is a valid cryptographic answer
One of the most important rules in IronCrypto is simple:
Never silently weaken the caller's requirement just because something else is available.
Ask for a FIPS-approved signature and IronCrypto can recommend ECDSA P-256 while explicitly explaining why Ed25519 and ML-DSA were not selected under that policy.
Ask for something that no available primitive can honestly satisfy and it returns no recommendation:
use ic_ontology::select::{recommend, Intent, NoRecommendation, Policy};
let outcome = recommend(
Intent::HashData,
Policy {
require_fips: false,
min_classical_bits: 0,
min_quantum_bits: 512,
aes_hardware: false,
},
);
assert_eq!(
outcome.unwrap_err(),
NoRecommendation::NothingSatisfiesPolicy
);
For an autonomous caller, an honest "nothing satisfies this policy" is more useful than a plausible-looking answer that quietly changes the policy.
That principle runs through the project: capabilities are reported as capabilities, gaps are reported as gaps, and unsupported algorithms stay visible in the ontology so asking for them produces a reasoned refusal rather than a missing symbol.
Pure Rust, all the way down
IronCrypto's algorithm crates have no third-party dependencies.
No OpenSSL.
No C compiler.
No platform crypto shim hiding underneath the API.
No build script downloading something you didn't ask for.
The implementation is no_std from the ground up and has been built for ARM Cortex-M, RISC-V, WebAssembly, and conventional desktop targets. The ic-rustls adapter is deliberately the exception to the dependency rule because implementing rustls's provider traits necessarily means depending on rustls; that exception is isolated and explicit rather than weakening the claim for the rest of the workspace.
The facade crate is straightforward:
[dependencies]
iron-crypto = "0.1"
Or depend only on the primitive families you need:
[dependencies]
ic-cipher = "0.1"
ic-hash = "0.1"
ic-ec = "0.1"
The workspace currently publishes eighteen crates, with iron-crypto re-exporting the full surface.
The primitive surface
IronCrypto already covers most of the cryptography a modern application actually reaches for:
- SHA-2, SHA-3, SHAKE, BLAKE2b
- HMAC, CMAC, KMAC, Poly1305
- AES-128/192/256
- CBC, CTR, AES-KW and KWP
- AES-GCM, AES-GCM-SIV, ChaCha20-Poly1305
- HKDF, PBKDF2, SP 800-108 and Argon2
- HMAC_DRBG and CTR_DRBG
- P-256, P-384 and P-521
- X25519 and Ed25519
- RSA-PSS and PKCS#1 v1.5 signatures
- ML-KEM-768
- ML-DSA-65
- DER and PEM key encodings
- TLS 1.2, TLS 1.3 and QUIC integration through rustls
Hardware acceleration is selected at runtime where available: AES-NI for AES, PCLMULQDQ for GHASH, SHA-NI for SHA-256, and AVX2 for ChaCha20. The portable implementation remains the fallback everywhere else.
Legacy algorithms including MD5, SHA-1, and Triple DES remain in the ontology specifically so an agent asking for them gets an explicit rejection and reason instead of an unexplained absence.
Connect an agent
Install the CLI:
cargo install ic-cli
Then expose IronCrypto as a Model Context Protocol server:
{
"mcpServers": {
"iron-crypto": {
"command": "ic",
"args": ["mcp"]
}
}
}
The agent gets tools for questions including:
crypto_recommend What should I use for X under constraints Y?
ontology_list What algorithms serve this purpose?
ontology_show What are the parameter bounds and failure modes?
ontology_errors What does this error mean, and can I retry?
crypto_capabilities What can this build actually do?
crypto_selftest Is the module healthy?
crypto_digest Compute a digest
crypto_hmac Compute an HMAC
crypto_seal Perform authenticated encryption
crypto_random Generate cryptographic randomness
There are also machine-readable views over security controls and framework mappings.
The important distinction is that the agent doesn't need to infer the library's semantics by reading Rust source or scraping human documentation. Discovery, recommendation, constraints, capability reporting, and execution all belong to the same system.
TLS without handing the crypto back to C
ic-rustls implements rustls's CryptoProvider, allowing IronCrypto to provide the underlying primitives for TLS 1.2 and TLS 1.3:
let roots = rustls::RootCertStore::empty();
let config =
rustls::ClientConfig::builder_with_provider(ic_rustls::arc_provider())
.with_safe_default_protocol_versions()?
.with_root_certificates(roots)
.with_no_client_auth();
The provider supplies AES-GCM and ChaCha20-Poly1305, SHA-256 and SHA-384, HMAC and HKDF, ECDSA, Ed25519 and RSA signatures, X25519 and NIST-curve ECDH, and OS-seeded SP 800-90A randomness.
It can verify signatures and produce them, so the same provider can authenticate a server, run a server, or present a client certificate rather than functioning as a verify-only adapter.
Post-quantum, but not by assertion
IronCrypto implements ML-KEM-768 from FIPS 203 and ML-DSA-65 from FIPS 204.
Both are checked against NIST ACVP vectors.
The project's test-vector machinery deliberately distinguishes between code that merely exists and code that has been checked against values produced independently of itself. An implemented algorithm with no external vector is marked experimental. Once the corresponding published test vectors are added, those tests become part of the build.
ML-KEM, ML-DSA, and AES-GCM-SIV all went through that transition.
At the current release, no implemented algorithm remains marked experimental.
FIPS-aware is not FIPS-validated
This distinction matters enough to deserve its own section.
IronCrypto implements approved-mode policy, pre-operational self-tests, algorithm known-answer tests, service indicators, and a latching error state.
It does not have a CMVP certificate.
Therefore it does not claim to be a FIPS-validated cryptographic module.
$ ic capabilities
[x] no-std
[x] zero-dependencies
[x] constant-time-symmetric
[x] hardware-acceleration
[ ] fips-validated
[x] post-quantum
[x] approved-asymmetric
[x] tls-provider
[x] key-encoding
The ontology carries that distinction all the way into control mappings. CMMC requirements that specifically require FIPS-validated cryptography remain unsatisfied because correctness evidence is not a substitute for laboratory validation and a certificate number.
That is exactly the sort of distinction an autonomous system must not be allowed to round away.
Reproducible supply-chain evidence
IronCrypto can emit a deterministic CycloneDX 1.5 SBOM:
ic sbom > bom.json
There is deliberately no timestamp or random serial number in the generated document, so identical source produces byte-identical output.
The component list is checked against the workspace manifest as part of the test suite. Add a crate without adding it to the SBOM model and the build fails.
The ontology also maps relevant controls and weakness classes across CWE, MITRE ATT&CK, and CMMC:
ic ontology controls --framework cwe
ic ontology controls --framework attack
ic ontology controls --framework cmmc
Again, these aren't intended to turn a library into a magic compliance checkbox. The same interface exposes what remains unsatisfied.
Performance
Security libraries don't get a free pass on performance.
On a Ryzen 9 9900X, IronCrypto's accelerated paths reach approximately 11 GiB/s for AES-256, 2.3 GiB/s for AES-256-GCM, 1.5 GiB/s for ChaCha20-Poly1305, and 2.3 GiB/s for SHA-256.
The repository also benchmarks IronCrypto against the fastest corresponding RustCrypto/dalek implementations on the same machine and buffers, using the best of nine runs:
Operation Relative performance
ECDSA P-256, sign 2.0× faster
AES-256-GCM 1.4× faster
ECDSA P-256, verify 1.4× faster
X25519 agreement 1.3× faster
AES-256 blocks, AES-NI 1.25× faster
Ed25519, sign 1.17× faster
SHA3-256 1.05× faster
AES-256 blocks, portable level
ChaCha20-Poly1305 level
SHA-256 level
HMAC-SHA256 level
SHA-512 1.1× slower
Ed25519, verify 1.25× slower
Across all 13 comparisons — including the regressions — the geometric mean is 1.143×, or about 14.3% faster overall.
Not every primitive wins, and that's intentional to show. The benchmark harness exists to identify where IronCrypto is fast, where it isn't, and where optimization work should go next. It lives outside the cryptographic workspace so comparison dependencies don't compromise the project's zero-dependency implementation.
Timing analysis without pretending it proves the impossible
IronCrypto includes a dudect-style timing analyzer:
ic timing
ic timing ct-verify --iterations 200000
It interleaves two classes of inputs and applies Welch's t-test to look for timing separation.
It also ships with a deliberately leaking positive control.
If the tool cannot detect the known leak, the rest of the run is declared meaningless.
And if it finds no leak, the result says exactly that: this run found no evidence of timing leakage on this machine.
Not "constant-time proven."
Cryptographic engineering gets worse when probabilistic evidence is turned into a green checkbox.
What IronCrypto does not claim
There are three limitations worth stating plainly.
First, IronCrypto has not received an independent cryptographic audit.
Second, IronCrypto is not CMVP validated.
Third, some intentionally excluded functionality exists elsewhere for good reasons. RSA encryption is not provided. Full X.509 certificate parsing is out of scope. Only the ML-KEM-768 and ML-DSA-65 post-quantum parameter sets are currently implemented. ARMv8 crypto acceleration exists behind an off-by-default feature until it has run on appropriate CI hardware.
That isn't a footnote.
It is part of the interface.
An agent should be able to discover the limits of a security component through the same mechanism it uses to discover its capabilities.
Try it
[dependencies]
iron-crypto = "0.1"
git clone https://github.com/nervosys/IronCrypto
cd IronCrypto
cargo test --workspace
For a bare-metal target:
cargo build -p iron-crypto --no-default-features --target thumbv7em-none-eabihf
And for an agent:
cargo install ic-cli
ic capabilities
ic ontology list
ic recommend encrypt-message
ic mcp
IronCrypto: https://github.com/nervosys/IronCrypto
AGPL-3.0-or-later + commercial licensing.
For proprietary, embedded, or closed-source SaaS use, contact licensing@nervosys.ai.
Cryptography for software that can reason
The cryptographic primitive is only part of the security system.
The rest is knowing when to use it, when not to use it, what assumptions it carries, what parameters are valid, what the failure modes mean, what the machine underneath can accelerate, what policy applies, and when the correct answer is simply no.
Historically we've encoded that knowledge for humans.
Agents change the requirement.
If autonomous systems are going to write, deploy, review, and operate security-sensitive software, cryptographic knowledge has to become queryable, machine-readable, testable, and coupled tightly enough to the implementation that the two cannot quietly drift apart.
That's what IronCrypto is for.
Fast cryptography in pure Rust — with the knowledge required to use it correctly built into the system itself.
Per aspera ad astra.
Subscribe for new dispatches
Research updates, technical deep-dives, and announcements from the frontier of embodied AI — delivered to your inbox.
Check your inbox to confirm your subscription.
