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
)