New in the IronSecurity stack: hash-based signatures, a shared PKI, and key custody
CRYPTOGRAPHY

New in the IronSecurity stack: hash-based signatures, a shared PKI, and key custody

Adam EricksonAdam EricksonOctober 12, 20266 min read
Listen to this article
0:00
--:--

Five days ago we described three Rust projects that give an agent cryptography, file privacy, and TLS it can inspect rather than guess at. Since then the work split along its natural seams, and the IronSecurity stack is now six projects.

Two of them are new because a layer was doing a job twice. Certificate validation lived in both IronSocketLayer and IronPrivacyGuard, and key custody lived in IronPrivacyGuard alone while TLS needed it too. Both are now shared, which is less code, one place for the hard parts, and one place to fix them.

Where the stack stands

Six projects, every one of them Rust over IronCrypto:

IronCrypto       0.2.24   primitives, ontology, FIPS-mode guidance
IronPKI          0.2.2    X.509 path validation and issuance
IronKeys         0.1.2    key custody: software, PKCS#11, TPM 2.0, CNG, AWS KMS
IronSocketLayer  0.5.1    TLS 1.3 and QUIC-TLS
IronPrivacyGuard 0.3.0    files, identities, trust policy
IronEvidence     early    control mappings as OSCAL

IronCrypto: the post-quantum signature set is complete

IronCrypto went from 0.2.15 to 0.2.24. The headline is that the hash-based signature standards are now implemented, which matters for anything with a long life and no easy update path — firmware, boot chains, archives.

SLH-DSA (FIPS 205) arrived in a new ic-slhdsa crate: all twelve parameter sets, SHA-2 and SHAKE, at security categories 1, 3 and 5, in both small-signature and fast-signing variants. HSS/LMS verification (RFC 8554) landed in ic-lms with RFC 9858's parameter sets, as NIST SP 800-208 approves them and CNSA 2.0 names them for signing software and firmware.

Alongside those, ic-sig gives one verify for every signature algorithm, taking a public key from a SubjectPublicKeyInfo and a signature in the encoding X.509 and TLS 1.3 already carry — which is exactly what a certificate validator needs, and why IronPKI could be lifted out cleanly. The Signer interface for keys held in an HSM, a TPM, or a key service gained a software implementation to test against, and two error kinds that such a key's holder actually needs: the device failed, and the device refused.

CNSA 2.0 is now a profile expressed as a list of algorithms rather than a strength floor, because that is what the suite is. And ic rand answers where openssl rand would, with hexadecimal or base64 output, for the scripts that already exist.

IronPKI: one certificate validator for the stack

IronPKI is IronSocketLayer 0.3.4's certificate code, moved into its own crate with its tests: parsing, path validation, name matching, CRLs, OCSP responses, and issuance. IronSocketLayer picked it up in 0.4.0 and IronPrivacyGuard in its own 0.3.0, so the two no longer disagree about what a valid path is — they cannot.

It validates for a stated Purpose, including a TPM endorsement key, within the caller's Limits, against the caller's trust anchors. Certificate policy processing (RFC 5280 section 6.1.1) came next, and then two defects in that processing were found and fixed in 0.2.1: a valid path with policy mappings could be refused as too involved to evaluate, and a chain under a trusted anchor could stretch one evaluation to 170 ms, now bounded near 15 ms. Neither accepted a path it should have refused.

IronKeys: one interface over five kinds of custody

IronKeys puts software keys, PKCS#11 tokens and HSMs, TPM 2.0, Windows CNG, and AWS KMS behind one interface, with provider descriptions and a custody policy an orchestrator can read. IronPrivacyGuard now takes its PKCS#11, CNG, TPM 2.0, and KMS crates from here instead of carrying its own, and the move changed no format, schema, or behaviour.

It is early, and the testing is worth stating precisely: the PKCS#11 provider against SoftHSM, the TPM 2.0 provider against swtpm, the KMS provider against a mock KMS on loopback and once against real AWS KMS, the CNG provider against one Windows TPM.

IronSocketLayer: certificate policy, and conformance from outside

