Source code for tpm2_pytss.tsskey

# SPDX-License-Identifier: BSD-2

import warnings
from ._libtpm2_pytss import lib
from .types import *
from .constants import TPM2_ECC, TPM2_CAP, ESYS_TR
from asn1crypto.core import ObjectIdentifier, Sequence, Boolean, OctetString, Integer
from asn1crypto import pem


_parent_rsa_template = TPMT_PUBLIC(
    type=TPM2_ALG.RSA,
    nameAlg=TPM2_ALG.SHA256,
    objectAttributes=TPMA_OBJECT.USERWITHAUTH
    | TPMA_OBJECT.RESTRICTED
    | TPMA_OBJECT.DECRYPT
    | TPMA_OBJECT.NODA
    | TPMA_OBJECT.FIXEDTPM
    | TPMA_OBJECT.FIXEDPARENT
    | TPMA_OBJECT.SENSITIVEDATAORIGIN,
    authPolicy=b"",
    parameters=TPMU_PUBLIC_PARMS(
        rsaDetail=TPMS_RSA_PARMS(
            symmetric=TPMT_SYM_DEF_OBJECT(
                algorithm=TPM2_ALG.AES,
                keyBits=TPMU_SYM_KEY_BITS(aes=128),
                mode=TPMU_SYM_MODE(aes=TPM2_ALG.CFB),
            ),
            scheme=TPMT_RSA_SCHEME(scheme=TPM2_ALG.NULL),
            keyBits=2048,
            exponent=0,
        ),
    ),
)

_parent_ecc_template = TPMT_PUBLIC(
    type=TPM2_ALG.ECC,
    nameAlg=TPM2_ALG.SHA256,
    objectAttributes=TPMA_OBJECT.USERWITHAUTH
    | TPMA_OBJECT.RESTRICTED
    | TPMA_OBJECT.DECRYPT
    | TPMA_OBJECT.NODA
    | TPMA_OBJECT.FIXEDTPM
    | TPMA_OBJECT.FIXEDPARENT
    | TPMA_OBJECT.SENSITIVEDATAORIGIN,
    authPolicy=b"",
    parameters=TPMU_PUBLIC_PARMS(
        eccDetail=TPMS_ECC_PARMS(
            symmetric=TPMT_SYM_DEF_OBJECT(
                algorithm=TPM2_ALG.AES,
                keyBits=TPMU_SYM_KEY_BITS(aes=128),
                mode=TPMU_SYM_MODE(aes=TPM2_ALG.CFB),
            ),
            scheme=TPMT_ECC_SCHEME(scheme=TPM2_ALG.NULL),
            curveID=TPM2_ECC.NIST_P256,
            kdf=TPMT_KDF_SCHEME(scheme=TPM2_ALG.NULL),
        ),
    ),
)

_rsa_template = TPMT_PUBLIC(
    type=TPM2_ALG.RSA,
    nameAlg=TPM2_ALG.SHA256,
    objectAttributes=TPMA_OBJECT.USERWITHAUTH
    | TPMA_OBJECT.SIGN_ENCRYPT
    | TPMA_OBJECT.DECRYPT
    | TPMA_OBJECT.NODA
    | TPMA_OBJECT.FIXEDTPM
    | TPMA_OBJECT.FIXEDPARENT
    | TPMA_OBJECT.SENSITIVEDATAORIGIN,
    authPolicy=b"",
    parameters=TPMU_PUBLIC_PARMS(
        rsaDetail=TPMS_RSA_PARMS(
            symmetric=TPMT_SYM_DEF_OBJECT(algorithm=TPM2_ALG.NULL),
            scheme=TPMT_RSA_SCHEME(scheme=TPM2_ALG.NULL),
        ),
    ),
)

_ecc_template = TPMT_PUBLIC(
    type=TPM2_ALG.ECC,
    nameAlg=TPM2_ALG.SHA256,
    objectAttributes=TPMA_OBJECT.USERWITHAUTH
    | TPMA_OBJECT.SIGN_ENCRYPT
    | TPMA_OBJECT.NODA
    | TPMA_OBJECT.FIXEDTPM
    | TPMA_OBJECT.FIXEDPARENT
    | TPMA_OBJECT.SENSITIVEDATAORIGIN,
    authPolicy=b"",
    parameters=TPMU_PUBLIC_PARMS(
        eccDetail=TPMS_ECC_PARMS(
            symmetric=TPMT_SYM_DEF_OBJECT(algorithm=TPM2_ALG.NULL),
            scheme=TPMT_ECC_SCHEME(scheme=TPM2_ALG.NULL),
            kdf=TPMT_KDF_SCHEME(scheme=TPM2_ALG.NULL),
        ),
    ),
)

_loadablekey_oid = ObjectIdentifier("2.23.133.10.1.3")

# _BooleanOne is used to encode True in the same way as tpm2-tss-engine
class _BooleanOne(Boolean):
    def set(self, value):
        self._native = bool(value)
        self.contents = b"\x00" if not value else b"\x01"
        self._header = None
        if self._trailer != b"":
            self._trailer = b""


[docs]class TSSPrivKey(object): """TSSPrivKey is class to create/load keys for/from tpm2-tss-engine / tpm2-openssl. Note: Most users should use create_rsa/create_ecc together with to_pem and from_pem together with load. """ class _tssprivkey_der(Sequence): _fields = [ ("type", ObjectIdentifier), ("empty_auth", _BooleanOne, {"explicit": 0, "optional": True}), ("parent", Integer), ("public", OctetString), ("private", OctetString), ] def __init__(self, private, public, empty_auth=True, parent=lib.TPM2_RH_OWNER): """Initialize TSSPrivKey using raw values. Args: private (TPM2B_PRIVATE): The private part of the TPM key. public (TPM2B_PUBLIC): The public part of the TPM key. empty_auth (bool): Defines if the authorization is a empty password, default is True. parent (int): The parent of the key, either a persistent key handle or TPM2_RH_OWNER, default is TPM2_RH_OWNER. """ self._private = private self._public = public self._empty_auth = bool(empty_auth) self._parent = parent @property def private(self): """TPM2B_PRIVATE: The private part of the TPM key.""" return self._private @property def public(self): """TPM2B_PUBLIC: The public part of the TPM key.""" return self._public @property def empty_auth(self): """bool: Defines if the authorization is a empty password.""" return self._empty_auth @property def parent(self): """int: Handle of the parent key.""" return self._parent
[docs] def to_der(self): """Encode the TSSPrivKey as DER encoded ASN.1. Returns: Returns the DER encoding as bytes. """ seq = self._tssprivkey_der() seq["type"] = _loadablekey_oid.native seq["empty_auth"] = self.empty_auth seq["parent"] = self.parent pub = self.public.marshal() seq["public"] = pub priv = self.private.marshal() seq["private"] = priv return seq.dump()
[docs] def to_pem(self): """Encode the TSSPrivKey as PEM encoded ASN.1. Returns: Returns the PEM encoding as bytes. """ der = self.to_der() return pem.armor("TSS2 PRIVATE KEY", der)
@staticmethod def _getparenttemplate(ectx): more = True al = list() while more: more, data = ectx.get_capability(TPM2_CAP.ALGS, 0, lib.TPM2_MAX_CAP_ALGS) algs = data.data.algorithms for i in range(0, algs.count): al.append(algs.algProperties[i].alg) if TPM2_ALG.ECC in al: return _parent_ecc_template elif TPM2_ALG.RSA in al: return _parent_rsa_template return None @staticmethod def _getparent(ectx, keytype, parent): if parent == lib.TPM2_RH_OWNER: template = TSSPrivKey._getparenttemplate(ectx) else: return ectx.tr_from_tpmpublic(parent) if template is None: raise RuntimeError("Unable to find supported parent key type") inpub = TPM2B_PUBLIC(publicArea=template) phandle, _, _, _, _ = ectx.create_primary( primary_handle=ESYS_TR.RH_OWNER, in_sensitive=TPM2B_SENSITIVE_CREATE(), in_public=inpub, outside_info=TPM2B_DATA(), creation_pcr=TPML_PCR_SELECTION(), session1=ESYS_TR.PASSWORD, ) return phandle
[docs] def load(self, ectx, password=None): """Load the TSSPrivKey. Args: ectx (ESAPI): The ESAPI instance to use for loading the key. password (bytes): The password of the TPM key, default is None. Returns: An ESYS_TR handle. """ if not password and not self.empty_auth: raise RuntimeError("no password specified but it is required") elif password and self.empty_auth: warnings.warn("password specified but empty_auth is true") phandle = self._getparent(ectx, self.public.publicArea.type, self.parent) handle = ectx.load(phandle, self.private, self.public) ectx.tr_set_auth(handle, password) return handle
[docs] @classmethod def create(cls, ectx, template, parent=lib.TPM2_RH_OWNER, password=None): """Create a TssPrivKey using a template. Note: Most users should use the create_rsa or create_ecc methods. Args: ectx (ESAPI): The ESAPI instance to use for creating the key. template (TPM2B_PUBLIC): The key template. parent (int): The parent of the key, default is TPM2_RH_OWNER. password (bytes): The password to set for the key, default is None. Returns: Returns a TSSPrivKey instance with the created key. """ insens = TPM2B_SENSITIVE_CREATE() emptyauth = True if password: insens.sensitive.userAuth = password emptyauth = False phandle = cls._getparent(ectx, template.type, parent) private, public, _, _, _ = ectx.create( parent_handle=phandle, in_sensitive=insens, in_public=TPM2B_PUBLIC(publicArea=template), outside_info=TPM2B_DATA(), creation_pcr=TPML_PCR_SELECTION(), ) return cls(private, public, emptyauth, parent)
[docs] @classmethod def create_rsa( cls, ectx, keyBits=2048, exponent=0, parent=lib.TPM2_RH_OWNER, password=None ): """Create a RSA TssPrivKey using a standard RSA key template. Args: ectx (ESAPI): The ESAPI instance to use for creating the key. keyBits (int): Size of the RSA key, default is 2048. exponent (int): The exponent to use for the RSA key, default is 0 (TPM default). parent (int): The parent of the key, default is TPM2_RH_OWNER. password (bytes): The password to set for the key, default is None. Returns: Returns a TSSPrivKey instance with the created RSA key. """ template = _rsa_template template.parameters.rsaDetail.keyBits = keyBits template.parameters.rsaDetail.exponent = exponent return cls.create(ectx, template, parent, password)
[docs] @classmethod def create_ecc( cls, ectx, curveID=TPM2_ECC.NIST_P256, parent=lib.TPM2_RH_OWNER, password=None ): """Create an ECC TssPrivKey using a standard ECC key template. Args: ectx (ESAPI): The ESAPI instance to use for creating the key. curveID (int): The ECC curve to be used, default is TPM2_ECC.NIST_P256. parent (int): The parent of the key, default is TPM2_RH_OWNER. password (bytes): The password to set for the key, default is None. Returns: Returns a TSSPrivKey instance with the created ECC key. """ template = _ecc_template template.parameters.eccDetail.curveID = curveID return cls.create(ectx, template, parent, password)
[docs] @classmethod def from_der(cls, data): """Load a TSSPrivKey from DER ASN.1. Args: data (bytes): The DER encoded ASN.1. Returns: Returns a TSSPrivKey instance. """ seq = cls._tssprivkey_der.load(data) if seq["type"].native != _loadablekey_oid.native: raise TypeError("unsupported key type") empty_auth = seq["empty_auth"].native parent = seq["parent"].native public, _ = TPM2B_PUBLIC.unmarshal(bytes(seq["public"])) private, _ = TPM2B_PRIVATE.unmarshal(bytes(seq["private"])) return cls(private, public, empty_auth, parent)
[docs] @classmethod def from_pem(cls, data): """Load a TSSPrivKey from PEM ASN.1. Args: data (bytes): The PEM encoded ASN.1. Returns: Returns a TSSPrivKey instance. """ pem_type, _, der = pem.unarmor(data) if pem_type != "TSS2 PRIVATE KEY": raise TypeError("unsupported PEM type") return cls.from_der(der)