This repo hosts software supporting the DICE measured boot in Hubris. That mostly includes:
- generating templates used by hubris stage0 to generate X.509 / PKCS#10 structures
- tools to certify the DeviceId key that acts as the platform identity Components in this workspace are here mostly because they don't belong in the hubris repo.
Only a single script lives in to top level of the project. This script is discussed in detail below. More detailed docs are included in each subdirectory / crate as needed.
This script is used to create and configure openssl certificate authorities (CAs). This includes creating the directory structure, configuration file, keys and certs. CAs created by this script may be useful in other contexts however they are specialized to this use-case.
Like most scripts, this one relies on other tools to do the heavy lifting. These dependencies must be installed before this script will work. Naming of packages is not consistent across OSs or distros so we use names from the upstream project here:
- libp11
- openssl
- pkg-config
- yubico-piv-tool
NOTE: We assume some common tools like tar
, realpath
and some compression
libraries are installed as well. Any dependency problems encountered that
aren't listed above should be reported in an issue.
NOTE: For development we also recommend installing the yubico ykman
(yubikey
manager) tool as well. In our experimentation we've found the yubico-piv-tool
reset
action to be unreliable. ykman piv reset
however works as expected.
Often the best way to understand how a tool works is to see how it's used. This script has two main functions:
- creating a root (self-signed) certificate and CA
$ ./dice-ca-init.sh \ --archive-prefix dice-root-ca \ --dir dice-root-ca \ --self-signed
- creating an intermediate CA
$ ./dice-ca-init.sh \ --archive-prefix dice-intermediate-ca \ --dir dice-intermediate-ca \ --slot 9d
In both cases an OpenSSL CA is created in the directory provided by the --dir
option. An archive of artifacts is also created (discussed in detail below) and
named according to the --archive-prefix
option. The openssl.cnf
created in
each CA dir is configured with defaults specific to their purpose:
- root CAs have defaults for certifying intermediate CAs
- intermediate CAs have defaults for certifying DICE DeviceId certs
When creating a root / self-signed cert in the first example the x.509 /
RFC5280 certificate created is self signed. When creating an intermediate we
instead create a PKCS#10 / RFD2986 certificate signing request (CSR). Before
the intermediate CA can be used however the CSR must be evaluated and a cert
generated by the parent CA. This can be done using an additional tool from the
dice-mfg
crate:
$ cargo run --bin dice-mfg -- \
sign-cert \
--openssl-cnf dice-root-ca/openssl.cnf \
--csr-in dice-intermediate-ca/csr/ca.csr.pem \
--cert-out dice-intermediate-ca/certs/ca.cert.pem
NOTE: Before signing a CSR, the CA should ensure the key holder meets some set of requirements. See key attestation below.
This script generates an "opinionated" openssl config files. The root directory
for the CA files is included as an absolute path. Using relative paths makes
the config more flexible but requires that invocations of the openssl ca
command be done from the root of the CA directory.
NOTE: The down side of this approach is that if the CA directory is moved the
openssl.cnf
will need to have the dir
entry in the CA config section
updated accordingly.
Similarly we set defaults in openssl.cnf
to prevent the need to provide any
additional data to openssl on the command line. This makes each config special
purpose and currently prevents us from creating multiple intermediate CAs for
our PKI hierarchy.
In the examples above encryption keys for the CA are created on a yubikey. This
is the default. We assume a key is present when the script is run. The default
slot used is 9a
and our example of a root CA uses this default. The second
example of the intermediate CA explicitly selects slot 9d
.
NOTE: The PIV spec (FIPS 201-3) defines specific requirements / uses for each
key slot. Slot 9c
is problematic for our use-case as it requires personal
identification number (PIN) entry for each operation. We can provide the PIN in
the openssl.cnf file but openssl will ignore this for operations on slot 9c
to force PIN entry.
Yubikeys are appealing as a way to prevent key exfiltration / duplication. By creating our CA keys in yubikeys we prevent simple key exfiltration as a yubikey is specifically designed to prevent key disclosure. Convincing ourselves that these keys were truly generated on a yubikey requires a significant amount of trust and communication between the parties involved. In a small enough group this may be feasible but convincing a 3rd party of this fact requires additional work.
Yubikeys implement an attestation mechanism intended to solve this problem. Key attestation works by yubico preloading every yubikey with an attestation signing key and a certificate signed by their attestation CA (both are in PIV slot f9). When an encryption key is created in the yubikey (not imported) the yubikey will generate a certificate attesting to the fact that it was created on the yubikey among other things. Assuming that the parties involved trust yubico and their devices these certificates can be used to prove that a given key was created on a legitimate yubikey.
When generating the keys for our CAs, dice-ca-init.sh
collects the artifacts
necessary to carry out this process. Additionally we include a script in this
archive to validate the attestation cert chain. Before certifying an
intermediate CA, the root CA should evaluate the attestation associated with
the request.