IronSocketLayer went 0.3.2 to 0.5.1. A configuration can now say which certificate policies it accepts on a peer's path — up to 64 by object identifier, with the RFC 5280 inputs for requiring an explicit policy and inhibiting mapping or anyPolicy. A peer whose path those refuse fails the handshake with a certificate-usage error, and the session report says whether a policy was required of the peer and which of the configured policies are valid for it. The default asks nothing, so existing configurations behave as before.

The CNSA profiles were then compared clause by clause against the profiles they defer to — RFC 9151 and RFC 8603 for CNSA 1.0, the current CNSA 2.0 TLS and PKIX drafts for CNSA 2.0. The algorithm lists already agreed; the differences were in what the profiles let through, and that is what got fixed.

Conformance evidence came from outside the project again: TLS-Anvil run against both the client and the server produced a short list of real client defects, since fixed. Received application data is now wiped from the connection once read, and a trust anchor can be supplied as a name and a key rather than a certificate.

IronPrivacyGuard: fewer moving parts, and the operator's half

IronPrivacyGuard 0.3.0 is mostly subtraction. Its own X.509 parser and path validator are gone in favour of IronPKI; its own TLS client is gone in favour of IronSocketLayer, which now carries the KMS traffic; its PKCS#11, CNG, TPM, and KMS code is gone in favour of IronKeys. All 40 independent path and endorsement-key cases still agree, and its KMS endpoint and FIPS environment variables work as they did.

What was added is documentation of the kind that is usually missing: a deployment guide for regulated environments — validated key custody, the FIPS algorithm policy, the MCP host flags, audit-log operation, and, for each control IPG meets only in part, what the organization has to do and the evidence to keep.

IronEvidence: compliance claims as data

Each project now publishes its control mappings in one shared schema, and IronEvidence turns those into an OSCAL component definition that an assessor's tooling can read. Its output validates against NIST's OSCAL 1.1.2 component-definition schema.

The mappings are deliberately unflattering. IronSocketLayer claims only session authenticity as met against SP 800-53 SC-8, SC-8(1), SC-13, and SC-23; SC-13 and CMMC 3.13.11 are unmet, because IronCrypto is not CMVP validated. IronPrivacyGuard reports 7 practices met for its part, 9 partial with the gap named, and the same FIPS practice unmet. They are claims, not assessments, and they say which is which. IronEvidence reads IronCrypto's mappings today; the others follow.

The bug we shipped, and how it was found

Running Google's Wycheproof vectors against IronCrypto turned up a real one. Poly1305 computed a wrong tag, or aborted the process, on attacker-chosen messages of 64 bytes or more — every release from 0.2.5 through 0.2.19, which means ChaCha20-Poly1305 everywhere it was used, including the rustls provider and IronSocketLayer's records. It is GHSA-xr22-8pqp-gwfh, high severity, fixed in IronCrypto 0.2.20, with GHSA-vx5c-3r95-p8jq covering the downstream crash in IronSocketLayer. If you are on anything earlier, upgrade.

The same sweep added NIST ACVP vectors for TupleHash, ParallelHash, and KMAC, Wycheproof coverage for RSA signing and verification, ECDH and PBKDF2, CAVP signatures for RSA-PSS, and a pass of malformed and edge-case inputs. That is the useful lesson: the bug was ours, and the thing that found it was somebody else's test suite. We would rather run more of those than claim fewer bugs.

What is still true from last time

None of these projects has had an independent cryptographic audit, and none is a FIPS-validated module — which is why the control mappings mark the FIPS practice unmet instead of arguing. IronKeys and IronEvidence are early, and IronEvidence is not published. IronPrivacyGuard 0.3.0 is in the repository; crates.io still carries 0.2.0 as this goes out.

cargo install isl-cli --locked        # TLS and QUIC: isl
cargo install ipg --locked            # files and identities: ipg
isl capabilities
ipg discover

Everything above is AGPL-3.0-or-later with a commercial licensing option, and the repositories carry the security models, the control mappings, and the test provenance behind these claims.

Explore IronCrypto, IronPKI, IronKeys, IronSocketLayer, IronPrivacyGuard, and IronEvidence on GitHub.

Per aspera ad astra.

Intelligence Briefings

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.