From 6363b9bbdc1fb90584332e2dc1e6a22d780e7119 Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Thu, 3 Jun 2021 23:21:48 +0200 Subject: [PATCH] add example code + basic concept of passing a secret to a TPM just knowing the EK Signed-off-by: Erik Larsson --- Attestation/ek-secret.md | 175 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 Attestation/ek-secret.md diff --git a/Attestation/ek-secret.md b/Attestation/ek-secret.md new file mode 100644 index 0000000..b7217fd --- /dev/null +++ b/Attestation/ek-secret.md @@ -0,0 +1,175 @@ +# `Passing a secret to a TPM using only the EK` + +This is example code to pass a secret to a system by just knowing the endorsenment key. +We will be using the current (commit 07a92e9fa75548ea102ce90b3b6182093b3f7a73 or later) master branch of https://github.com/tpm2-software/tpm2-pytss + +The terms for the systems are `client`, the system we want to pass the secret to and `server`, the system which has the secret but doesn't need a TPM. +One assumtion that will be made is that you already have the EKpub for the remote system on the local system, and trust it. +While we will use the EK in this guide any key accepted by ActivateCredential should work. + +## Concept +The concept is that the key being activated by a call to ActivateCredential on the remote system doesn't have to be generated on the TPM, as long as the public and private parts are loaded it will succeed. +By generating a temporary key pair on the local system we can run MakeCredential with the remote system EK, name of the locally generated key and the secret. + +## server script +```python +#!/usr/bin/python3 + +import sys +from tpm2_pytss import * +from tpm2_pytss.makecred import MakeCredential +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat, PrivateFormat, NoEncryption + +def main(ekpath, publicpath, sensitivepath, credpath, secretpath, oursecret): + # first read the EK and unmarshal it + with open(ekpath, 'rb') as ef: + ekb = ef.read() + ekpub, _ = TPM2B_PUBLIC.Unmarshal(ekb) + + # Now we generate the temporary key pair + # We are using ECC keys here as they are generally fast to generate, but RSA should work as well + # We will use the curve SECP256R1 as it should work on all TPMs + # One could use a well known/the same pre-generated key for multiple systems + privatekey = ec.generate_private_key(ec.SECP256R1, backend=default_backend()) + publickey = privatekey.public_key() + + # Now it's time to TPM structures from the keys + # First we need to encode it due to how the tpm2_pytss API currently works + privateenc = privatekey.private_bytes(Encoding.DER, PrivateFormat.PKCS8, NoEncryption()) + publicenc = publickey.public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo) + sensitive = TPM2B_SENSITIVE.fromPEM(privateenc) + # by objectAttributes to 0 we reduce the change that keys will be used for anything + public = TPM2B_PUBLIC.fromPEM(publicenc, objectAttributes=0) + # the same applices to authPolicy + public.publicArea.authPolicy = b"\x00" * 32 + + # now it's time to run the MakeCredential part, using the software implementation in tpm2_pytss + # the API is slight different to the standard, but behaves the same + credblob, secret = MakeCredential(ekpub, oursecret, bytes(public.getName())) + + # time to marshal the structures and save them to disk so we can send them the remote system + pubb = public.Marshal() + with open(publicpath, 'xb') as pubf: + pubf.write(pubb) + sensb = sensitive.Marshal() + with open(sensitivepath, 'xb') as sensf: + sensf.write(sensb) + credb = credblob.Marshal() + with open(credpath, 'xb') as credf: + credf.write(credb) + secretb = secret.Marshal() + with open(secretpath, 'xb') as secretf: + secretf.write(secretb) + + +if __name__ == '__main__': + if len(sys.argv) < 6: + sys.stderr.write(f"usage: {sys.argv[0]} ek-public temp-public temp-sensitive credblob secret\n") + exit(1) + main(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5], b"example secret") +``` + +Arguments to the script is the following: +ek-public: the path to the public part of the EK +temp-public: where to save the public part of the temporary key +temp-sensitive: where to save the sensitive part of the temporary key +credlob: where to save the encrypted credential generated by MakeCredential +secret: where to save the encrypted secret generated by MakeCredential + +## client script +```python +#!/usr/bin/python3 + + +import sys +from tpm2_pytss import * + +def unmarshal_tools_context(ekb): + ekctx = TPMS_CONTEXT() + magic = int.from_bytes(ekb[0:4], byteorder='big') + version = int.from_bytes(ekb[4:8], byteorder='big') + ekctx.hierarchy = int.from_bytes(ekb[8:12], byteorder='big') + ekctx.savedHandle = int.from_bytes(ekb[12:16], byteorder='big') + ekctx.sequence = int.from_bytes(ekb[16:24], byteorder='big') + ekctx.contextBlob, _ = TPM2B_CONTEXT_DATA.Unmarshal(ekb[24:]) + return ekctx + +def eksession(ectx): + session = ectx.StartAuthSession( + ESYS_TR.NONE, + ESYS_TR.NONE, + None, + TPM2_SE.POLICY, + TPMT_SYM_DEF(algorithm=TPM2_ALG.NULL), + TPM2_ALG.SHA256, + ) + + ectx.PolicySecret( + ESYS_TR.RH_ENDORSEMENT, + session, + TPM2B_NONCE()._cdata, + TPM2B_DIGEST()._cdata, + TPM2B_NONCE()._cdata, + 0, + session1=ESYS_TR.PASSWORD, + ) + + return session + +def main(ekpath, publicpath, sensitivepath, credpath, secretpath): + # time to setup a ESAPI context, we will use the default TCTI for the system + ectx = ESAPI() + + # Time to load the EK context, by using tpm2_createek there is no reason the implement the whole setup in this example code + with open(ekpath, 'rb') as ekf: + ekb = ekf.read() + ekctx = unmarshal_tools_context(ekb) + ekhandle = ectx.ContextLoad(ekctx) + + # now lets setup the standard EK policy session + session = eksession(ectx) + + # Now we should read, unmarshal and load the temporary key pair + with open(publicpath, 'rb') as pubf: + pubb = pubf.read() + public, _ = TPM2B_PUBLIC.Unmarshal(pubb) + with open(sensitivepath, 'rb') as sensf: + sensb = sensf.read() + sensitive, _ = TPM2B_SENSITIVE.Unmarshal(sensb) + print(sensitive.sensitiveArea.authValue.size, public.publicArea.authPolicy.size) + # We will load it under the NULL hierarchy as that is the only hierarchy allowing both the public and private part to be loaded for external keys + handle = ectx.LoadExternal(sensitive, public, ESYS_TR.RH_NULL) + + + # Time to read and unmarshal the credential and secret for ActivateCredential + with open(credpath, 'rb') as credf: + credb = credf.read() + credblob, _ = TPM2B_ID_OBJECT.Unmarshal(credb) + with open(secretpath, 'rb') as secretf: + secretb = secretf.read() + secret, _ = TPM2B_ENCRYPTED_SECRET.Unmarshal(secretb) + + # Well, now there is nothing left but calling ActivateCredential and getting our secret on the remove system! + oursecret = ectx.ActivateCredential(handle, ekhandle, credblob, secret, session2=session) + + print(f"we got the secret: {bytes(oursecret)}") + +if __name__ == '__main__': + if len(sys.argv) < 6: + sys.stderr.write(f"usage: {sys.argv[0]} ek-ctx temp-public temp-sensitive credblob secret\n") + exit(1) + main(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5]) +``` + +Generate the EK context with `tpm2_createek -c ek.ctx` +The arguments are: +ek-ctx: the context generated by tpm2_createek +temp-public: the temp-public output from the local system script +temp-sensitive: the temp-sensitive output from the local system script +credblob: the credblob output from the local system script +secret: the secret output from the local system script + +## Issues +there is no TPM based protection against replay attacks in this example