main-with-ancient-kernel
10
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1c5783b112 |
report: Support signing reports with multiple TPM2 keys
In preparation for adding a varlink interface for key management, add support for signing reports with multiple TPM2 keys. During signing, this iterates over all *.key files in /var/lib/systemd/report.sign.tpm2, generating a TPM signature with each one. It also looks for the corresponding *.voucher file, containing some credential that is used to prove that the signing key is authentic, and which is added to the signature if it exists. If no signing keys exist when a signature is requested, a default signing key is still created as default.key. |
||
|
|
efe5e4d271 |
report: Add the session audit digest algorithm to the TPM2 signature
When generating a session audit digest, the algorithm isn't included in the attestation. The algorithm is the auth digest algorithm that the TPM session is created with, which is currently hardcoded as SHA-256. This might not always be the case though, and verifiers shouldn't have to guess what the digest algorithm is, so include the algorithm in the report signature component. |
||
|
|
fc5875d137 |
report: Don't create TPM2 attestation key with adminWithPolicy
The default attestation key for the TPM2 report signer is currently
created with an empty policy but with the adminWithPolicy attribute set.
This makes it impossible to authorize use of the key for the ADMIN role,
which is required by TPM2_Certify.
This drops the adminWithPolicy attribute so that it can be authorized
using its (empty) authorization value. This is appropriate for local
style attestation keys ("LAKs"). I've been working on an interface for
managing attestation keys which will support creating an IAK, and this
will have both the adminWithPolicy attribute set and a policy defined as
specified in the TCG "TPM 2.0 Keys for Device Identity and Attestation"
spec.
|
||
|
|
7ecc970249 |
tpm2: add missing dlopen_libcrypto
tpm2_tpmt_signature_to_pem uses openssl functions directly, so it needs to ensure that it is dlopened. |
||
|
|
768b6f21ec |
report: Add a TPM2 signing backend
This adds an initial TPM2 implementation of the io.systemd.Report.Signer interface, which returns a set of signed TPM attestations that also includes the supplied report digest. The returned report consists of multiple components: - A signed PCR quote generated with TPM2_Quote. - Signed NvPCR attestations generated with TPM2_NV_Certify. - A signed audit session attestation generated with TPM2_GetSessionAuditDigest. Each signed report component includes the JSON encoded TPMS_ATTEST attestation structure and corresponding signature, in both PEM encoded form and as a JSON encoded TPMT_SIGNATURE structure. NvPCR report components also include the readable name of the NvPCR, the NV index public area as the JSON encoded TPMS_NV_PUBLIC structure, and some additional authenticated data that is digested and supplied as the qualifying data to the TPM2_NV_Certify command. This authenticated data includes the NvPCR name and priority. The report digest that is supplied via varlink is supplied as the qualifying data to the audit session attestation. The returned report also includes the event log which is retrieved from pcrlock's varlink interface, and the public area of the signing key in both PEM encoded form and as a JSON encoded TPMT_PUBLIC structure. The audit session attestation is always the last component. The preceding components are all executed with the audit session, and the signer requires the audit session to remain exclusive whilst generating the report. What this does is provide evidence to a verifier that the overall report is a single, consistent snapshot of the system (ie, no other TPM commands executed whilst generating the report) because the audit session attestation contains a field that indicates that the session was exclusive at the time it was attested. The set of attested PCRs and NvPCRs are currently hardcoded. It includes all firmware PCRs with the exception of PCR6, and all other PCRs with the exception of the IMA one (which isn't useful on its own without the IMA log). All defined NvPCRs are included. A verifier can verify the signed attestation components individually by verifying their signatures against the supplied attestation data, using the supplied public key. For the PCR report component, a verifier can replay the measurements in the log in order to reconstruct the PCR values, and then use this and the PCR selection from the attestation in order to calculate the PCR digest, and then check that this matches the one in the attestation. For the NvPCR report components, a verifier checks that the NV index name in the attestation matches one that is anchored in PCR9 to be sure that the attestation is for a valid NvPCR. A verifier can then replay the measurements in the log to ensure that the resulting NvPCR value matches the one supplied in the attestation. The authenticated data is a JSON blob containing the NvPCR name and priority. A verifier can digest this and ensure it matches the qualifying data in the attestation. The audit session attestation includes the audit session digest and whether the audit session was still exclusive when the attestation was generated. A verifier should confirm exclusivity. The digest includes a record of all TPM commands executed with the audit session, as each command results in it being extended with the command parameter hash and the result parameter hash. A verifier should replay the preceding report components to compute an expected audit digest and make sure that this digest matches the one in the attestation in order to be sure that the overall report represents a single, consistent snapshot. The other report components provide sufficient information for a verifier to reconstruct the cpHash and rpHash. When reconstructing the cpHash for the PCR and NvPCR components, the signing scheme is the NULL scheme (they use the attestation key's signing scheme). The required signing key public area is supplied in the report. The supplied qualifying data is in the extra data field of the attestation. The PCR selection is also required to reconstruct the cpHash for the PCR component. The PCR selection is available in the attestation. The name of the NV index is also required to reconstruct the cpHash for the NvPCR components. The name is available in the attestation. The rpHash for each component can be reconstructed from the supplied attestation blob and signature. In the future, we might want to add more report component types. Eg, we could add report components for ordinary NV indexes. Using the audit session means that we could also add additional components that the TPM can't attest directly, such as the values of interesting TPM properties (as the TPM2_GetCapability calls will appear in the audit session digest). The attestation signing key is stored as a JSON file in the report signer's state directory. Different types of keys are supported: - Ordinary keys that are protected by an existing persistent TPM key such as the SRK or EK. - Persistent TPM keys. - Primary keys, which are recreated using a template on use. When an ordinary or primary key is used, the report signer saves the object context in its runtime directory. This context is used on future invocations. This has the benefit that where a primary key is used, it doesn't need to be recreated on each use. When an ordinary key is used, we don't need to run TPM2_Load, which requires authorization with the parent key, on each This authorization currently requires a policy session for low-range EKs. Regardless of the key type, it must be a restricted signing key. If no key exists, an ordinary key is generated automatically. The attestation key can also have an associated credential which is attached to the signed report. The credential can be issued by a CA or some other party during onboarding, and is presented to a verifier as proof that the attestation key is authentic. It is considered an opaque blob in order to not require it to be a specific format, such as a X.509 certificate. I've called this a "voucher" though because "cred" or "credential" has other meanings in systemd. As a follow-up to this, I plan to add a varlink interface for creating attestation keys that can be used for report signing. |
||
|
|
f0f3687700 |
tpm2: Improve how NvPCR protection works.
NV indexes created in the storage hierarchy can be undefined and
redefined with TPM owner auth. Because of this, NvPCRs need some way
to prevent them from being redfined in a way that allows spoof
measurements to be replayed.
The current approach requires knowledge of a secret ("anchor secret")
in order to derive the initial NvPCR measurement and to derive a
measurement to an existing PCR (9). The credential is protected by the
TPM with a PCR policy. Without access to the credential, it's not
possible to replay measurements to a newly defined NvPCR without
breaking the binding with the measurement in PCR 9. However, this
approach has a couple of issues:
- The credential is currently only protected by PCR11. As it's not
protected by the rest of the boot chain, it's possible to boot other
operating systems in order to replay the PCR11 measurements and
recover the secret. Note that as the NvPCR anchoring happens in early
boot, the credential is stored in the ESP.
- Someone with privileged access to a system can just create a new
credential containing a known secret and store this in /var/lib and
the ESP. The NvPCRs are anchored with this known secret on subsequent
boots, and therefore the measurements can no longer be trusted.
Imagine the scenario where privileged access is theoretically possible
as a result of some vulnerability. After upgrading the system to fix
this vulnerability, the system should be able to attest that it is
now in a good state. However, if an adversary were able to use their
priviliges to replace the credential, they are able to obtain
persistence and the NvPCR measurements are no longer trustworthy.
This PR changes things to take a different approach. Instead of
requiring knowledge of a secret, the NvPCRs are now created in a way
that requires a policy to be satisfied for writing. The write policy has
2 branches:
- TPM2_PolicyNvWritten(true), which can be satisified without any
further authorization if the NvPCR has already been extended.
- TPM2_PolicyAuthorize(pcrPubKey, SHA256("nvpcr-init")) which can be
satisfied with a signed PCR policy, and must be used to perform the
initial extend to a NvPCR.
The intention here is that the signed PCR policy that can be used to
authorize the initial extend to the NvPCR can only be satisfied during
early boot. During later boot phases, this signed PCR policy must not be
valid. This means that if a NvPCR is undefined and redefined, it won't
be possible to satisfy its write policy in order to able to perform the
initial extend.
In order to anchor the NvPCRs and prevent them from being undefined and
then redefined with a different policy that does allow them to be
extended, the names of the NvPCRs are measured to PCR9. Verifiers must
check that the names of attested NvPCRs match the measurements in PCR9.
This uses the PCR signing key from the currently booted UKI to create
the NvPCRs. If this changes between boots, then tpm2-setup automatically
recreates new NvPCRs with an updated write policy to reflect this. I've
tried to be careful to not undefine arbitrary NV indexes in this case,
so it checks that the existing NV index looks like a NvPCR (ie, it has
the expected attributes) before undefining it.
I did originally try to preserve the old behaviour for existing systems,
but it makes things a lot more complicated. As the new implementation
already creates new NvPCRs when the PCR signing key changes, I ended up
just automatically upgrading the old NvPCRs as well. Again, I check here
that any existing NV index looks like an old style NvPCR (ie, it has the
expected attributes) before undefining it.
I did notice that the initial NvPCR measurement isn't going into the
log. I don't know if that was an intentional choice, but I've preserved
that behaviour in this PR.
This also adds a new option to ukify (--sign-initrd-pcrs) which creates
signed policies (one per PCR bank) that can only be satisfied from the
initrd. These policies are used for initializing the NvPCRs, but can also
be used for protecting TPM2 keyslots enrolled with systemd-cryptenroll
(by using the --tpm2-public-key-policyref=initrd option).
There is one outstanding issue. The NvPCR definitions support different
algorithms, but the use of PolicyAuthorize means that they can only support
SHA-256 for now. This is because the signed policy algorithm must match
the name algorithm, and some additional work is required to support
signed PCR policies for algorithms other than SHA256. I've left a note in
tpm2_nvpcr_initialize that details what's required, and I'll take a look
at that in a subsequent PR.
|
||
|
|
2348941b5d |
measure: Support binding signed policies to individual phases
systemd-measure can produce multiple signed policies for different phases. However, a policy for a TPM resource that includes these signed policies can currently be satisfied by any policy that is signed with the same key. It can be desirable to bind a resource's policy to one or more specific phases. One way to do this could be to sign policies for different phases or phase combinations with different keys. Another approach is to limit the scope of signed policies using a policy reference. Using a policy reference works because: - The reference is included along with the approved policy digest in the digest that is signed. - The reference argument is included in the authorization policy for a resource via the TPM2_PolicyAuthorize assertion. - During execution of the TPM2_PolicyAuthorize assertion, the TPM checks that the session's current policy digest is the approved policy digest, computes a digest from the approved policy digest and the supplied policy reference, and checks that the resulting digest is the one that was verified by TPM2_VerifySignature (via the returned ticket). This adds a new --policyref argument to systemd-measure which binds all of the signed policies to the specified policy reference. I did consider making this more intelligent by auto-generating policy references for each phase, but this approach provides the most flexibility for now. By making use of the existing --append argument, a signer can produce multiple signed policies with the same key that are bound to any individual phases or combinations of phases. The policy reference is a string without the NULL terminator. It is supplied to the TPM via the TPM2B_NONCE type, which has a maximum size equivalent to the size of the largest digest supported by the TPM. As the signer doesn't know the capabilities of the target TPM, systemd-measure limits the size of the policy reference to 32 bytes, to fit within the size of a SHA256 digest. This also includes the corresponding changes to systemd-cryptenroll and systemd-repart (to add an equivalent --tpm2-public-key-policyref argument so that the policy can be bound to the desired corresponding phase, and to ensure that the policy reference is included in the LUKS2 token metadata), and systemd-cryptsetup (to handle the policy reference stored in the LUKS2 token metadata). This doesn't include policy reference support for credentials yet because it requires a change to the credential headers. |
||
|
|
5a80137aa4 |
tpm2-setup: Create and persist an endorsement key
This updates systemd-tpm2-setup to create and persist an endorsement key if there isn't one already. For each supported EK template profile, it will read the EK certificate from its NV index if there is one. When there is an EK certificate present, a primary key is created using the corresponding template. If the resulting EKpub matches the public key in the certificate, the created EK is persisted and the process is complete. The low-range templates and the high-range RSA 2048/3072 and ECC NIST P256/P384 storage templates are supported, as detailed in section 5.3 of the "TCG EK Credential Profile For TPM Family 2.0" spec v2.7. High-range templates are preferred because these permit EK usage without requiring knowledge of the authorization value for the endorsement hierarchy, meaning that, like with the SRK, it is possible to restrict the usage of the endorsement hierarchy whilst still permitting use of the persistent EK. The EK is always persisted at handle 0x81010001. This handle is reserved in Table 2 of the "TCG TPM v2.0 Provisioning Guidance" spec v1.0r1.0, although this is only a recommendation. This handle is within the block of handles reserved for endorsement primary keys in the "Registry of Reserved TPM 2.0 Handles and Localities" spec v1.2r1.00. Section 2.3.2 of this specification also makes a suggestion that there should be a relationship between the EK certificate NV index and a corresponding persistent EK handle by using handles at the same offsets within their respective ranges. However, this contradicts the provisioning guidance spec which reserves 0x81010001 when there isn't a certificate at 0x01c00001. For simplicity, I've chosen to use a single handle for the EK regardless of which profile it is created with. The "TCG EK Credential Profile For TPM Family 2.0" spec also provides a way for endorsement keys to be certified with non-standard templates by storing the template in a NV index. This is also supported. The EK creation is not executed with tpm2-setup --early, as there's no need for it to be created so early, unlike with the SRK. I also haven't stored EKpub in /var/lib/systemd like with the SRKpub, as I'm not sure there will be a use case for this yet. A follow-up PR may be needed to add some internal helpers to make use of the persisted EK, as use of low-range EKs requires a policy session. High-range EKs can be used with a HMAC session because they have the userWithAuth attribute set and we are creating them with an empty authorization value. https://trustedcomputinggroup.org/resource/http-trustedcomputinggroup-org-wp-content-uploads-tcg-ek-credential-profile/ https://trustedcomputinggroup.org/resource/tcg-tpm-v2-0-provisioning-guidance/ https://trustedcomputinggroup.org/resource/registry/ |
||
|
|
93e9c2c974 |
shared/tpm2: support chunked reads of NV indexes
The TPM2_NV_Read commands returns the requested data in a TPM2B_MAX_NV_BUFFER type, the maximum size of which is TPM-specific and can be determined by querying the value of the TPM_PT_NV_BUFFER_MAX property. The value of this may be smaller than the payload size of some NV indexes, particularly when that payload is a X509 certificate with a RSA public key. Eg, the manufacturer supplied RSA EK certificate on my own machine has a size of 1035 bytes, and the value of TPM_PT_NV_BUFFER_MAX is 1024. To handle this case and make it possible to read any EK certificate from the TPM, make tpm2_read_nv_index support chunked reads when the payload size is larger than what the TPM can return in a single command. |
||
|
|
5e7f94a23f |
shared/tpm2: split the tpm2_index_to_handle helper function
The existing tpm2_index_to_handle helper has optional return arguments for an object's public area and its qualified name. However, it can also be called for handles corresponding to NV indexes, where the public area and qualified name don't apply. I'm working on another change that would benefit from having an equivalent helper for NV indexes, and there is already some code in shared/tpm2-util.c that could use this as well. This splits the existing tpm2_index_to_handle function into 3 functions: - tpm2_index_to_handle, which works for both objects and NV indexes and returns a name and handle. - tpm2_object_index_to_handle, which works the same as the existing function for transient or persistent objects, returning a public area, name, qualified name and handle. - tpm2_nv_index_to_handle, which works for NV indexes, returning the NV public area, name and handle. |