Skip to content

Instantly share code, notes, and snippets.

@salrashid123
Last active June 2, 2024 18:24
Show Gist options
  • Save salrashid123/9ee5e02d5991c8d53717bd9a179a65b0 to your computer and use it in GitHub Desktop.
Save salrashid123/9ee5e02d5991c8d53717bd9a179a65b0 to your computer and use it in GitHub Desktop.
tpm2_hmac with pcr policy

seal an external hmac key to a tpm with a PCR policy

export secret="change this password to a secret"
export plain="foo"
echo -n $secret > hmac.key
hexkey=$(xxd -p -c 256 < hmac.key)
echo $hexkey
echo -n $plain > data.in
openssl dgst -sha256 -mac hmac -macopt hexkey:$hexkey data.in


tpm2_createprimary -G rsa -g sha256 -C o -c primary.ctx

tpm2_startauthsession -S session.dat
tpm2_pcrread sha256:23 -o pcr23_val.bin
tpm2_policypcr -S session.dat -l sha256:23 -f pcr23_val.bin -L policy.dat
tpm2_flushcontext session.dat

tpm2_import -C primary.ctx -G hmac -i hmac.key -u hmac.pub -r hmac.priv -L policy.dat
tpm2_load -C primary.ctx -u hmac.pub -r hmac.priv -c hmac.ctx 

tpm2_startauthsession --policy-session -S session.dat
tpm2_pcrread sha256:23 -o pcr23_val.bin
tpm2_policypcr -S session.dat -l sha256:23 -f pcr23_val.bin -L policy.dat

echo -n $plain > hmac_input.txt 
tpm2_hmac -g sha256 -c hmac.ctx -p"session:session.dat"  hmac_input.txt | xxd -p -c 256
tpm2_flushcontext session.dat
 
## then extend the pcr ...rerunning should fail

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

tpm2_load -C primary.ctx -u hmac.pub -r hmac.priv -c hmac.ctx 
tpm2_startauthsession --policy-session -S session.dat
tpm2_pcrread sha256:23 -o pcr23_val.bin
tpm2_policypcr -S session.dat -l sha256:23 -f pcr23_val.bin -L policy.dat
tpm2_hmac -g sha256 -c hmac.ctx -p"session:session.dat"  hmac_input.txt | xxd -p -c 256
tpm2_flushcontext session.dat

package main

import (
	"encoding/hex"
	"flag"
	"fmt"
	"log"
	"os"

	// "github.com/google/go-tpm-tools/simulator"
	// "github.com/google/go-tpm/tpmutil"

	keyfile "github.com/foxboron/go-tpm-keyfiles"
	"github.com/google/go-tpm/tpm2"
	"github.com/google/go-tpm/tpm2/transport"
	"github.com/google/go-tpm/tpmutil"
)

const (
	maxInputBuffer = 1024
)

var (
	tpmPath = flag.String("tpm-path", "/dev/tpm0", "Path to the TPM device (character device or a Unix socket).")
	in      = flag.String("in", "private.pem", "privateKey File")

	ECCSRKHTemplate = tpm2.TPMTPublic{
		Type:    tpm2.TPMAlgECC,
		NameAlg: tpm2.TPMAlgSHA256,
		ObjectAttributes: tpm2.TPMAObject{
			FixedTPM:            true,
			FixedParent:         true,
			SensitiveDataOrigin: true,
			UserWithAuth:        true,
			NoDA:                true,
			Restricted:          true,
			Decrypt:             true,
		},
		Parameters: tpm2.NewTPMUPublicParms(
			tpm2.TPMAlgECC,
			&tpm2.TPMSECCParms{
				Symmetric: tpm2.TPMTSymDefObject{
					Algorithm: tpm2.TPMAlgAES,
					KeyBits: tpm2.NewTPMUSymKeyBits(
						tpm2.TPMAlgAES,
						tpm2.TPMKeyBits(128),
					),
					Mode: tpm2.NewTPMUSymMode(
						tpm2.TPMAlgAES,
						tpm2.TPMAlgCFB,
					),
				},
				CurveID: tpm2.TPMECCNistP256,
			},
		),
		Unique: tpm2.NewTPMUPublicID(
			tpm2.TPMAlgECC,
			&tpm2.TPMSECCPoint{
				X: tpm2.TPM2BECCParameter{
					Buffer: make([]byte, 0),
				},
				Y: tpm2.TPM2BECCParameter{
					Buffer: make([]byte, 0),
				},
			},
		),
	}
)

func main() {
	flag.Parse()

	flag.Parse()
	log.Println("======= Init  ========")

	// ************************

	rwc, err := tpmutil.OpenTPM(*tpmPath)
	//rwc, err := simulator.GetWithFixedSeedInsecure(1073741825)
	if err != nil {
		log.Fatalf("can't open TPM %q: %v", *tpmPath, err)
	}
	defer func() {
		rwc.Close()
	}()

	rwr := transport.FromReadWriter(rwc)

	c, err := os.ReadFile(*in)
	if err != nil {
		log.Fatalf("can't load keys %q: %v", *tpmPath, err)
	}
	key, err := keyfile.Decode(c)
	if err != nil {
		log.Fatalf("can't decode keys %q: %v", *tpmPath, err)
	}

	// specify its parent directly
	primaryKey, err := tpm2.CreatePrimary{
		PrimaryHandle: key.Parent,
		InPublic:      tpm2.New2B(ECCSRKHTemplate),
	}.Execute(rwr)
	if err != nil {
		log.Fatalf("can't create primary %q: %v", *tpmPath, err)
	}

	defer func() {
		flushContextCmd := tpm2.FlushContext{
			FlushHandle: primaryKey.ObjectHandle,
		}
		_, _ = flushContextCmd.Execute(rwr)
	}()

	// now the actual key can get loaded from that parent
	hmacKey, err := tpm2.Load{
		ParentHandle: tpm2.AuthHandle{
			Handle: primaryKey.ObjectHandle,
			Name:   tpm2.TPM2BName(primaryKey.Name),
			Auth:   tpm2.PasswordAuth([]byte("")),
		},
		InPublic:  key.Pubkey,
		InPrivate: key.Privkey,
	}.Execute(rwr)

	if err != nil {
		log.Fatalf("can't load  hmacKey : %v", err)
	}

	defer func() {
		flushContextCmd := tpm2.FlushContext{
			FlushHandle: hmacKey.ObjectHandle,
		}
		_, _ = flushContextCmd.Execute(rwr)
	}()

	/// ============================ =================================================================================================
	data := []byte("foo")

	// tpm2_evictcontrol -C o -c hmac.ctx 0x81008000
	//hmacKey := tpm2.TPMHandle(0x81008000)

	p, err := tpm2.ReadPublic{
		//ObjectHandle: hmacKey,
		ObjectHandle: hmacKey.ObjectHandle,
	}.Execute(rwr)
	if err != nil {
		log.Fatalf(">>> can't hmac public key %v", err)
	}
	// hp, err := p.OutPublic.Contents()
	// if err != nil {
	// 	log.Fatalf("can't open TPM %q: %v", *tpmPath, err)
	// }

	// log.Printf(">> %v\n", hp.)
	/// ============================ =================================================================================================
	hmackeyPassword := []byte("")
	objAuth := &tpm2.TPM2BAuth{
		Buffer: hmackeyPassword,
	}
	//sess := tpm2.PasswordAuth(objAuth.Buffer)
	sess, cleanup, err := tpm2.PolicySession(rwr, tpm2.TPMAlgSHA256, 16)
	if err != nil {
		log.Printf("ERROR:  could not get PolicySession: %v", err)
		return
	}
	defer cleanup()

	selection := tpm2.TPMLPCRSelection{
		PCRSelections: []tpm2.TPMSPCRSelection{
			{
				Hash:      tpm2.TPMAlgSHA256,
				PCRSelect: tpm2.PCClientCompatible.PCRs(23),
			},
		},
	}
	expectedDigest, err := getExpectedPCRDigest(rwr, selection, tpm2.TPMAlgSHA256)
	if err != nil {
		log.Printf("ERROR:  could not get PolicySession: %v", err)
		return
	}
	_, err = tpm2.PolicyPCR{
		PolicySession: sess.Handle(),
		PcrDigest: tpm2.TPM2BDigest{
			Buffer: expectedDigest,
		},
		Pcrs: selection,
	}.Execute(rwr)
	if err != nil {
		log.Fatalf("Unable to initialize tpmJWT: %v", err)
	}

	//hmacBytes, err := hmac(rwr, data, hmacKey, p.Name, *objAuth, sess)
	hmacBytes, err := hmac(rwr, data, hmacKey.ObjectHandle, p.Name, *objAuth, sess)
	if err != nil {
		log.Fatalf("ccc can't open TPM %q: %v", *tpmPath, err)
	}
	log.Printf("Hmac: %s\n", hex.EncodeToString(hmacBytes))

}

func hmac(rwr transport.TPM, data []byte, objHandle tpm2.TPMHandle, objName tpm2.TPM2BName, objAuth tpm2.TPM2BAuth, sess tpm2.Session) ([]byte, error) {

	// sas, sasCloser, err := tpm2.HMACSession(rwr, tpm2.TPMAlgSHA256, 16)
	// if err != nil {
	// 	return nil, err
	// }
	// defer func() {
	// 	_ = sasCloser()
	// }()

	// defer func() {
	// 	flushContextCmd := tpm2.FlushContext{
	// 		FlushHandle: sas.Handle(),
	// 	}
	// 	_, err = flushContextCmd.Execute(rwr)
	// }()

	hmacStart := tpm2.HmacStart{
		Handle: tpm2.AuthHandle{
			Handle: objHandle,
			Name:   objName,
			Auth:   sess,
		},
		Auth:    objAuth,
		HashAlg: tpm2.TPMAlgNull,
	}

	rspHS, err := hmacStart.Execute(rwr)
	if err != nil {
		return nil, err
	}

	authHandle := tpm2.AuthHandle{
		Name:   objName,
		Handle: rspHS.SequenceHandle,
		Auth:   tpm2.PasswordAuth(objAuth.Buffer),
	}
	for len(data) > maxInputBuffer {
		sequenceUpdate := tpm2.SequenceUpdate{
			SequenceHandle: authHandle,
			Buffer: tpm2.TPM2BMaxBuffer{
				Buffer: data[:maxInputBuffer],
			},
		}
		_, err = sequenceUpdate.Execute(rwr)
		if err != nil {
			return nil, err
		}
		data = data[maxInputBuffer:]
	}

	sequenceComplete := tpm2.SequenceComplete{
		SequenceHandle: authHandle,
		Buffer: tpm2.TPM2BMaxBuffer{
			Buffer: data,
		},
		Hierarchy: tpm2.TPMRHOwner,
	}

	rspSC, err := sequenceComplete.Execute(rwr)
	if err != nil {
		return nil, err
	}

	return rspSC.Result.Buffer, nil

}

func getExpectedPCRDigest(thetpm transport.TPM, selection tpm2.TPMLPCRSelection, hashAlg tpm2.TPMAlgID) ([]byte, error) {
	pcrRead := tpm2.PCRRead{
		PCRSelectionIn: selection,
	}

	pcrReadRsp, err := pcrRead.Execute(thetpm)
	if err != nil {
		return nil, err
	}

	var expectedVal []byte
	for _, digest := range pcrReadRsp.PCRValues.Digests {
		expectedVal = append(expectedVal, digest.Buffer...)
	}

	cryptoHashAlg, err := hashAlg.Hash()
	if err != nil {
		return nil, err
	}

	hash := cryptoHashAlg.New()
	hash.Write(expectedVal)
	return hash.Sum(nil), nil
}

func pcrPolicyDigest(thetpm transport.TPM, pcr []uint) ([]byte, error) {
	sess, cleanup, err := tpm2.PolicySession(thetpm, tpm2.TPMAlgSHA256, 16, tpm2.Trial())
	if err != nil {
		return nil, err
	}
	defer cleanup()

	selection := tpm2.TPMLPCRSelection{
		PCRSelections: []tpm2.TPMSPCRSelection{
			{
				Hash:      tpm2.TPMAlgSHA256,
				PCRSelect: tpm2.PCClientCompatible.PCRs(pcr...),
			},
		},
	}
	fmt.Printf(">> pcr %d\n", pcr)
	expectedDigest, err := getExpectedPCRDigest(thetpm, selection, tpm2.TPMAlgSHA256)
	if err != nil {
		return nil, err
	}
	fmt.Printf(">>>>>>>>> %v", selection.PCRSelections[0].PCRSelect)
	_, err = tpm2.PolicyPCR{
		PolicySession: sess.Handle(),
		Pcrs:          selection,
		PcrDigest: tpm2.TPM2BDigest{
			Buffer: expectedDigest,
		},
	}.Execute(thetpm)
	if err != nil {
		return nil, err
	}

	pgd, err := tpm2.PolicyGetDigest{
		PolicySession: sess.Handle(),
	}.Execute(thetpm)
	if err != nil {
		return nil, err
	}
	_, err = tpm2.FlushContext{FlushHandle: sess.Handle()}.Execute(thetpm)
	if err != nil {
		return nil, err
	}
	return pgd.PolicyDigest.Buffer, nil
}

module main

go 1.22.2

require (
	github.com/foxboron/go-tpm-keyfiles v0.0.0-20240525122353-0883da4eb332
	github.com/google/go-tpm v0.9.1-0.20240510201744-5c2f0887e003
)

require (
	golang.org/x/crypto v0.19.0 // indirect
	golang.org/x/sys v0.17.0 // indirect
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment