diff --git a/Attestation/README.md b/Attestation/README.md index 740f043..4a4d08b 100644 --- a/Attestation/README.md +++ b/Attestation/README.md @@ -780,54 +780,103 @@ for secret transport. This list is almost certainly not exhaustive. [`TPM2_MakeCredential()`](/TPM-Commands/TPM2_MakeCredential.md) and [`TPM2_ActivateCredential()`](/TPM-Commands/TPM2_ActivateCredential.md) -can use any kind of loaded object with a private area as the "credential -object name" and "credential object handle" arguments of the two calls, -respectively. During attestation we need to use an AK for this object -due to the need to have a signature key for -[`TPM2_Quote()`](/TPM-Commands/TPM2_Quote.md), and we want that AK to -have `stClear` set, meaning that it is ephemeral, rendering the outputs -of `TPM2_MakeCredential(EKpub, AKname, secrets)` ephemeral as well, -therefore `TPM2_MakeCredential(EKpub, AKname, secrets)` must be called -on each attestation, which means the `secrets` also have to be ephemeral -or else must be stored in cleartext on the attestation server. +are a form of limited asymmetric encryption (`TPM2_MakeCredential()`) +and asymmetric decryption (`TPM2_ActivateCredential()`) subject to the +sender's choice of authorization. The details are explained +[here](/TPM-Commands/TPM2_MakeCredential.md) and +[here](/TPM-Commands/TPM2_ActivateCredential.md). Basically, there are +two TPM key objects involved: -However, we can use a key that does not have `stClear` set as the -credential object. A long-term key that survives reboots. + - a transport key (typically the `EK`), + - and an authorization key (typically an `AK`) -> We'd like to use the EK itself, however, -> [that is not actually possible](https://github.com/tpm2-software/tpm2-tools/issues/1883) -> because the `activateHandle` argument to -> [`TPM2_ActivateCredential()`](/TPM-Commands/TPM2_ActivateCredential.md) -> requires `ADMIN` role, and on most TPMs the auth policy for the EK -> does not provide any way to satisfy it for this usage. Though in -> principle this should be possible, and it would be very convenient if -> it was. +and the caller of `TPM2_MakeCredential()` must specify the public part +of the transport key and the +[name](/Intro/README.md#Cryptographic-Object-Naming) of the +authorization key, along with a small secret to transport. The caller +of `TPM2_ActivateCredential()` must then provide the handles for those +two key objects and the outputs of `TPM2_MakeCredential()` in order to +extract the small secret. Typically the small secret is an AES key for +encrypting larger secrets. -So for this approach one has to create a long-term attestation key that -we shall call the `LTAK`, and then the server can store -`TPM2_MakeCredential(EKpub, LTAKname, secrets)` without knowing the -secrets. +So if we can store the outputs of `TPM2_MakeCredential()` long-term so +that the client can activate over multiple reboots, then we have a way +to deliver secrets to the client. -The client has to use -[`TPM2_CreatePrimary()`](/TPM-Commands/TPM2_CreatePrimary.md) or -[`TPM2_CreateLoaded()`](/TPM-Commands/TPM2_CreateLoaded.md) in order to -deterministically create the same `LTAK` (again, without the `stClear` -attribute), else if it uses -[`TPM2_Create()`](/TPM-Commands/TPM2_Create.md) then it must store the -key save file somewhere (possibly in the attestation server!) or make -the key object persistent. +We'll discuss two ways to do this: + + - use a `WK` -- a universally well-known key (thus WK, for well-known) + + Since the `WK`'s private area is not used for any cryptography in + `TPM2_MakeCredential()`/`TPM2_ActivateCredential()`, it can be a key + that everyone knows. + + Note that the `WK`'s public area can name arbitrary an auth policy, + and `TPM2_MakeCredential()` will enforce it. + + E.g., the `WK` could be the all-zeros AES key. Its policy could be + whatever is appropriate for the organization. For example, the + policy could require that some non-resettable application PCR have + the value zero so that extending it can disable use of + `TPM2_MakeCredential()` post-boot. + + - use an `LTAK` -- a long-term `AK` + + I.e., an `AK` that lacks the `stClear` attribute, and _preferably_ + created deterministically with either + [`TPM2_CreateLoaded()`](/TPM-Commands/TPM2_CreateLoaded.md) or + [`TPM2_CreatePrimary()`](/TPM-Commands/TPM2_CreatePrimary.md). + + > Note that the `LTAK` need not be a primary. + + > If the `LTAK` were created with + > [`TPM2_Create()`](/TPM-Commands/TPM2_Create.md) then the key's saved + > context file would have to be stored somewhere so that it could be + > loaded again on next boot with + > [`TPM2_Load()`](/TPM-Commands/TPM2_Load.md). Whereas creating it + > deterministically means that it can be re-created every time it's + > needed using the same hiercarchy, template, and entropy as + > arguments to `TPM2_CreatePrimary()` or `TPM2_CreateLoaded()` + + Note that the `AK`'s public area can name arbitrary an auth policy, + and `TPM2_MakeCredential()` will enforce it. + +The best option here is to use a `WK` because using an `LTAK` would +require recording its public key in the device's enrolled attestation +state, which would complicate enrollment, whereas the `WK`, being +well-known and the same for all cases, would not need to be recorded in +server-side attestation state. + +> One might like to use the `EK` as the `activateHandle`. Sadly, this +> is not possible. +> While `TPM2_MakeCredential(EKpub, EKname, input)` works, +> `TPM2_ActivateCredential(EK, EK, credentialBlob, secret)` does not +> and cannot. +> +> The reason for this is that `TPM2_ActivateCredential()` requires +> `ADMIN` role for the `activateHandle`, and since the `EK` has +> `adminWithPolicy` attribute set and its policy doesn't have the +> `TPM_CC_ACTIVATECREDENTIAL` command permitted, the call must fail. +> +> Credit for the `WK` idea goes to [Erik > Larsson](https://developers.tpm.dev/chats/new?user_id=4336638). + +Normally during attestation we want to use an `AK` with `stClear` set so +that each boot forces the client to use a new one. However, for sending +secrets to the client via `TPM2_MakeCredential()` / +`TPM2_ActivateCredential()` we really need need the `activateHandle` +object to not have `stClear` set. + +For this approach then, the best solution is to use a `WK`. ``` - - CS0: timestamp, AKpub, PCRs, eventlog, TPM2_Quote(AK, PCRs, extra_data)=Signed_AK({hash-of-PCRs, misc, extra_data}) SC0: {TPM2_MakeCredential(EKpub, AKname, session_key), Encrypt_session_key(long_term_Credential)} + + where + + long_term_Credential = TPM2_MakeCredential(EKpub, WKname, secrets) ``` New secrets can be added at any time without interaction with the @@ -846,12 +895,10 @@ The schema for storing secrets transported this way would be: "current_operating_system_profiles": ["OSProfile1", "OSProfile2", "..."], "previous_PCRs": "<...>", "proposed_PCRs": "<...>", - "ak_cert_template": "", "resetCount": "", "secret store and transport fields":"vvvvvvvvvvvvvvvvvv", - "LTAKname": "<...>", "secrets": ["", "", "..", ""] "secrets_backup": ["