Skip to content

salrashid123/tpm-kdf

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TPM based Key Deriviation Function (KDF)

CLI and library which provides a counter mode KDF as described in NIST SP 800-108.

This CLI utilizes an TPM-based HMAC key as a basis to derive the KDF.

Library basically overrides Canonicals KDF and has been verified against NIST test vectors.


References


Parent Key

By default, parent key represented by the H2 Template.

This was done for compatibility with openssl (which by default only supports the h2)

The H2 template is also what is specified in ASN.1 Specification for TPM 2.0 Key Files

This utility also supports other parent key types (rsa_ek, ecc_ek, rsa_srk, ecc_srk) but H2 is the easiest. If you need other parent types, please see the sections below.

The following shows an example of creating the h2 parent and then an HMAC key on the TPM

printf '\x00\x00' > unique.dat
tpm2_createprimary -C o -G ecc \
    -g sha256  -c primary.ctx -a "fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda|restricted|decrypt" -u unique.dat

tpm2_create -C primary.ctx -g sha256 -G hmac -u hmac-fixed.pub -r hmac-fixed.priv
tpm2_encodeobject -C primary.ctx -u hmac-fixed.pub -r hmac-fixed.priv -o tpm-key-fixed.pem

If you want to import an external hmac key, the flow uses the same parent

echo -n "my_api_key" > hmac.key
hexkey=$(xxd -p -c 256 < hmac.key)

printf '\x00\x00' > unique.dat
tpm2_createprimary -C o -G ecc \
    -g sha256  -c primary.ctx -a "fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda|restricted|decrypt" -u unique.dat

tpm2_import -C primary.ctx -G hmac -i hmac.key -u hmac.pub -r hmac.priv
tpm2_load -C primary.ctx -u hmac.pub -r hmac.priv -c hmac.ctx
tpm2_encodeobject -C primary.ctx -u hmac.pub -r hmac.priv -o tpm-key.pem

Library

As a library, you need to import Vault's base KDF class and override it with this libarary.

The following with derive a key from "foo":

import (
	"encoding/hex"
	tpmkdf "github.com/salrashid123/tpm-kdf"
)

	keyFileBytes, err := os.ReadFile("/path/to/tpm-key.pem")

	keyLengthBits := uint32(256)
	label := []byte("foo")
	context := []byte("context")

	prf, err := tpmkdf.NewTPMPRF("/dev/tpmrm0", nil, keyFileBytes, tpmkdfpolicy.H2, nil, nil, "")

	// Derive the key using the Counter Mode KDF
	derivedKey, err := tpmkdf.CounterModeKey(
		prf,           // The pseudorandom function (HMAC-SHA256)
		nil,           // The master secret key, set this to nil since its derived from the PRF
		label,         // Optional label
		context,       // Optional context
		keyLengthBits, // Desired key length in bits
	)

	fmt.Printf("Derived Key (%d bytes): %x\n", len(derivedKey), derivedKey)

if you want the library to open and close the tpm for every call, specify the tpmPath (eg tpmPath=/dev/tpmrm0)

if you want to manage the TPM read closer externally, set tpmPath nil and set the rwc to a TPM

	rwc, err := tkdf.OpenTPM(*tpmPath)

	keyFileBytes, err := os.ReadFile("/path/to/tpm-key.pem")

	prf, err := tpmkdf.NewTPMPRF(nil, rwc, keyFileBytes, tpmkdfpolicy.H2, nil, nil, "")

	// Derive the key using the Counter Mode KDF
	derivedKey, err := tpmkdf.CounterModeKey(
		prf,           // The pseudorandom function (HMAC-SHA256)
		nil,           // The master secret key, set this to nil since its derived from the PRF
		label,         // Optional label
		context,       // Optional context
		keyLengthBits, // Desired key length in bits
	)

	fmt.Printf("Derived Key (%d bytes): %x\n", len(derivedKey), derivedKey)

CLI

As a library, provide the PEM formatted TPM private key and the length of of the data to mac.

You can get the signed and attested binary on the Releases page

./tpm-kdf  --label=foo   --context=context  \
    --keyFile=example/certs/tpm-key.pem --length=256 --tpm-path="127.0.0.1:2321
Option Description
-tpmPath Path to the TPM device (character device or a Unix socket). (default: /dev/tpmrm0)
-keyFile Path to the PEM formatted KeyFile
-length result size
-label kdf label
-context kdf context
-parentKeyType Parent type (rsa_ek, ecc_ek, h2; default: h2)
-keyPass Passphrase for the key handle
-parentPass Passphrase for the owner handle
-pcrValues PCR Bound slot:value (increasing order, comma separated)
-tpm-session-encrypt-with-name hex encoded TPM object 'name' to use with an encrypted session
-outputBase64 output as base64

The keyFile parameter here accepts a PEM formatted key as described in ASN.1 Specification for TPM 2.0 Key Files.

You can save a key in PEM format using tpm2_encodeobject or tpm2 key utility

