Skip to main content

Module module

Module module 

Source
Expand description

The module’s error state, where every crate can see it.

A cryptographic module that finds itself wrong – a known-answer test that fails, corruption detected by its host – must stop producing output, and must not start again until it is restarted. ic_fips runs the tests and holds the rest of the module’s state, but the primitive crates cannot depend on it: it depends on them. So the one fact they all need lives here, below them, as a flag that can be set and never cleared.

Once enter_error_state has been called, every operation in this library that can report an error reports ErrorKind::ModuleErrorState and does nothing else: encryption and decryption, signing and verification, key generation and agreement, MACs, hashes and XOFs, key derivation and random generation. The operations that return bool return false.

Hashes, XOFs and MACs refuse on output – finalize, digest, xof, a reader’s read or fill, mac – so an object made before the failure produces nothing after it. Making one and feeding it input produce no output, and are not refused. Until 0.3.0 those interfaces had no error to return and could not refuse at all; they were the gate’s stated limit.

§What this does not gate

The primitives call one another through ic_core::traits::internal, which does not refuse: a signature or a KDF refuses at its own entry, and does not check again for each hash on the way to its answer. That module is hidden and not API; a caller who names it has stepped inside the boundary.

One thing that could refuse is left ungated on purpose: a block cipher object’s encrypt_block, decrypt_block and encrypt_blocks. They are the inner loop of every mode, and every mode refuses at its own entry, as does BlockCipher::new; a check per block would be paid by all of them to stop only raw single-block use of a key made before the failure.

Parsing and encoding, which use no key, are not gated either.

§Before the self-tests

In the default build nothing here requires the self-tests to have run: a primitive called before ic_fips::initialize works, and only a module that has failed refuses. ic_fips::check is the gate for that.

A build with --cfg ic_approved_only closes it. There, every operation that consults operational also refuses, with ErrorKind::ModuleErrorState, until ic_fips::initialize has run the known-answer tests and they have passed. FIPS 140-3 asks for the self-tests before a service is first used, and in that build it no longer depends on the caller asking for them.

While the tests run, the primitives they call are let through, since the tests are made of them. A second thread that calls a primitive in that window is let through too: nothing below ic_fips can tell the two threads apart without std. A program that starts threads before initialize returns should not use the library from them until it does.

This repository’s own test suites are made of primitive calls that do not go through ic_fips, and the crates that hold them cannot depend on it, so scripts/approved-only.sh runs them with --cfg ic_startup_gate_off as well, which turns this gate, and only this gate, off. It is a test switch: a build with it does not run its self-tests before use. The same script then runs the tests that show the gate refusing, without it.

§Cost

One relaxed load of one byte per operation.

Functions§

conditional_self_test
Pass on the result of a conditional self-test, entering the error state if it failed.
enter_error_state
Put the module into its error state.
in_error_state
Whether the module is in its error state.
operational
Ok(()) unless the module is in its error state – or, in a build with --cfg ic_approved_only, has not yet passed its self-tests.
self_tests_have_passed
Whether the known-answer tests have passed in this process.
self_tests_passed
Record that every known-answer test passed.
self_tests_started
Record that the known-answer tests have started.