From 812cf3a6d12adad80df27825d511b0482a3e2c08 Mon Sep 17 00:00:00 2001 From: sal rashid Date: Wed, 28 Aug 2024 10:45:42 -0400 Subject: [PATCH] set session encryption in-out for import; update docs Signed-off-by: sal rashid --- README.md | 196 +++++++++++++++++++++++++++--------------------------- import.go | 15 +---- seal.go | 13 +--- 3 files changed, 104 insertions(+), 120 deletions(-) diff --git a/README.md b/README.md index 43905ae..1962464 100644 --- a/README.md +++ b/README.md @@ -20,14 +20,14 @@ There are two modes to using this library: * `Remote encryption` - This mode utilizes a TPM `Endorsement Public Key (EKPub)` to wrap the encryption key which can ONLY get decrypted by the TPM that owns the EKPub + This mode utilizes a TPM `Endorsement Public Key (EKPub)` to wrap the some data which can ONLY get decrypted by the TPM that owns the EKPub This mode requires local access to a real or simulated TPM to encrypt the data. -For a detailed description on how these modes work, see the `Background` section at the end +For a detailed description on how these modes work, see the [Background](#background) section at the end -This library is a a variation of [https://github.com/hashicorp/go-kms-wrapping](https://github.com/hashicorp/go-kms-wrapping) +This library builds off of Hashicorp Vault wrapping [https://github.com/hashicorp/go-kms-wrapping](https://github.com/hashicorp/go-kms-wrapping). You can use this as a library or CLI @@ -41,6 +41,14 @@ To configure the software TPM on your laptop for testing, see the `Using swtpm` --- +### CLI + +You can build or download the cli from the `Releases` section + +```bash +go build -o go-tpm-wrapping cmd/main.go +``` + CLI Options: | Option | Description | @@ -60,21 +68,14 @@ CLI Options: --- -### CLI - -You can build or download the cli from the `Releases` section - -```bash -go build -o go-tpm-wrapping cmd/main.go -``` - #### Usage Seal To use, simply initialize the wrapper as shown below, specify a path to the TPM and optionally the PCR values to bind against -To use as a CLI, you can run `cmd/main.go` or download from the `Releases` page. If you want to use as an API, see the `example` folder +To use as a CLI, you can run `cmd/main.go` or download from the [Releases](/releases) page. If you want to use as an API, see the [example/](/tree/main/example) folder + +- **encrypt/decrypt** -- encrypt/decrypt ```bash $ go-tpm-wrapping --mode=seal --debug \ --dataToEncrypt=foo --encryptedBlob=/tmp/encrypted.json \ @@ -84,7 +85,7 @@ $ go-tpm-wrapping --mode=seal --debug --decrypt=true \ --encryptedBlob=/tmp/encrypted.json --tpm-path="127.0.0.1:2321" ``` -- encrypt/decrypt with passphrase +- **encrypt/decrypt with passphrase** ```bash $ go-tpm-wrapping --mode=seal --debug \ @@ -95,7 +96,7 @@ $ go-tpm-wrapping --mode=seal --debug --keyPass=testpass --decrypt=true \ --encryptedBlob=/tmp/encrypted.json --tpm-path="127.0.0.1:2321" ``` -- encrypt/decrypt with passphrase and PCR values. +- **encrypt/decrypt with passphrase and PCR values** ```bash ### for example, if you want to stipulate the following PCR values must be present to unseal @@ -176,7 +177,7 @@ Just to note, you don't *really* need access to a real, permanent TPM on the sys The following encrypts some data using just the remote `ekpub` -- encrypt/decrypt +- **encrypt/decrypt** ```bash # encrypt @@ -197,7 +198,7 @@ $ go-tpm-wrapping --mode=import --debug --decrypt --encrypting_public_key=/tmp/e --tpm-path="127.0.0.1:2341" ``` -- With userAuth +- **With userAuth** ```bash # encrypt @@ -216,7 +217,7 @@ $ go-tpm-wrapping --mode=import --debug --decrypt \ --tpm-path="127.0.0.1:2341" ``` -- With PCR +- **With PCR** ```bash ## encrypt/decrypt and bind the data to the **destination TPM's** values in @@ -440,10 +441,9 @@ $ go run import_decrypt/main.go --encryptedBlob=/tmp/encrypted.json \ --tpm-path="127.0.0.1:2341" ``` - ### Session Encryption -Each operation uses encrypted sessions but by default, the library interrogates the TPM for the current EK directly. +Each operation uses [encrypted sessions](https://trustedcomputinggroup.org/wp-content/uploads/TCG_CPU_TPM_Bus_Protection_Guidance_Passive_Attack_Mitigation_8May23-3.pdf) by default and interrogates the TPM for the current EK directly. If for whatever reason you want to specify the "name" of the EK to to use, set the `--tpm-session-encrypt-with-name=` parameter shown below @@ -459,69 +459,6 @@ xxd -p -c 100 /tmp/ekpubAname.bin --tpm-session-encrypt-with-name=000b47ab97fdda365cbb86a37548e38468f72e8baccc633cffc42402183679956608 ``` -also see - -* [tpmrand Encrypted Session](https://github.com/salrashid123/tpmrand?tab=readme-ov-file#encrypted-session) -* [aws-tpm-process-credential Encrypted Sessions](https://github.com/salrashid123/aws-tpm-process-credential?tab=readme-ov-file#encrypted-tpm-sessions) -* [salrashid123/tpm2/Session Encryption](https://github.com/salrashid123/tpm2/tree/master/tpm_encrypted_session) - - -### Build - -If you want to regenerate with protoc: - -```bash -$ /usr/local/bin/protoc --version - libprotoc 25.1 - -$ go get -u github.com/golang/protobuf/protoc-gen-go - -$ /usr/local/bin/protoc -I ./ --include_imports \ - --experimental_allow_proto3_optional --include_source_info \ - --descriptor_set_out=tpmwrappb/wrap.proto.pb \ - --go_out=paths=source_relative:. tpmwrappb/wrap.proto -``` - -### Seal/Import with non-EKPub - -_TODO_ - -The default mode for "import" utilizes the Endorsement Public key. A TODO is to allow _any_ encryption key you trust on the target TPM (`TPM-B`). - -You would create an arbitrary encryption-only key using something like the following and evict it to a persistent handle as shown below on `TPM-B` - - -```bash -export TPM2TOOLS_TCTI="swtpm:port=2341" -export TPM2OPENSSL_TCTI="swtpm:port=2341" -tpm2_pcrread sha256:0,23 - -## create "H2 Template" as primary, you can setup any primary you want -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 - -tpm2_create -G rsa -u key.pub -r key.priv -C primary.ctx -a "fixedtpm|fixedparent|sensitivedataorigin|userwithauth|decrypt" -tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx - -echo "meet me at..." > secret.txt -tpm2_rsaencrypt -c key.ctx -o secret.txt.enc secret.txt -tpm2_rsadecrypt -c key.ctx -o secret.txt.dec secret.txt.enc - -tpm2_flushcontext -t - -## cant' use this key for signing -## tpm2_sign -c key.ctx -g sha256 -o sig.rssa secret.txt - -tpm2_readpublic -c key.ctx -o /tmp/pubA.pem -f PEM -Q -tpm2_evictcontrol -C o -c key.ctx 0x81010001 -``` - -Copy `/tmp/pubA.pem` to `TPM-A` and start the import. - -Copy the `encryptedblob.json` to `TPM-B`. Specify the persistent handle while importing on `TPM-B` (eg, use (`--mode=import --parentHandle=0x81010001`)) - ---- - ### Background The following details some background how each of these modes works: @@ -543,7 +480,7 @@ to `Encrypt`: ``` key1, ciphertext1, iv1: = go-kms-wrapping.Encrypt(plaintext1) - tpm_key = TPMKey.Seal(key1) + tpm_key = tpm2_seal(key1) ``` to `Decrypt`: @@ -557,7 +494,7 @@ to `Decrypt`: 7. return the plaintext ``` - key1 = TPMKey.Unseal() + key1 = tpm2_unseal() plaintext1 = go-kms-wrapping.Decrypt(key1, iv1, ciphertext1) ``` @@ -585,9 +522,9 @@ on `TPM-A`: ``` key1, ciphertext1, iv1: = go-kms-wrapping.Encrypt(plaintext1) per_use_iv = new random iv - tpm_key = new TPMKey(with_auth_policy) - ciphertext2 = tpm_key.Encrypt(key1, per_use_iv) - duplicate = TPMDuplicate(tpm_key, ekPubB.pem) + tpm_key = new tpm2_create(auth=with_auth_policy) + ciphertext2 = tpm2_encrypt(key1, per_use_iv) + duplicate = tpm2_duplicate(tpm_key, ekPubB.pem) ``` copy the duplicated key and wrapped _inner encryption key_,per_use_iv, iv1, ciphertext to `TPM-B` (all of which is encoded into one file) @@ -600,8 +537,8 @@ on `TPM-B`: 11. use the inner key, IV and ciphertext to run [go-kms-wrapping.Decrypt()](https://pkg.go.dev/github.com/hashicorp/go-kms-wrapping#Envelope.Decrypt) ``` - tpm_key = TPMImport(duplicate) - key1 = tpm_key.Decrypt(ciphertext2, per_use_iv) + tpm_key = tpm2_import(duplicate) + key1 = tpm2_decrypt(ciphertext2, per_use_iv) plaintext1 = go-kms-wrapping.Decrypt(key1, iv1, ciphertext1) ``` @@ -612,8 +549,10 @@ on `TPM-B`: on `TPM-A`: - 3. given plaintext, use [go-kms-wrapping.Encrypt()](https://pkg.go.dev/github.com/hashicorp/go-kms-wrapping#Envelope.Encrypt) to encrypt. + 3. given plaintext, use [go-kms-wrapping.Encrypt()](https://pkg.go.dev/github.com/hashicorp/go-kms-wrapping#Envelope.Encrypt). + `go-kms-wrapping.Encrypt` function will return a new _inner encryption key_, initialization vector and cipher text + 4. create *NEW* random local (non-tpm) AES key 5. use the AES key to encrypt the _inner encryption key_ 6. create a trial TPM `PolicyOR` session with a `PolicyPCR` and `PolicyDuplicateSelect` (the latter which bound to `TPM-B`'s ekpub) @@ -625,11 +564,11 @@ on `TPM-A`: ``` key1, ciphertext1, iv1: = go-kms-wrapping.Encrypt(plaintext1) per_use_iv = new random iv - per_useaes_key = new AESCFBKey() // this is a nonTPM key that is per-use + per_useaes_key = new go.crypto.AESCFBKey() // this is a nonTPM key that is per-use wrapped_key1 = per_useaes_key.Encrypt(key1, per_use_iv) // we're doing this because we maynot able to fulfill the pcr policy on TPM-A - tpm_key = new TPMKey(with_auth_policy, per_useaes_key as senstitive ) // this is critical, we set the sentsitive to the per-use key; - duplicate = TPMDuplicate(tpm_key, ekPubB.pem) + tpm_key = new tpm2_create(auth=with_auth_policy, **sensitvie=per_useaes_key** ) // this is critical, we set the sentsitive to the per-use key; + duplicate = tpm2_duplicate(tpm_key, ekPubB.pem) ``` copy the duplicated tpm_key, wrapped_key1, iv1, per_use_iv to `TPM-B` @@ -642,14 +581,77 @@ on `TPM-B` 13. Use the KEK to decrypt the DEK ``` - tpm_key = TPMImport(duplicate) - key1 = tpm_key.Decrypt(wrapped_key1, per_use_iv) // we can do this because its the same key and iv which we encrypted with + tpm_key = tpm2_import(duplicate) + key1 = tpm2_decrypt(wrapped_key1, per_use_iv) // we can do this because its the same key and iv which we encrypted with plaintext1 = go-kms-wrapping.Decrypt(key1, iv1, ciphertext1) ``` --- -### Using swtpm +also see + +* [tpmrand Encrypted Session](https://github.com/salrashid123/tpmrand?tab=readme-ov-file#encrypted-session) +* [aws-tpm-process-credential Encrypted Sessions](https://github.com/salrashid123/aws-tpm-process-credential?tab=readme-ov-file#encrypted-tpm-sessions) +* [salrashid123/tpm2/Session Encryption](https://github.com/salrashid123/tpm2/tree/master/tpm_encrypted_session) + + +#### Build + +If you want to regenerate with protoc: + +```bash +$ /usr/local/bin/protoc --version + libprotoc 25.1 + +$ go get -u github.com/golang/protobuf/protoc-gen-go + +$ /usr/local/bin/protoc -I ./ --include_imports \ + --experimental_allow_proto3_optional --include_source_info \ + --descriptor_set_out=tpmwrappb/wrap.proto.pb \ + --go_out=paths=source_relative:. tpmwrappb/wrap.proto +``` + +#### Seal/Import with non-EKPub + +_TODO_ + +The default mode for "import" utilizes the Endorsement Public key. A TODO is to allow _any_ encryption key you trust on the target TPM (`TPM-B`). + +You would create an arbitrary encryption-only key using something like the following and evict it to a persistent handle as shown below on `TPM-B` + + +```bash +export TPM2TOOLS_TCTI="swtpm:port=2341" +export TPM2OPENSSL_TCTI="swtpm:port=2341" +tpm2_pcrread sha256:0,23 + +## create "H2 Template" as primary, you can setup any primary you want +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 + +tpm2_create -G rsa -u key.pub -r key.priv -C primary.ctx -a "fixedtpm|fixedparent|sensitivedataorigin|userwithauth|decrypt" +tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx + +echo "meet me at..." > secret.txt +tpm2_rsaencrypt -c key.ctx -o secret.txt.enc secret.txt +tpm2_rsadecrypt -c key.ctx -o secret.txt.dec secret.txt.enc + +tpm2_flushcontext -t + +## cant' use this key for signing +## tpm2_sign -c key.ctx -g sha256 -o sig.rssa secret.txt + +tpm2_readpublic -c key.ctx -o /tmp/pubA.pem -f PEM -Q +tpm2_evictcontrol -C o -c key.ctx 0x81010001 +``` + +Copy `/tmp/pubA.pem` to `TPM-A` and start the import. + +Copy the `encryptedblob.json` to `TPM-B`. Specify the persistent handle while importing on `TPM-B` (eg, use (`--mode=import --parentHandle=0x81010001`)) + +--- + +#### Using swtpm If you want to test locally with software TPMs: diff --git a/import.go b/import.go index fa8113b..998a73e 100644 --- a/import.go +++ b/import.go @@ -219,20 +219,11 @@ func (s *RemoteWrapper) Encrypt(ctx context.Context, plaintext []byte, opt ...wr rwr := transport.FromReadWriter(rwc) - // create a basic encryption session - encsess := tpm2.HMAC(tpm2.TPMAlgSHA256, 16, tpm2.AESEncryption(128, tpm2.EncryptOut)) - defer func() { - flushContextCmd := tpm2.FlushContext{ - FlushHandle: encsess.Handle(), - } - _, _ = flushContextCmd.Execute(rwr) - }() - - // get the endorsement key using that initial session + // get the endorsement key for the local TPM which we will use for parameter encryption createEKRsp, err := tpm2.CreatePrimary{ PrimaryHandle: tpm2.TPMRHEndorsement, InPublic: tpm2.New2B(tpm2.RSAEKTemplate), - }.Execute(rwr, encsess) + }.Execute(rwr) if err != nil { return nil, fmt.Errorf("error creating EK Primary %v", err) } @@ -844,7 +835,7 @@ func (s *RemoteWrapper) Decrypt(ctx context.Context, in *wrapping.BlobInfo, opt } // create an actual full encryption session using the EK we trust - rsessInOut := tpm2.HMAC(tpm2.TPMAlgSHA256, 16, tpm2.AESEncryption(128, tpm2.EncryptIn), tpm2.Salted(createEKRsp.ObjectHandle, *encryptionPub)) + rsessInOut := tpm2.HMAC(tpm2.TPMAlgSHA256, 16, tpm2.AESEncryption(128, tpm2.EncryptInOut), tpm2.Salted(createEKRsp.ObjectHandle, *encryptionPub)) defer func() { flushContextCmd := tpm2.FlushContext{ FlushHandle: rsessInOut.Handle(), diff --git a/seal.go b/seal.go index 1b70784..720ad28 100644 --- a/seal.go +++ b/seal.go @@ -176,20 +176,11 @@ func (s *TPMWrapper) Encrypt(ctx context.Context, plaintext []byte, opt ...wrapp return nil, fmt.Errorf("error wrapping data: %w", err) } - // start an initial encryption session - encsess := tpm2.HMAC(tpm2.TPMAlgSHA256, 16, tpm2.AESEncryption(128, tpm2.EncryptOut)) - defer func() { - flushContextCmd := tpm2.FlushContext{ - FlushHandle: encsess.Handle(), - } - _, _ = flushContextCmd.Execute(rwr) - }() - - // get the endorsement key using that initial session + // get the endorsement key for the local TPM which we will use for parameter encryption createEKRsp, err := tpm2.CreatePrimary{ PrimaryHandle: tpm2.TPMRHEndorsement, InPublic: tpm2.New2B(tpm2.RSAEKTemplate), - }.Execute(rwr, encsess) + }.Execute(rwr) if err != nil { return nil, fmt.Errorf("error creating EK Primary %v", err) }