Setup

The following sets up a sofware TPM but you can ofcourse use the real thing.

cd example/

# mkdir myvtpm
# swtpm_setup --tpmstate myvtpm --tpm2 --create-ek-cert

swtpm socket --tpmstate dir=myvtpm \
   --tpm2 --server type=tcp,port=2321 --ctrl type=tcp,port=2322 --flags not-need-init,startup-clear --log level=5

export TPM2TOOLS_TCTI="swtpm:port=2321"

### if you wanted to generate a new key, then run the following.
###  the example/ folder contains key files with this passphrase.

echo -n "my_api_key" > hmac.key
hexkey=$(xxd -p -c 256 < hmac.key)

printf '\x00\x00' > unique.dat
tpm2_createprimary -C o -G ecc \
    -g sha256  -c primary.ctx -a "fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda|restricted|decrypt" -u unique.dat

tpm2_import -C primary.ctx -G hmac -i hmac.key -u hmac.pub -r hmac.priv
tpm2_flushcontext -t && tpm2_flushcontext -s && tpm2_flushcontext -l
tpm2_load -C primary.ctx -u hmac.pub -r hmac.priv -c hmac.ctx
tpm2_encodeobject -C primary.ctx -u hmac.pub -r hmac.priv -o tpm-key.pem

### with cli
./tpm-kdf  --label=foo --context=context \
   --keyFile=example/certs/tpm-key.pem --length=256 --tpm-path="127.0.0.1:2321"

### with library
cd example/
go run counter/main.go -in certs/tpm-key.pem --tpm-path="127.0.0.1:2321"  

Policy

Password

If you want to setup a key with auth, create an authsession and policypassword after the primary

printf '\x00\x00' > unique.dat
tpm2_createprimary -C o -G ecc  -g sha256  -c primary.ctx -a "fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda|restricted|decrypt" -u unique.dat

tpm2_startauthsession -S session.dat
tpm2_policypassword -S session.dat -L policy.dat
tpm2_flushcontext session.dat

echo -n "my_api_key" > hmac.key
hexkey=$(xxd -p -c 256 < hmac.key)

tpm2_import -C primary.ctx -G hmac -i hmac.key -u hmac.pub -r hmac.priv -L policy.dat
tpm2_flushcontext -t && tpm2_flushcontext -s && tpm2_flushcontext -l
tpm2_load -C primary.ctx -u hmac.pub -r hmac.priv -c hmac.ctx
tpm2_encodeobject -C primary.ctx -u hmac.pub -r hmac.priv -o tpm-key.pem 
tpm2_flushcontext -t && tpm2_flushcontext -s && tpm2_flushcontext -l

## with cli
./tpm-kdf   --label=foo  \
   --keyFile=example/certs_policy_password/tpm-key.pem --keyPass=testpswd --length=256 --tpm-path="127.0.0.1:2321"

## with library
cd example/
go run policy_password/main.go -in certs_policy_password/tpm-key.pem --password=testpswd --tpm-path="127.0.0.1:2321"
PCR Policy

If you want to bind the hmac key to a PCR value,

$ tpm2_pcrread sha256:23
  sha256:
    23: 0x0000000000000000000000000000000000000000000000000000000000000000
$ tpm2_pcrextend 23:sha256=0x0000000000000000000000000000000000000000000000000000000000000000
$ tpm2_pcrread sha256:23
  sha256:
    23: 0xF5A5FD42D16A20302798EF6ED309979B43003D2320D9F0E8EA9831A92759FB4B

printf '\x00\x00' > unique.dat
tpm2_createprimary -C o -G ecc  -g sha256  -c primary.ctx -a "fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda|restricted|decrypt" -u unique.dat

tpm2_pcrread sha256:23
tpm2_startauthsession -S session.dat
tpm2_policypcr -S session.dat -l sha256:23  -L policy.dat
tpm2_flushcontext session.dat

echo -n "my_api_key" > hmac.key
hexkey=$(xxd -p -c 256 < hmac.key)

tpm2_import -C primary.ctx -G hmac -i hmac.key -u hmac.pub -r hmac.priv -L policy.dat
tpm2_flushcontext -t && tpm2_flushcontext -s && tpm2_flushcontext -l
tpm2_load -C primary.ctx -u hmac.pub -r hmac.priv -c hmac.ctx
tpm2_flushcontext -t && tpm2_flushcontext -s && tpm2_flushcontext -l	
tpm2_encodeobject -C primary.ctx -u hmac.pub -r hmac.priv -o tpm-key.pem

### with cli
./tpm-kdf  --label=foo  \
   --keyFile=example/certs_policy_pcr/tpm-key.pem --pcrValues=23:F5A5FD42D16A20302798EF6ED309979B43003D2320D9F0E8EA9831A92759FB4B \
    --length=256 --tpm-path="127.0.0.1:2321"

