attest-enroll: enrolling a device for TPM2 remote attestation
This script, sbin/attest-enroll
implements
enrollment of a device into the attestation system using the device's
TPM's Endorsement Key's public key (EKpub
).
It takes as arguments an EKpub
or the EKpub
's public key in PEM
form, and a desired hostname
, and it creates the enrollment state for
that tuple.
Enrollment state consists of:
- the
EKpub
- the
hostname
- any configured secrets and metadata for that device, with secrets
encrypted to the
EKpub
Secrets are encrypted to the EKpub
using
sbin/tpm2-send
. More on this below.
The enrollment database is file based. The directory structure looks like:
$DBDIR/??/... # enrollment state for enrolled hosts
$DBDIR/${ekhash:0:2}/${ekhash}/ # <- enrollment state for SHA-256(EKpub)
$DBDIR/${ekhash:0:2}/${ekhash}/ # <- enrollment state for SHA-256(EKpub)
$DBDIR/hostname2ekpub/ # <- index by hostname
$DBDIR/hostname2ekpub/${hostname} # <- file containing ${hostname}'s SHA-256(EKpub)
$DBDIR/hostname2ekpub/...
Configuration is via bash scripts sourced by
sbin/attest-enroll
:
/etc/safeboot-enroll/conf # Principal config file (optional)
$DBDIR/attest-enroll.conf # Additional config file (optional)
Configuration parameters can also be given on the command-line. See the
sbin/attest-enroll
usage message for more
details.
Escrow of Enrolled Secrets
If an ESCROW_PUBS_DIR
is configured, then every secret subsequently
encrypted to any TPM is also encrypted to the defined escrow
authorities' public keys:
$ESCROW_PUBS_DIR/ # <- EKpubs/PEM of escrow agents here (optional)
$ESCROW_PUBS_DIR/someEscrowName.pub # EKpub
$ESCROW_PUBS_DIR/otherEscrowName.pem # Public key in PEM form
Enrollment State Generation
Enrollment state is generated by configured genprog
s. Two built-in
genprogs are:
genhostname
-- creates the metadata file recording the hostname,genrootfskey
-- creates a 64-byte secret key for root filesystem / volume encryption.
Other, external genprog
s can be added and configured. The following
external genprog
s are included:
gencert
-- creates a private key and a certificate for its public key naming thehostname
,genkeytab
-- creates a "keytab" with the keys for thehostname
's host service Kerberos principal.
Sites can provide additional genprog
s to generate a large variety of
credentials and metadata:
- PKIX certificates for IPsec, TLS, and/or other purposes
- OpenSSH host keys and possibly OpenSSH host key certificates
- Service access tokens
Encryption
Encryption is implemented by sbin/tpm2-send
.
Decryption is implemented by sbin/tpm2-recv
.
Two methods are possible for encryption to a target TPM's EKpub
:
- the "WK" method (our name for it)
- the "TK" method (our name for it)
Both methods support setting a policy on the ciphertext such that any application using the target's TPM to decrypt it must first execute and satisfy that policy.
The "WK" method uses TPM2_MakeCredential()
via tpm2-tools' tpm2
makecredential
command, using the none
TCTI (i.e., implemented in
software). The target's EKpub
is used as the handle
input parameter
to TPM2_MakeCredential()
. A well-known key (WK
), and the desired policy
(if any) are used to compute the cryptographic name of the "activation
object" (objectName
) input parameter to TPM2_MakeCredential()
.
Decryption consists of calling TPM2_ActivateCredential()
with the
handle to the WK
as the activationHandle
input
parameter, and the EK
as the keyHandle
input parameter of
TPM2_ActivateCredential()
. If a policy is desired then the
adminWithPolicy
attribute will be set on the WKname
, which will
cause TPM2_ActivateCredential()
to require that the policy be
satisfied.
The "TK" method uses TPM2_Duplicate()
via tpm2-tools' tpm2 duplicate
command using the none
TCTI (i.e., implemented in software. An RSA
keypair is generated and its private key is exported to the target TPM
using TPM2_Duplicate()
-- we call this the "transport key", the TK
.
The small secret will then be encrypted to the TK
's public key
(TKpub
). Decryption works by importing the exported TK
and then
using TPM2_RSA_Decrypt()
to decrypt the small secret encrypted to the
TKpub
.
Encryption of Larger Secrets
In all cases, regardless of a secret's size, we use
sbin/tpm2-send
to encrypt an ephemeral, random
AES-256 key to the target's TPM, then we encrypt the actual secret in
that AES key in confounded AES-256-CBC-HMAC-SHA-256 cipher mode. The
final ciphertext consists of those two ciphertexts: the one generated by
sbin/tpm2-send
and the one generated by AES encryption.
Encryption using the confounded AES-256-CBC-HMAC-SHA-256 cipher mode consists of:
- prepending a full cipher block of random bits (the "confounder") to the plaintext,
- encrypting the plaintext in AES-256 in cipher block chaining (CBC) mode with padding and zero IV,
- appending the HMAC of the resulting ciphertext using SHA-256 as the hash function for HMAC.
Decryption using the confounded AES-256-CBC-HMAC-SHA-256 cipher mode consists of:
- computing the HMAC-SHA-256 of the ciphertext excluding the MAC,
- checking that the HMAC of the ciphertext matches the HMAC in the ciphertext,
- decryption of the ciphertext using AES-256 in CBC with zero IV,
- removing the first full cipher block of the plaintext (the "confounder"),
- removing the padding.
The confounder serves mainly to function as a sort of explicit random IV
while allowing us to use a zero IV in the openssl enc
command
invocations. Since all of this is implemented in bash with openssl
,
and OpenSSL does not provide a decent authenticated encryption mode for
AES, we script the confounded AES-256-CBC-HMAC-SHA-256 cipher mode,
which turns out to be simple and
elegant in bash.
With OpenSSL 3.0 we could use ciphertext stealing mode (CTS) instead of CBC, and then we'd be using exactly the same more as Kerberos uses (confounded CTS with HMAC).
The CTS cipher mode is a variant of CBC mode that avoids the need for padding, in exchange for which advantage CTS requires plaintexts to be at least one full cipher block (16 bytes) long, thus CTS is always used with a confounder, and the confounder functions as an explicit IV that allows the external IV to be zero.