Plugin provides the ability to use a Trusted Platform Module (TPM) to seal and unseal keys
The sealed data can only get recovered on the same TPM it was sealed against.
This library also supports sealing data and binding it to a certain passphrase or TPM PCRs values
For an example of standalone usage, see the example below.
You can override the configuration values provided in the file by using the following env-var overrides
| Option | Description |
|---|---|
TPM_PATH |
Path to the TPM device (default: /dev/tpmrm0) |
TPM_PCRVALUES |
Comma separated PCR banks to bind the key to. See Policy Constrains section (default: ``) |
TPM_USERAUTH |
Password to bind the key to. See Policy Constrains section (default: ``) |
TPM_HIERARCHYAUTH |
Password of the TPM hierarchy (default: "") |
TPM_KEY |
(optional) Seal/unseal will load this AES PEM formatted key instead of creating one (default: "") |
You can specify the same values in the config file variables:
| Option | Description |
|---|---|
tpm_path |
Path to the TPM device (default: /dev/tpmrm0) |
pcr_values |
Comma separated PCR banks to bind the key to. See Policy Constrains section (default: ``) |
user_auth |
Password to bind the key to. See Policy Constrains section (default: ``) |
hierarchy_auth |
Password of the TPM hierarchy (default: "") |
key |
(optional) Seal/unseal will load this AES PEM formatted key instead of creating one (default: "") |
for example,
seal "tpm" {
tpm_path = "/dev/tpmrm0"
tpm_pcrvalues = "23:0000000000000000000000000000000000000000000000000000000000000000"
tpm_userauth = "foo"
}
TPM wrapper support the following constraints set while sealing which must be fulfilled while unsealing
The userauth constraints basically a passphrase which must be provided when sealing the data and the same passphrase for unsealing.
export TPM_USERAUTH=fooor
seal "tpm" {
tpm_path = "/dev/tpmrm0"
user_auth=foo
}
For PCR binding, specify the PCR index and sha256 hex value to bind to.
Each pcr bank must comma separated and formatted as int(index):hex(sha256(pcr_value)).
For example, to bind to pcrs 15, 23 which has the following values,
$ tpm2_pcrread sha256:15,23
sha256:
15: 0x0000000000000000000000000000000000000000000000000000000000000000
23: 0xF5A5FD42D16A20302798EF6ED309979B43003D2320D9F0E8EA9831A92759FB4Bthe string format for to specify would be the following. Note the index value should be ascending and the 0x from tpm2_pcrread format is omitted.
export TPM_PCRVALUES=15:0000000000000000000000000000000000000000000000000000000000000000,23:F5A5FD42D16A20302798EF6ED309979B43003D2320D9F0E8EA9831A92759FB4Bor
seal "tpm" {
tpm_path = "/dev/tpmrm0"
pcr_values=15:0000000000000000000000000000000000000000000000000000000000000000,23:F5A5FD42D16A20302798EF6ED309979B43003D2320D9F0E8EA9831A92759FB4B
}
By default, this plugin will generate a new AES256-CTR key for a new seal operation and the same key will be used for unsealing.
If you would rather specify a key you generated externally, you can set the key parameter in the config or environment variable.
The key prameter must be the PEM encoded formate for an AES-CTR256 key with the PCRPolicy and PolicyAuthValue set and the parent must be the H2 Primary
For example,
export TPM2TOOLS_TCTI="swtpm:port=2321"
## create an H2 primary
printf '\x00\x00' > unique.dat
tpm2_createprimary -C o -G ecc -g sha256 -c primary.ctx -a "fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda|restricted|decrypt" -u unique.dat
## setup the trial policy
tpm2_startauthsession -S session.dat
tpm2_pcrread sha256:23 -o pcr23_val.bin
tpm2_policypcr -S session.dat -l sha256:23 -L policy.dat -f pcr23_val.bin
tpm2_policyauthvalue -S session.dat -L policy.dat
tpm2_flushcontext -t && tpm2_flushcontext -s && tpm2_flushcontext -l
## create the key
tpm2_create -g sha256 -G aes256ctr -u key.pub -r key.prv -C primary.ctx -L policy.dat -p foo
tpm2_flushcontext -t && tpm2_flushcontext -s && tpm2_flushcontext -l
## now encode it as PEM and:
tpm2_encodeobject -C primary.ctx -u key.pub -r key.prv -o private.pem -p
$ cat private.pem
-----BEGIN TSS2 PRIVATE KEY-----
MIIBDAYGZ4EFCgEDoAMBAQECBEAAAAEEVABSACUACwAGAHIAIFdonSCswgZqeft1
2oXASbTDMv/u/x+E9n+Oa9gVtMmUAAYBAABAACDaYRUZfv/VjWXNqKydy1W6A8ke
UyU3VCTtt6Uzt/eZYwSBoACeACB8KqcsrhIV6vj+zYNY8qn51hiB/A1f6wfdnLRY
D5JhsAAQYXHuKO+iHa8/JyXCco2/3z7tdinL95LbeY35Khzkb1Su4BCwpXN0mqqM
0+aGhAAXcqZTjfbEPyOMS7v6KKxecjT3MF1BU5k4WqaZzaGEk6hfyTpcg6hSLSop
FiVG9F+cupwPWhle2caSq2GtFgMrbjVGuwfFxu+19Ig=
-----END TSS2 PRIVATE KEY-----
## to test encryption/decryption
tpm2_load -C primary.ctx -u key.pub -r key.prv -n key.name -c aes.ctx
echo "foo" > secret.dat
openssl rand -out iv.bin 16
tpm2_startauthsession --policy-session -S session.dat
tpm2_pcrread sha256:23 -o pcr23_val.bin
tpm2_policypcr -S session.dat -l sha256:23 -L policy.dat -f pcr23_val.bin
tpm2_policyauthvalue -S session.dat -L policy.dat
tpm2_encryptdecrypt -Q --iv iv.bin -c aes.ctx -o cipher.out secret.dat -p"session:session.dat+foo"
tpm2_flushcontext -t && tpm2_flushcontext -s && tpm2_flushcontext -l
## redo the policy again to decrypt
tpm2_startauthsession --policy-session -S session.dat
tpm2_pcrread sha256:23 -o pcr23_val.bin
tpm2_policypcr -S session.dat -l sha256:23 -L policy.dat -f pcr23_val.bin
tpm2_policyauthvalue -S session.dat -L policy.dat
tpm2_encryptdecrypt -Q --iv iv.bin -c aes.ctx -d -o plain.out cipher.out -p"session:session.dat+foo" which means the config file would be something like:
seal "tpm" {
tpm_path = "127.0.0.1:2321"
pcr_values = "23:0000000000000000000000000000000000000000000000000000000000000000"
user_auth = "foo"
key = "-----BEGIN TSS2 PRIVATE KEY-----\nMIIBDAYGZ4EFCgEDoAMBAQECBEAAAAEEVABSACUACwAGAHIAIFdonSCswgZqeft1\n2oXASbTDMv/u/x+E9n+Oa9gVtMmUAAYBAABAACDaYRUZfv/VjWXNqKydy1W6A8ke\nUyU3VCTtt6Uzt/eZYwSBoACeACB8KqcsrhIV6vj+zYNY8qn51hiB/A1f6wfdnLRY\nD5JhsAAQYXHuKO+iHa8/JyXCco2/3z7tdinL95LbeY35Khzkb1Su4BCwpXN0mqqM\n0+aGhAAXcqZTjfbEPyOMS7v6KKxecjT3MF1BU5k4WqaZzaGEk6hfyTpcg6hSLSop\nFiVG9F+cupwPWhle2caSq2GtFgMrbjVGuwfFxu+19Ig=\n-----END TSS2 PRIVATE KEY-----"
}- Invalid Password
Indicates the passphrased used during sealing does not match what is set for unsealing.
[WARN] failed to unseal core: error="fetching stored unseal keys failed: failed to decrypt keys from storage: go-tpm-wrapping: error executing unseal: TPM_RC_AUTH_FAIL (session 1): the authorization HMAC check failed and DA counter incremented"
Please note that repeated attempts with an incorrect phassphrase can result in a TPM Dictionary Lockout (TPM_RC_LOCKOUT)
You can reset the lockout using tpm2_dictionarylockout command from tpm2_tools
tpm2_dictionarylockout --clear-lockout- Invalid PCR
Likely indicates the PCR value used to seal the key does not match match what is in the config file or in the actual PCR bank:
The following indicates the PCR value provided to the wrapper does not match the existing value of the PCR banks
[WARN] failed to unseal core: error="fetching stored unseal keys failed: failed to decrypt keys from storage: go-tpm-wrapping: error executing PolicyPCR: TPM_RC_VALUE (parameter 1): value is out of range or is not correct for the context"
The following indicates the PCR values provided to the wrapper matches what the TPM values are but does not match what the key was initially bound to
[WARN] failed to unseal core: error="fetching stored unseal keys failed: failed to decrypt keys from storage: go-tpm-wrapping: error executing unseal: TPM_RC_POLICY_FAIL (session 1): a policy check failed"
- Invalid Encrypted Key for TPM
Indicates the TPM based sealing key cannot be decrypted by the given TPM.
Likley the TPM that was used to encrypt the sealing key is not the same one currently being used.
[WARN] failed to unseal core: error="fetching stored unseal keys failed: failed to decrypt keys from storage: go-tpm-wrapping: error executing Load: TPM_RC_INTEGRITY (parameter 1): integrity check failed"
These tests uses a softwareTPM which can be found here.
First initialize it:
rm -rf /tmp/myvtpm && \
mkdir /tmp/myvtpm && \
swtpm_setup --tpmstate /tmp/myvtpm --tpm2 --create-ek-cert && swtpm socket --tpmstate dir=/tmp/myvtpm --tpm2 --server type=tcp,port=2321 --ctrl type=tcp,port=2322 --flags not-need-init,startup-clear --log level=5
export TPM2TOOLS_TCTI="swtpm:port=2321"Now that you've completed the required setup, you can run the tests via:
export VAULT_ACC=true
go test -vYou should see
$ go test -v
=== RUN TestDisableEnv
--- PASS: TestDisableEnv (0.01s)
=== RUN TestTPMSeal
=== RUN TestTPMSeal/config
--- PASS: TestTPMSeal (0.00s)
--- PASS: TestTPMSeal/config (0.00s)
=== RUN TestTPMSeal_Lifecycle
--- PASS: TestTPMSeal_Lifecycle (0.01s)
=== RUN TestTPMSeal_LifecycleExternalKey
--- PASS: TestTPMSeal_LifecycleExternalKey (0.01s)
=== RUN TestTPMSeal_Lifecycle_UserAuth_Pass
--- PASS: TestTPMSeal_Lifecycle_UserAuth_Pass (0.01s)
=== RUN TestTPMSeal_Lifecycle_UserAuth_Fail
--- PASS: TestTPMSeal_Lifecycle_UserAuth_Fail (0.01s)
=== RUN TestTPMSeal_Lifecycle_PCR_Pass
--- PASS: TestTPMSeal_Lifecycle_PCR_Pass (0.01s)
=== RUN TestTPMSeal_Lifecycle_PCR_Fail
--- PASS: TestTPMSeal_Lifecycle_PCR_Fail (0.01s)
PASS
ok github.com/openbao/go-kms-wrapping/wrappers/tpm/v2 0.087sNote that the TestTPMSeal_Lifecycle_PCR_Fail changes the value of PCR=23. What that means is you must restart the software TPM again to rerun the test suite. Alternatively, you can skip just that one:
go test -v -count=1 -skip '^(TestTPMSeal_Lifecycle_PCR_Fail)'You can also just use the Dockerfile to run the tests above
docker build --progress=plain -f wrappers/tpm/Dockerfile .