threefoldtech / zos

Autonomous operating system
https://threefold.io/host/
Apache License 2.0
84 stars 13 forks source link

ZOS TPM Support specifications #1580

Open muhamadazmy opened 2 years ago

muhamadazmy commented 2 years ago

Related issues #1319

maxux commented 2 years ago

Theses commands rely on the fact that tpm2-abrmd daemon runs.

/etc/init.d/tpm2-abrmd start

Using OpenSSL

Check TPM support in openssl:

# openssl engine -c tpm2tss
(tpm2tss) TPM2-TSS engine for OpenSSL
 [RSA, RAND]

Clear the current state to start from scratch:

tpm2_clear

Generate a new RSA key (ecdsa is supported as well but not as widely available than RSA in the field):

tpm2tss-genkey -a rsa rsakey

Extract the public key from the generated key:

openssl rsa -engine tpm2tss -inform engine -in rsakey -pubout -outform pem -out mykey.pub
cat mykey.pub

Generate some data and sign them with the key:

echo HelloWorld > data
openssl pkeyutl -pubin -inkey mykey.pub -in data -encrypt -out data.sign

Decrypt the data signed by public key:

openssl pkeyutl -engine tpm2tss -keyform engine -inkey rsakey -decrypt -in data.sign -out data.decrypt
md5sum data.decrypt data

Using tpm2 natively

Create a parent key and a custom key (simple rsa key)

# tpm2_createprimary -C o -c parent.ctx -G rsa2048:null:aes128cfb
# openssl genrsa > maxux.key

Install the key inside tpm memory and make it persistant:

tpm2_evictcontrol -c parent.ctx

Get a public and private key usable by tpm2, linked to parent and coming from custom key:

tpm2_import -i maxux.key -r private_key.tss -u public_key.tss -Grsa -C parent.ctx

Get a context file usable by tpm2 from maxux.key (key.ctx will be usable)

tpm2_load -C parent.ctx -u public_key.tss -r private_key.tss -c key.ctx

You can now sign using that key:

tpm2_sign -c key.ctx -g sha256 -o message.sign message.dat

And verify signature:

tpm2_verifysignature -c key.ctx -g sha256 -s message.sign -m message.dat

Extra search

Based on PCR (registers), you can disallow access to a key if registers are not in a well-known state. Theses registers are accumulators, each settings in specific part (boot process, bios settings, etc:) increment the accumulator, you cannot set the register to something specific. Theses register ensure that any change to any settings won't produce the same register, so if a register is in a specific state, you know all settings behind get the expected values.

image

List current registers state:

tpm2_pcrread

Tool tpm2_policypcr can be used to play with this but I don't get the commands anymore, will take a look later :)

muhamadazmy commented 2 years ago

Note that titan nodes (and freefarm zos nodes) don't have tpm devices built in. So far I am experimenting with some tpm2 libraries (including native golang libs) What i am trying to achieve is:

TPM documentations is very obscure (tools, specs, and libraries) which is why it's taking to long to get the first prototype.

muhamadazmy commented 2 years ago

Just to clarify, we should use tpm mainly to "store" the node key securely not to use it to sign/verify. This operation should then be done without depending on external context files or other files from storage (unless those files can be generated again with the same tpm device).

muhamadazmy commented 2 years ago

So far i have reached here:

Those are done first time to only

tpm2_createprimary -c primary.ctx -Q

tpm2_pcrread -Q -o pcr.bin sha256:0,1,2,3

tpm2_createpolicy -Q --policy-pcr -l sha256:0,1,2,3 -f pcr.bin -L pcr.policy

echo 'secret' | tpm2_create -C primary.ctx -L pcr.policy -i-\
-u seal.pub -r seal.priv -c seal.ctx -Q

After that, the object need to be loaded to be unsealed

Note: after a reboot the primary.ctx need to be re-created with tpm2_createprimary

tpm2_load -c seal.ctx -C primary.ctx -u seal.pub -r seal.priv
tpm2_unseal -c seal.ctx -p pcr:sha256:0,1,2,3 > unsealed.txt

The problem is we still need to persist the seal.* files on disk. They are not usable on other machines but we might lose them. I am still looking into a way to store them on the tpm itself.

maxux commented 2 years ago

Here is a script which uses same PCR and seal data into TPM without the need of local file. Please try it locally and tell me if it works for you. You maybe need to uncomment the tpm2_clear to start from scratch. Can you try a reboot and ensure you can only unseal it if PCR is the same ? Maybe try adding more PCR which changes. I don't have a machine I can reboot right now to test that.

I use sha1 and not sha256 for PCR because some machine (like mine) don't support sha256.

We can use this method to store disk-encryption key as well and fix our storage key issue :)

#!/bin/bash
set -ex

echo "Private Key Data" > secret.bin

# tpm2_clear
tpm2_createpolicy --policy-pcr -l sha1:0,1,2 -L policy.digest
tpm2_createprimary -C e -g sha1 -G rsa -c primary.context

tpm2_create -g sha256 -u obj.pub -r obj.priv -C primary.context -L policy.digest -a "noda|adminwithpolicy|fixedparent|fixedtpm" -i secret.bin

tpm2_load -C primary.context -u obj.pub -r obj.priv -c load.context
tpm2_evictcontrol -C o -c load.context 0x81000000

rm -f load.context obj.priv obj.pub policy.digest primary.context

tpm2_getcap handles-persistent
tpm2_readpublic -c 0x81000000

tpm2_unseal -c 0x81000000 -p pcr:sha1:0,1,2 # -o secret.bin

# remove
# tpm2_evictcontrol -C o -c 0x81000000
muhamadazmy commented 2 years ago

I use the swtpm to emulate a tpm device for a VM. so rebooting is no problem

I tested your code and it works fine after a reboot and also after a complete power off and it works just fine, so thanks. I will see what I can do to change the values of the PCR without wiping the tpm data files (from the emulator)

maxux commented 2 years ago

I think including more PCR or some PCR which changes across reboot could do it

muhamadazmy commented 2 years ago

I think including more PCR or some PCR which changes across reboot could do it

Not sure about this because the policy (as u specify it) include only PCR 0, 1, and 2 so if I added more this won't change the values of 0, 1 or 2. So only changes to those PCRs would So a quick test would be doing something like

tpm2_unseal -c 0x81000000 -p pcr:sha1:0,1,3  # replace one of the PRCs with 3 instead of 2

which "as expected" fails. I will see if i can change something to actually affect the PRCs values.

maxux commented 2 years ago

I mean, start from scratch and include PCR 1 > 7 in the tpm2_createpolicy I think some value above 3 will changes across reboot :) Use same values on unseal, of course

muhamadazmy commented 2 years ago

Yeah, i understand. That's what i was trying to do. Anyway, i used pcrextend command to change values of PRCs at other positions (8) and included this in the sealing policy. after rebooting i couldn't unseal until i rexetended the same banks with the same values again.

So this is great. I can work on the implementation now. Thanks @maxux :)

maxux commented 2 years ago

Awesome :heart: To implement, this can maybe helps to not runs commands: https://github.com/canonical/go-tpm2

muhamadazmy commented 2 years ago

Yeah, i landed on this a while back. As if the commands are already not complex enough!

The good side of how to use this is that now we won't have to include the tpm2 binary, hence this will work on machines booted with older images that doesn't include the tpm command. On the other side, i now have to figure out how to basically re-write the command using the tpm2 API.

I will give it another look see if I can do it.