Today, Clevis supports "static" PCR Policies through the TPM Pin. This makes using the TPM much more easier for regular users, replacing all the TPM2 Tools command with a single instruction for encryption and decryption. Unfortunately, by using this strategy, a future update of binaries that affects the values of the PCR banks selected in the policy can cause the inability to unseal the secrets previously sealed to those PCRs. This is called PCR fragility (or PCR brittleness). Fragility of PCR values was one of the most annoying problems with the 1.0 family of TPMs. Both keys and data can be locked to certain PCRs having particular values. But if keys or data are locked to a PCR that represents the BIOS of a system, it's tricky to upgrade the BIOS. Typically, before a BIOS upgrade was performed on TPM 1.2 systems, all secrets locked to PCR 0, for example (which represents the BIOS), had to be unsealed and then resealed after the upgrade was done. This is both a manageability nightmare and a nuisance to users. In the TPM 2.0 specification, you can seal things to a PCR value approved by a particular signer instead of to a particular PCR value (although this can still be done if you wish). That is, you can have the TPM release a secret only if PCRs are in a state approved (via a digital signature) by a particular authority. With the TPM2 Tools 4.0 release, Authorized Policies support is provided thorough the tpm2_policyauthorize command. This allows to setup a policy that can be updated in the future to react to system updates that affects the PCR. The process to use tpm2_policyauthorize is a little bit complex, but I think Clevis can replace that complexity as it already did with tpm2_policypcr. The way to use Authorized Policies today is specified below in that long script. The script was tested against the following versions TPM2_SIMULATOR_VERSION=1332 TPM2_TSS_VERSION=2.3.1 TPM2_ABRMD_VERSION=2.2.0 TPM2_TOOLS_VERSION=4.0 NOTE: This is a test similar to the abrmd_policyauthorize.sh test in the tpm2-software/tpm2-tools repo (thank you @idesai for your help). It uses a simulator, and can be executed in a Docker container. It does not uses the resource manager, and that is why there are several tpm2_flushcontext --transient commands issued. The resource manager aware version would be a little shorter. # Simulator Setup tpm_server -rm > /dev/null 2>&1 & TPM2_SERVER_PID=$! sleep 1 export TPM2TOOLS_TCTI=mssim tpm2_startup --clear # Create a PCR policy tpm2_startauthsession --session session.ctx tpm2_policypcr \ --session session.ctx \ --pcr-list sha256:0,1 \ --policy pcr.policy tpm2_flushcontext session.ctx rm -f session.ctx # Generate public/private key pair for signing openssl genrsa -out signing.priv.pem openssl rsa \ -in signing.priv.pem \ -out signing.pub.pem \ -pubout # Loading the public key in TPM tpm2_loadexternal \ --key-algorithm rsa \ --hierarchy o \ --public signing.pub.pem \ --key-context signing.key.ctx \ --name signing.key.name # Flush transient objects tpm2_flushcontext --transient # Create an authorized policy tpm2_startauthsession --session session.ctx tpm2_policyauthorize \ --session session.ctx \ --policy authorized.policy \ --name signing.key.name \ --input pcr.policy tpm2_flushcontext session.ctx rm -f session.ctx signing.key.ctx # Create the TPM object that holds the secret echo "Secret" > secret.txt tpm2_createprimary \ --hierarchy o \ --hash-algorithm sha256 \ --key-algorithm rsa \ --key-context primary.ctx tpm2_create \ --parent-context primary.ctx \ --hash-algorithm sha256 \ --public key.obj.pub \ --private key.obj.priv \ --sealing-input secret.txt \ --policy authorized.policy # Flush transient objects tpm2_flushcontext --transient # Load the generated object in the TPM tpm2_load \ --parent-context primary.ctx \ --public key.obj.pub \ --private key.obj.priv \ --name key.obj.name \ --key-context key.obj.ctx # Persist the TPM Object tpm2_evictcontrol \ --hierarchy o \ --object-context key.obj.ctx \ 0x81010001 # Flush transient objects tpm2_flushcontext --transient # Sign the PCR policy openssl dgst \ -sign signing.priv.pem \ -out pcr.policy.signature \ pcr.policy # Load the keys to be used in signature verification tpm2_loadexternal \ --key-algorithm rsa \ --hierarchy o \ --public signing.pub.pem \ --key-context signing.key.ctx \ --name signing.key.name # Verify the signature tpm2_verifysignature \ --ticket verification.tkt \ --key-context signing.key.ctx \ --hash-algorithm sha256 \ --message pcr.policy \ --signature pcr.policy.signature \ --format rsassa # Flush transient objects tpm2_flushcontext --transient # Unseal the TPM secret using the Authorized PCR Policy tpm2_startauthsession \ --policy-session \ --session session.ctx tpm2_policypcr \ --session session.ctx \ --pcr-list sha256:0,1 tpm2_policyauthorize \ --session session.ctx \ --policy authorized.policy \ --input pcr.policy \ --name signing.key.name \ --ticket verification.tkt tpm2_unseal \ --object-context 0x81010001 \ --auth session:session.ctx tpm2_flushcontext session.ctx rm -f session.ctx A negative test of this setup is shown below. In this case, the PCR 0 is updated, and the secret cannot be unsealed again. This will be the current behavior with the "static" PCR policies. # Negative Test: after pcr extend unsealing should fail tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000 tpm2_startauthsession \ --policy-session \ --session session.ctx tpm2_policypcr \ --session session.ctx \ --pcr-list sha256:0,1 tpm2_policyauthorize \ --session session.ctx \ --policy authorized.policy \ --input pcr.policy \ --name signing.key.name \ --ticket verification.tkt || echo 'Failed' tpm2_flushcontext session.ctx rm -f session.ctx # Flush transient objects tpm2_flushcontext --transient And here, the update scenario, where a new valid signed policy is provided for unsealing: # Update Test: Providing a new policy with updated values should work tpm2_startauthsession --session session.ctx tpm2_policypcr \ --session session.ctx \ --pcr-list sha256:0,1 \ --policy newpcr.policy tpm2_flushcontext session.ctx rm -f session.ctx # Update Test: Sign new policy with updated values openssl dgst \ -sign signing.priv.pem \ -out newpcr.policy.signature \ newpcr.policy # Update Test: Get new verification ticket for the policy tpm2_verifysignature \ --ticket newverification.tkt \ --key-context signing.key.ctx \ --hash-algorithm sha256 \ --message newpcr.policy \ --signature newpcr.policy.signature \ --format rsassa # Update Test: Unseal with updated policy tpm2_startauthsession \ --policy-session \ --session session.ctx tpm2_policypcr \ --session session.ctx \ --pcr-list sha256:0,1 tpm2_policyauthorize \ --session session.ctx \ --policy authorized.policy \ --input newpcr.policy \ --name signing.key.name \ --ticket newverification.tkt tpm2_unseal \ --object-context 0x81010001 \ --auth session:session.ctx tpm2_flushcontext session.ctx rm -f session.ctx Finally, some cleanup and shutdown instructions: # Removing persistent handles tpm2_getcap handles-persistent tpm2_evictcontrol \ --hierarchy o \ --object-context 0x81010001 # Simulator Shutdown kill ${TPM2_SERVER_PID} All this could be replaced with something similar to this in clevis: # Encrypt the key with Clevis cat key.txt | clevis encrypt \ tpm2 '{"pcr_bank":"sha256","pcr_ids":"0", "authorized_policy": true, "signing_auth": "public.key"}' > key.jwe # Decrypt the rsa key with Clevis, should work if PCRs are in expected state clevis decrypt < key.jwe > key.txt # Negative test # Extend PCR 0 and attempt to unseal, should fail tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000 clevis decrypt < key.jwe || echo 'Expected to Fail' # Update test # Provide a new authorized policy contemplating the update, should work clevis decrypt tpm2 '{"policy": "authorized.policy" }' < key.jwe # Update test # Policy should be updated, and secret should be able to unseal clevis decrypt < key.jwe > key.txt Is this something you could implement? Thank you!
TPM2 Tools 4.0 is now available in F31 and F32: http://rpmfind.net/linux/RPM/fedora/devel/rawhide/x86_64/t/tpm2-tools-4.0.1-1.fc32.x86_64.html