## with library
cd example/
go run policy_pcr/main.go -in certs_policy_pcr/tpm-key.pem --pcr=23 --tpm-path="127.0.0.1:2321"   

Other policy times can get encoded into the TPM but i'm just waiting for the specs to finalize. For now, see Reconstruct Policy using command parameters

Using EK Parent

You can also transfer a file from one TPM to another using duplicate select.

If you use this, the parent can be an EK_RSA and the key itself with pcr and password policies.

Basically, the key can be bound using

For example,

wget https://github.com/salrashid123/tpmcopy/releases/download/v0.5.2/tpmcopy_0.5.2_linux_amd64

tpm2_createek -c primaryB.ctx -G rsa -u ekB.pub -Q
tpm2_readpublic -c primaryB.ctx -o ekpubB.pem -f PEM -Q

### TPM-B "/dev/tpmrm0"
export TPMB="127.0.0.1:2321"

./tpmcopy_0.5.2_linux_amd64 --mode publickey --parentKeyType=rsa_ek -tpmPublicKeyFile=public.pem --tpm-path=$TPMB

echo -n "my_api_key" > hmac.key
hexkey=$(xxd -p -c 256 < hmac.key)
./tpmcopy_0.5.2_linux_amd64 --mode duplicate --keyType=hmac --hashScheme=sha256 --secret=hmac.key \
   --password=bar -tpmPublicKeyFile=public.pem -out=out.json

./tpmcopy_0.5.2_linux_amd64 --mode import --parentKeyType=rsa_ek --in=out.json --out=tpm-key.pem  --tpm-path=$TPMB

### via cli

./tpm-kdf  --label=foo  \
   --keyFile=example/certs_ek/tpm-key.pem --parentKeyType=rsa_ek --keyPass=bar \
    --length=256 --tpm-path="127.0.0.1:2321"

### via library

go run policy_duplicate/main.go --in="certs_ek/tpm-key.pem" \
   --password=bar --tpm-path= "127.0.0.1:2321"

Encrypted TPM Sessions

If you want to enable TPM Encrypted sessions, you should provide the "name" of a trusted key on the TPM for each call.

A trusted key can only be the RSA-EK Key. You can get the name using tpm2_tools:

tpm2_createek -c ekprimary.ctx -G rsa -u ek.pub -Q
tpm2_readpublic -c ekprimary.ctx -o ek.pem -n name.bin -f pem -Q
xxd -p -c 100 name.bin 
  000b84c403b83cdf6472d9e84f87169cac746be95c1182ed61bffd12f18adaea8e63

Then use the hex value returned in the --tpm-session-encrypt-with-name= argument.

For example:

   --tpm-session-encrypt-with-name=000b84c403b83cdf6472d9e84f87169cac746be95c1182ed61bffd12f18adaea8e63

# for example
 go run policy/main.go \
    --tpm-session-encrypt-with-name=000b84c403b83cdf6472d9e84f87169cac746be95c1182ed61bffd12f18adaea8e63

For example, most TPM calls are encrypted.

start

images/hmac_start.png

complete

images/hmac_complete.png

If you do not specify an encryption name, the default rsa-ek is recalled and used anyway.

Latency

Its certainly expected that there is significant latency in the KDF generation for keys (proportional to the number of hmac calls are made)

In an unscientific test, the TPM based operations has far larger latency:

$ time ./counter/main 
Derived Key (32 bytes): aa8c90530bb6c6e3e7d9047dfe4bd750c4c0c7a6206e4b75905a62a27b479333

real	0m0.443s
user	0m0.050s
sys	0m0.028s

$ time ./baseline/main 
Derived Key (32 bytes): aa8c90530bb6c6e3e7d9047dfe4bd750c4c0c7a6206e4b75905a62a27b479333

real	0m0.031s
user	0m0.012s
sys	0m0.023s

Testing

Using swtpm

rm -rf /tmp/myvtpm && mkdir /tmp/myvtpm
swtpm_setup --tpmstate /tmp/myvtpm --tpm2 --create-ek-cert
swtpm socket --tpmstate dir=/tmp/myvtpm --tpm2 --server type=tcp,port=2321 --ctrl type=tcp,port=2322 --flags not-need-init,startup-clear

# then specify "127.0.0.1:2321"  as the TPM device path in the examples
# and for tpm2_tools, export the following var
export TPM2TOOLS_TCTI="swtpm:port=2321"

go test -v

You can also verify using canonical's default library and with python:

$ go run counter/main.go 
Derived Key (32 bytes): aa8c90530bb6c6e3e7d9047dfe4bd750c4c0c7a6206e4b75905a62a27b479333

$ go run baseline/main.go 
Derived Key (32 bytes): aa8c90530bb6c6e3e7d9047dfe4bd750c4c0c7a6206e4b75905a62a27b479333

$ python3 baseline/kdf.py 
aa8c90530bb6c6e3e7d9047dfe4bd750c4c0c7a6206e4b75905a62a27b479333

About

TPM based Key Deriviation Function (KDF)

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors