wiki:tpm

Trusted Protection Module

A Trusted Platform Module (TPM) is a small piece of hardware designed to provide various security functionalities. It offers numerous features, such as storing secrets, ‘measuring’ boot, and may act as an external cryptographic engine.

The Trusted Computing Group (TCG) delivers a document called TPM Interface Specifications (TIS) which describes the architecture of such devices and how they are supposed to behave as well as various details around the concepts. Additionally they provide a Library Specification.

TPM chips are either compliant with the initial specification or the v2.0+ specification: See TPM v1.2 vs TPM 2.0 for details.

Note that the information here is not meant to be a full guide to Trusted Platform Modules - this should be considered a brief overview.

See also:

Microchip ATTPM20P

Gateworks has an optional TPM on the Venice and Malibu family SBCs:

  • Malibu GW8901
  • Venice:
    • GW74xx (revision B+)
    • GW73xx (revision F+)
    • GW72xx (revision F+)
    • GW71xx (revision E+)

The TPM used is a Microchip ATTPM20P-H6MA1-10 TPM connected to the SPI bus and is compliant to the Trusted Computing Group (TCG) Trusted Platform Module (TPM) Version 2.0

This provides cryptographic support for:

  • HMAC
  • AES-128
  • SHA-1
  • SHA-256
  • ECC BN_P256, ECCNIST_P256
  • RSA 1024-2048 bit keys
  • 16KB of user accessible NVM
  • storage for up to 10x 2048-bit keys

Notes:

  • the ATTPM20P supports 1 active PCR bank at a time and defaults to SHA1 (see below on how to switch banks)

PCR Values

A cryptographic hash (sometimes called a 'digest') is a kind of 'signature' for a set of data. For example the SHA-256 algorithm can be used to generate an almost-unique 256-bit (32-byte) signature (aka 'hash' or 'digest') for a file. Note that this signature/hash/digest is not 'encryption' - it is a one way cryptographic function and is a fixed size for any source of data.

Starting from a root of trust (typically the SoC BOOT ROM) each software stage during the boot process is supposed to to some measurements and store them in a safe place. A 'measure' is just a signature/hash/digest of a memory region. This value can be sent to the TPM as a measure which will merge with measurement with the previous ones.

The hardware feature used to store and merge these measurements is called Platform Configuration Registers (PCR). At power-up a PCR is set to a known value (typically either 0x00's or 0xff's) and sending a new value to the TPM is called 'extending a PCR' because the chosen register will extend its value with the one received. This way a PCR can only evolve in one direction and never go back unless the platform is reset. Each software stage will be in charge of extending a set of PCRs with digests of the next software stage. Once in Linux for example user software may ask the TPM to deliver its secrects but the only way to get them is having all PCRs matching a known pattern which can only be obtained by extending the PCRs in the right order with the right digets.

If the stored PCR values in the TPM do not match the currently booting system PCRs, access will not be granted. For example, someone trying to boot a Ubuntu Live CD would not be able to access the TPM key as the PCRs generated from the original disk and stored in the TPM will not match the newly generated PCRs from the boot CD. PCRs use hashing and thus any new value is concatenated with the old and then hashed. This new hash will replace the old hash. The definition of each specific PCR register can be found online.

PCR Banks

The TCG "PC Client Platform Profile for TPM 2.0" specification lists two hash algorithms as mandatory to implement (SHA1 and SHA256) and it is permitted for a TPM to support fewer active banks than algorithms. The firmware chooses which ones to actually enable at boot time.

In general, you can configure a TPM to have multiple PCR banks active. When TPM measurements are performed it should be done on all active PCR banks. Note the 'tpm pcrread' command shows available banks and PCR values for those banks that are active.

Note that the ATTPM20P TPM allows 1 bank to be allocated at a time and defaults to SHA1 but you can switch it to SHA256.

You can change active banks using the 'tpm pcrallocate' command from the tpm2-tools package in Linux:

# switch from SHA1 bank to SHA256
tpm2 pcrallocate sha1:none+sha256:all
tpm2 pcrread # notice the sha256 bank is active and sha1 is not
# switch from SHA256 bank to SHA1
tpm2 pcrallocate sha256:none+sha1:all
tpm2 pcrread # notice the sha1 bank is active and sha256 is not

TPM Keys

A TPM can securely store cryptographic keys that are specific to the host system and provide restricted access to the stored keys and secrets.

You can never read out private keys from the TPM once stored but you can sign something and verify a signed object with the private key and verify that against the public key using the TPM. This way your private keys remain secure in the TPM even if the host system is compromised.

There are two categories of keys in a TPM:

  1. Non-migratable keys; unique keys bound to a specific TPM that can not be migrated outside of the TPM.
  2. Migratable keys; keys that are not specific to a TPM and can be migrated outside with he right owner authorization.

Migratable keys

Migratable keys are keys that are not specific to a TPM and can be migrated outside (public and private portion) with the right owner authorization. Primary keys for restricted storage, encryption and signing can be generated from the storage primary seed and can be used to encrypt other non-root keys.

The tpm2 createprimary command is used to derive primary keys from the seed of corresponding hierarchy and the tpm2 evictcontrol command can make the key persistent.

NVM is the general pupose memory in a TPM that can be used to store user credentials, user data and certificates. Each area in NVRAM is addressed by a handle/index. There are a few reserved handles as defined in the TCG specification for accessing the TPM manufacture-installed Endorsement Key (EK) certificates:

  • handle 0x1c00002 stores the RSA EK certificate
  • handle 0x1c0000a stores the ECC EK certificate

To see a list of available persistent handles use tpm2_getcap handles-persistent

A general flow for loading a key into the TPM: (arguments needed for below commands specific to each application )

tpm2_createpolicy  # Create PCR Policy
tpm2_createprimary  # Create primary TPM object
tpm2_create  # Create TPM Object with Secret
tpm2_load  # Load object into the TPM
tpm2_evictcontrol  # Make TPM Object Persistant
rm files #remove your working files

Measured Boot

The concept of measured boot utilizes a TPM to used measured values of boot stages to extend PCR's that are used to lock a secret. The secret can not be revealed unless the PCR values match what was used when the secret was stored and the only way that can happen is if the measurements are the same. So if the measurements used in the PCR's cover each boot phase your secret is secure and can never be pulled from the TPM if access to your device is obtained.

For example consider the following:

  • boot firmware is loaded by an SoC BOOT_ROM and authenticated by fuses and signature (the BOOT_ROM would be the root of trust here)
  • boot firmware code is measured into a PCR; a hash algorithm can be run on the code itself and the PCR extended with that hash
  • a key used to unlock/decrypt the root filesystem is locked by that PCR. Once the key is read and used the PCR is extended again which effectively 'seals' the key from being read further down the chain

Examples:

  • using a Venice board with a TPM
    • Hide a 128byte secret key in the TPM based on hash of boot firmware and first 64MB of user partition (which could be your partition table and FIT image containing a kernel+dtb+ramdisk used to decrypt a filesystem that starts at 64MB)
      • via Linux:
        • store the key
          #
          # config
          #
          DEV=/dev/mmcblk2 # emmc on Venice
          KEY_HANDLE=0x81234567 # TPM2 Owner Persistent Handle Range: 0x81000000 - 0x81800000
          KEY=fs.key # a 128 byte key secrect (ie dd if=/dev/urandom of=fs.key bs=1 count=128)
          
          #
          # extend PCRs (measure the boot artifacts)
          #
          # PCR0: extend with sha1 hash of emmc boot0:0MB-4MB (boot firmware including env)
          dd if=${DEV}boot0 of=data bs=1M count=4
          tpm2 pcrextend 0:sha1=$(sha1sum data | cut -d" " -f1)
          # PCR8: extend with sha1 hash of emmc user:0MB-16M (ie partition table and FIT image)
          dd if=$DEV of=data bs=1M count=16
          tpm2 pcrextend 8:sha1=$(sha1sum data | cut -d" " -f1)
          # show current PCR's
          tpm2 pcrread
          
          #
          # store key
          #
          # evict any existing persistent object at $KEY_HANDLE
          tpm2 evictcontrol -C o -c $KEY_HANDLE
          # create a policy that depends on PCR0 and PCR8
          tpm2 createpolicy --policy-pcr -l sha1:0,8 -L policy.digest
          tpm2 createprimary -g sha256 -G rsa -c primary.context
          # create an object containing the key
          hexdump -C $KEY # for debugging
          tpm2 create -g sha256 -u obj.pub -r obj.priv -C primary.context -L policy.digest -a "noda|adminwithpolicy|fixedparent|fixedtpm" -i $KEY
          # load it
          tpm2 load -C primary.context -u obj.pub -r obj.priv -c load.context
          # make it it persistent at $KEY_HANDLE
          tpm2 evictcontrol -C o -c load.context $KEY_HANDLE
          # clean up our temporary files
          rm -f policy.digest primary.context obj.pub obj.priv load.context fs.key
          
        • retrieve the key (after a power cycle)
          #
          # config
          #
          DEV=/dev/mmcblk2 # emmc on Venice
          KEY_HANDLE=0x81234567 # TPM2 Owner Persistent Handle Range: 0x81000000 - 0x81800000
          KEY=fs.key # the key we will read from the TPM
          
          #
          # extend PCRs (measure the boot artifacts)
          #
          # PCR0: extend with sha1 hash of emmc boot0:0MB-4MB (boot firmware including env)
          dd if=${DEV}boot0 of=data bs=1M count=4
          tpm2 pcrextend 0:sha1=$(sha1sum data | cut -d" " -f1)
          # PCR8: extend with sha1 hash of emmc user:0MB-16M (ie partition table and FIT image)
          dd if=$DEV of=data bs=1M count=16
          tpm2 pcrextend 8:sha1=$(sha1sum data | cut -d" " -f1)
          # show current PCR's (which should equal the values when the key was stored as long as the hashes the PCR's were extended with remain the same)
          tpm2 pcrread
          
          #
          # get key
          #
          # unseal key at $KEY_HANDLE
          tpm2 unseal -c $KEY_HANDLE -p pcr:sha1:0,8 -o $KEY
          hexdump -C $KEY # for debugging; should equal the key that was stored
          # reseal it after use by extending the PCR's so that it can't be stolen if system is later compromised
          rm -f $KEY
          tpm2 pcrextend 0:sha1=0000000000000000000000000000000000000000
          tpm2 pcrextend 8:sha1=0000000000000000000000000000000000000000
          

Note that while U-Boot's TPM support is not as full featured as Linux you can still read and extend PCR's in U-Boot thus perform some measurement and verification there as well:

# initialize the tpm
tpm2 init && tpm2 startup TPM2_SU_CLEAR && tpm2 self_test full && tpm2 self_test continue

#
# extend PCRs (measure the boot artifacts)
#
# PCR0: extend with sha1 hash of emmc boot0:0MB-4MB (boot firmware including env)
mmc dev 2 1 && mmc read $loadaddr 0 0x2000 && hash sha1 $loadaddr $filesize *0x40200000
tpm2 pcr_extend 0 0x40200000 sha1
# PCR8: extend with sha1 hash of emmc user:0MB-16M (ie partition table and FIT image)
mmc dev 2 0 && mmc read $loadaddr 0x0 0x8000 && hash sha1 $loadaddr $filesize *0x40200000
tpm2 pcr_extend 8 0x40200000 sha1
# show current PCR's (which should equal the values when the key was stored as long as the hashes the PCR's were extended with remain the same)
tpm2 pcr_read 0 0x40200000 sha1
tpm2 pcr_read 8 0x40200000 sha1

note the firmware hashes and thus PCR's will be different (which will keep the key inaccessible) if there are any changes to the hash values used to extend the PCR (meaning any changes to flash/memory areas you hash over)

