diff --git a/test/test_utils.py b/test/test_utils.py index e7da54ca..a739eeaa 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -430,6 +430,101 @@ def test_tpm_export_rsa_child_rsa_parent_with_inner_key(self): hashes.SHA256(), ) + def test_create_ek_ecc(self): + ecc_template = create_ek_template(self.ectx) + _, ecc, _, _, _ = self.ectx.create_primary( + TPM2B_SENSITIVE_CREATE(), ecc_template, ESYS_TR.ENDORSEMENT + ) + + self.assertEqual(ecc.publicArea.type, TPM2_ALG.ECC) + + ecc_nv_nonce = TPM2B_NV_PUBLIC( + nvPublic=TPMS_NV_PUBLIC( + nvIndex=0x1C0000B, + nameAlg=TPM2_ALG.SHA256, + attributes=TPMA_NV.AUTHWRITE | TPMA_NV.AUTHREAD, + dataSize=15, + ) + ) + eh = self.ectx.nv_define_space(b"", ecc_nv_nonce, ESYS_TR.OWNER) + self.ectx.nv_write(eh, b"\xFF" * 15) + ecc_nonce_template = create_ek_template(self.ectx) + _, ecc_nonce, _, _, _ = self.ectx.create_primary( + TPM2B_SENSITIVE_CREATE(), ecc_nonce_template, ESYS_TR.ENDORSEMENT + ) + self.assertNotEqual( + ecc_nonce.publicArea.unique.ecc.x, ecc.publicArea.unique.ecc.x + ) + self.assertNotEqual( + ecc_nonce.publicArea.unique.ecc.y, ecc.publicArea.unique.ecc.y + ) + + def test_create_ek_rsa(self): + rsa_template = create_ek_template(self.ectx, TPM2_ALG.RSA) + _, rsa, _, _, _ = self.ectx.create_primary( + TPM2B_SENSITIVE_CREATE(), rsa_template, ESYS_TR.ENDORSEMENT + ) + self.assertEqual(rsa.publicArea.type, TPM2_ALG.RSA) + + rsa_nv_nonce = TPM2B_NV_PUBLIC( + nvPublic=TPMS_NV_PUBLIC( + nvIndex=0x1C00003, + nameAlg=TPM2_ALG.SHA256, + attributes=TPMA_NV.AUTHWRITE | TPMA_NV.AUTHREAD, + dataSize=127, + ) + ) + rh = self.ectx.nv_define_space(b"", rsa_nv_nonce, ESYS_TR.OWNER) + self.ectx.nv_write(rh, b"\xFF" * 127) + rsa_nonce_template = create_ek_template(self.ectx, TPM2_ALG.RSA) + _, rsa_nonce, _, _, _ = self.ectx.create_primary( + TPM2B_SENSITIVE_CREATE(), rsa_nonce_template, ESYS_TR.ENDORSEMENT + ) + self.assertNotEqual(rsa_nonce.publicArea.unique.rsa, rsa.publicArea.unique.rsa) + + def test_create_ek_template(self): + template = TPMT_PUBLIC( + type=TPM2_ALG.KEYEDHASH, + nameAlg=TPM2_ALG.SHA256, + objectAttributes=TPMA_OBJECT.FIXEDTPM + | TPMA_OBJECT.FIXEDPARENT + | TPMA_OBJECT.SENSITIVEDATAORIGIN + | TPMA_OBJECT.ADMINWITHPOLICY + | TPMA_OBJECT.RESTRICTED + | TPMA_OBJECT.SIGN_ENCRYPT, + parameters=TPMU_PUBLIC_PARMS( + keyedHashDetail=TPMS_KEYEDHASH_PARMS( + scheme=TPMT_KEYEDHASH_SCHEME( + scheme=TPM2_ALG.HMAC, + details=TPMU_SCHEME_KEYEDHASH( + hmac=TPMS_SCHEME_HASH(hashAlg=TPM2_ALG.SHA256), + ), + ) + ), + ), + ) + tb = template.marshal() + nv_template = TPM2B_NV_PUBLIC( + nvPublic=TPMS_NV_PUBLIC( + nvIndex=0x1C0000C, + nameAlg=TPM2_ALG.SHA256, + attributes=TPMA_NV.AUTHWRITE | TPMA_NV.AUTHREAD, + dataSize=len(tb), + ) + ) + tnh = self.ectx.nv_define_space(b"", nv_template, ESYS_TR.OWNER) + self.ectx.nv_write(tnh, tb) + templpub = create_ek_template(self.ectx) + _, templ, _, _, _ = self.ectx.create_primary( + TPM2B_SENSITIVE_CREATE(), templpub, ESYS_TR.ENDORSEMENT + ) + self.assertEqual(templ.publicArea.type, TPM2_ALG.KEYEDHASH) + + def test_create_ek_bad(self): + with self.assertRaises(ValueError) as e: + create_ek_template(self.ectx, TPM2_ALG.NULL) + self.assertEqual(str(e.exception), "unsupported EK algorithm null") + if __name__ == "__main__": unittest.main() diff --git a/tpm2_pytss/utils.py b/tpm2_pytss/utils.py index 8fa81f0f..c5007fcc 100644 --- a/tpm2_pytss/utils.py +++ b/tpm2_pytss/utils.py @@ -10,6 +10,8 @@ _hmac, ) from .types import * +from .ESAPI import ESAPI +from .constants import ESYS_TR, TPM2_CAP, TPM2_HT, TPM2_PT_NV, TPM2_ECC from cryptography.hazmat.primitives import constant_time as ct from cryptography.hazmat.primitives import hashes from cryptography.hazmat.backends import default_backend @@ -226,3 +228,129 @@ def unwrap( ) return s + + +def create_ek_template( + ectx: ESAPI, ekalg: TPM2_ALG = TPM2_ALG.ECC, session: ESYS_TR = ESYS_TR.NONE +) -> TPM2B_PUBLIC: + """Creates an Endorsenment Key template which when created matches the EK certificate + + The template is created according to TCG EK Credential Profile For TPM Family 2.0: + - https://trustedcomputinggroup.org/resource/tcg-ek-credential-profile-for-tpm-family-2-0/ + + Args: + ectx (ESAPI): The ESAPI instance used to look for a possible template and nonce in NV areas + ekalg: (TPM2_ALG.ECC or TPM2_ALG.RSA): Type of template to create, defaults to TPM2_ALG.ECC + session: (ESYS_TR): Optional session, for encrypt/decrypt and audit sessions, defaults to ESYS_TR.NONE + + Returns: + TPM2B_PUBLIC FIXME + """ + symmetric = TPMT_SYM_DEF_OBJECT( + algorithm=TPM2_ALG.AES, + keyBits=TPMU_SYM_KEY_BITS(aes=128), + mode=TPMU_SYM_MODE(aes=TPM2_ALG.CFB), + ) + attrs = ( + TPMA_OBJECT.FIXEDTPM + | TPMA_OBJECT.FIXEDPARENT + | TPMA_OBJECT.SENSITIVEDATAORIGIN + | TPMA_OBJECT.ADMINWITHPOLICY + | TPMA_OBJECT.RESTRICTED + | TPMA_OBJECT.DECRYPT + ) + policy = b"\x83q\x97gD\x84\xb3\xf8\x1a\x90\xcc\x8dF\xa5\xd7$\xfdR\xd7n\x06R\x0bd\xf2\xa1\xda\x1b3\x14i\xaa" + ecc_template = TPM2B_PUBLIC( + publicArea=TPMT_PUBLIC( + type=TPM2_ALG.ECC, + nameAlg=TPM2_ALG.SHA256, + objectAttributes=attrs, + authPolicy=policy, + parameters=TPMU_PUBLIC_PARMS( + eccDetail=TPMS_ECC_PARMS( + symmetric=symmetric, + scheme=TPMT_ECC_SCHEME(scheme=TPM2_ALG.NULL), + curveID=TPM2_ECC.NIST_P256, + kdf=TPMT_KDF_SCHEME(scheme=TPM2_ALG.NULL), + ) + ), + unique=TPMU_PUBLIC_ID(ecc=TPMS_ECC_POINT(x=b"\x00" * 32, y=b"\x00" * 32)), + ) + ) + rsa_template = TPM2B_PUBLIC( + publicArea=TPMT_PUBLIC( + type=TPM2_ALG.RSA, + nameAlg=TPM2_ALG.SHA256, + objectAttributes=attrs, + authPolicy=policy, + parameters=TPMU_PUBLIC_PARMS( + rsaDetail=TPMS_RSA_PARMS( + symmetric=symmetric, + scheme=TPMT_RSA_SCHEME(scheme=TPM2_ALG.NULL), + keyBits=2048, + ), + ), + unique=TPMU_PUBLIC_ID(rsa=b"\x00" * 256), + ) + ) + if ekalg == TPM2_ALG.ECC: + nvbase = 0x1C0000A + template = ecc_template + elif ekalg == TPM2_ALG.RSA: + nvbase = 0x1C00002 + template = rsa_template + else: + raise ValueError(f"unsupported EK algorithm {ekalg}") + + nvnonce = nvbase + 1 + nvtemplate = nvbase + 2 + + indices = [] + more = True + while more: + more, data = ectx.get_capability( + TPM2_CAP.HANDLES, + TPM2_HT.NV_INDEX << 24, + TPM2_PT_NV.INDEX_MAX, + session1=session, + ) + handles = list(data.data.handles) + indices.extend(handles) + + if nvtemplate in indices: + thandle = ectx.tr_from_tpmpublic(nvtemplate, session1=session) + tpub, _ = ectx.nv_read_public(thandle, session1=session) + left = tpub.nvPublic.dataSize + mtempl = b"" + while left > 0: + off = tpub.nvPublic.dataSize - left + size = 0x300 if left > 0x300 else left + data = ectx.nv_read(thandle, size, off, session2=session) + mtempl = mtempl + bytes(data) + left = left - len(data) + templ, _ = TPMT_PUBLIC.unmarshal(mtempl) + template = TPM2B_PUBLIC(publicArea=templ) + + nonce = b"" + if nvnonce in indices: + nhandle = ectx.tr_from_tpmpublic(nvnonce, session1=session) + npub, _ = ectx.nv_read_public(nhandle, session1=session) + left = npub.nvPublic.dataSize + while left > 0: + off = npub.nvPublic.dataSize - left + size = 0x300 if left > 0x300 else left + data = ectx.nv_read(nhandle, size, off, session2=session) + nonce = nonce + bytes(data) + left = left - len(data) + + if len(nonce) > 0 and template.publicArea.type == TPM2_ALG.RSA: + template.publicArea.unique.rsa = nonce + ((256 - len(nonce)) * b"\x00") + elif ( + len(nonce) > 0 + and template.publicArea.type == TPM2_ALG.ECC + and template.publicArea.parameters.eccDetail.curveID == TPM2_ECC.NIST_P256 + ): + template.publicArea.unique.ecc.x = nonce + ((32 - len(nonce)) * b"\x00") + template.publicArea.unique.ecc.y = b"\x00" * 32 + + return template