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.