Note that the above example uses the SHA1 PCR bank which is the default bank enabled on the ATTPM20P. You can switch to the SHA256 bank if desired (only one bank can be active at a time on the ATTMP20P) - see above

Linux

Driver

The TIS compliant TPM devices are supported by the TCG SPI Linux driver:

  • drivers/char/tpm/ (CONFIG_TCG_TIS_CORE, CONFIG_TCG_TIS, CONFIG_TCG_TIS_SPI)

This driver provides access via:

  • /dev/tpm0
  • /dev/tpmrm0

Software Stack

A solid TPM 2.0 software stack is available for Linux:

Examples:

  • Install packages
    apt install tpm2-tools tpm2-abrmd
    
  • Show tpm capabilities/properties:
    root@jammy-malibu:~# tpm2_getcap properties-fixed
    TPM2_PT_FAMILY_INDICATOR:
      raw: 0x322E3000
      value: "2.0"
    TPM2_PT_LEVEL:
      raw: 0
    TPM2_PT_REVISION:
      raw: 0x77
      value: 1.19
    TPM2_PT_DAY_OF_YEAR:
      raw: 0x42
    TPM2_PT_YEAR:
      raw: 0x7DE
    TPM2_PT_MANUFACTURER:
      raw: 0x4D434850
      value: "MCHP"
    TPM2_PT_VENDOR_STRING_1:
      raw: 0x0
      value: ""
      .....
    
  • Read the PCR Values:
    root@jammy-venice:~# tpm2_pcrread
      sha1:
        0 : 0x0000000000000000000000000000000000000000
        1 : 0x0000000000000000000000000000000000000000
        2 : 0x0000000000000000000000000000000000000000
        3 : 0x0000000000000000000000000000000000000000
        4 : 0x0000000000000000000000000000000000000000
        5 : 0x0000000000000000000000000000000000000000
        6 : 0x0000000000000000000000000000000000000000
        7 : 0x0000000000000000000000000000000000000000
        8 : 0x0000000000000000000000000000000000000000
        9 : 0x0000000000000000000000000000000000000000
        10: 0x0000000000000000000000000000000000000000
        11: 0x0000000000000000000000000000000000000000
        12: 0x0000000000000000000000000000000000000000
        13: 0x0000000000000000000000000000000000000000
        14: 0x0000000000000000000000000000000000000000
        15: 0x0000000000000000000000000000000000000000
        16: 0x0000000000000000000000000000000000000000
        17: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
        18: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
        19: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
        20: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
        21: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
        22: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
        23: 0x0000000000000000000000000000000000000000
      sha256:
    
    • the above shows that the sha1 bank is active and sha256 is supported but not active. You can switch to the SHA256 bank if desired (only one bank can be active at a time on the ATTMP20P) - see above
  • Clearing the TPM
    tpm2_clear
    
  • Generate Random Number
    tpm2_getrandom --hex 8
    

Please also read: Gateworks TPM Wiki when used with Secure Boot

U-Boot

U-Boot has TPM support as well:

  • drivers/tpm/tpm2_tis_spi.c (CONFIG_TPM,CONFIG_TPM2_TIS_SPI)

Usage Example:

u-boot=> tpm device
device 0: tpm@0 v2.0: VendorID 0x1114, DeviceID 0x3205, RevisionID 0x01 [open]
u-boot=> tpm info
tpm@0 v2.0: VendorID 0x1114, DeviceID 0x3205, RevisionID 0x01 [open]
u-boot=> tpm init
u-boot=> tpm startup TPM2_SU_CLEAR
u-boot=> tpm self_test full                  
u-boot=> tpm self_test continue

At this point you can pursue measured boot byt extending the PCR as needed:

u-boot=> tpm extend 0 $loadaddr # extend PCR 0 using digest loaded to $loadaddr

See also:

Seee also:

Additional Resources

Last modified 2 months ago Last modified on 05/30/2024 04:41:55 PM
Note: See TracWiki for help on using the wiki.