mirror of
https://github.com/tpm2dev/tpm.dev.tutorials.git
synced 2024-11-10 01:12:10 +00:00
176 lines
7.3 KiB
Markdown
176 lines
7.3 KiB
Markdown
|
# `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
|