Skip to content

Instantly share code, notes, and snippets.

@salrashid123
Last active December 12, 2024 22:03
Show Gist options
  • Save salrashid123/ba4ade905336466fa9d928c4d1f0e67f to your computer and use it in GitHub Desktop.
Save salrashid123/ba4ade905336466fa9d928c4d1f0e67f to your computer and use it in GitHub Desktop.
TPM Policy command parameter generation for PCR
package main
import (
"encoding/binary"
"encoding/hex"
"flag"
"io"
"log"
"net"
"os"
"slices"
keyfile "github.com/foxboron/go-tpm-keyfiles"
"github.com/google/go-tpm-tools/simulator"
"github.com/google/go-tpm/tpm2"
"github.com/google/go-tpm/tpm2/transport"
"github.com/google/go-tpm/tpmutil"
)
/*
rm -rf /tmp/myvtpm && mkdir /tmp/myvtpm
sudo swtpm_setup --tpmstate /tmp/myvtpm --tpm2 --create-ek-cert
sudo swtpm socket --tpmstate dir=/tmp/myvtpm --tpm2 --server type=tcp,port=2321 --ctrl type=tcp,port=2322 --flags not-need-init,startup-clear --log level=5
## new window
export TPM2TOOLS_TCTI="swtpm:port=2321"
export TPM2OPENSSL_TCTI="swtpm:port=2321"
export TPM2TSSENGINE_TCTI="swtpm:port=2321"
export OPENSSL_MODULES=/usr/lib/x86_64-linux-gnu/ossl-modules/
tpm2_pcrextend 23:sha256=0x0000000000000000000000000000000000000000000000000000000000000000
tpm2_pcrread sha256:23
$ tpm2_pcrread
sha1:
sha256:
0 : 0x0000000000000000000000000000000000000000000000000000000000000000
1 : 0x0000000000000000000000000000000000000000000000000000000000000000
2 : 0x0000000000000000000000000000000000000000000000000000000000000000
3 : 0x0000000000000000000000000000000000000000000000000000000000000000
4 : 0x0000000000000000000000000000000000000000000000000000000000000000
5 : 0x0000000000000000000000000000000000000000000000000000000000000000
6 : 0x0000000000000000000000000000000000000000000000000000000000000000
7 : 0x0000000000000000000000000000000000000000000000000000000000000000
8 : 0x0000000000000000000000000000000000000000000000000000000000000000
9 : 0x0000000000000000000000000000000000000000000000000000000000000000
10: 0x0000000000000000000000000000000000000000000000000000000000000000
11: 0x0000000000000000000000000000000000000000000000000000000000000000
12: 0x0000000000000000000000000000000000000000000000000000000000000000
13: 0x0000000000000000000000000000000000000000000000000000000000000000
14: 0x0000000000000000000000000000000000000000000000000000000000000000
15: 0x0000000000000000000000000000000000000000000000000000000000000000
16: 0x0000000000000000000000000000000000000000000000000000000000000000
17: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
18: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
19: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
20: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
21: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
22: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
23: 0xF5A5FD42D16A20302798EF6ED309979B43003D2320D9F0E8EA9831A92759FB4B
echo "foo" > secret.dat
openssl rand -out iv.bin 16
tpm2_flushcontext -t && tpm2_flushcontext -s && tpm2_flushcontext -l
tpm2_pcrread sha256:23 -o pcr23_val.bin
tpm2_startauthsession -S session.dat
tpm2_policypcr -S session.dat -l sha256:23 -L policy.dat -f pcr23_val.bin
tpm2_flushcontext session.dat
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 -g sha256 -G aes -u key.pub -r key.priv -C primary.ctx -L policy.dat
tpm2_flushcontext -t && tpm2_flushcontext -s && tpm2_flushcontext -l
tpm2_load -C primary.ctx -u key.pub -r key.priv -n key.name -c aes.ctx
tpm2_startauthsession --policy-session -S session.dat
tpm2_policypcr -S session.dat -l "sha256:23" -L policy.dat
tpm2_encryptdecrypt -Q --iv iv.bin -c aes.ctx -o cipher.out -p"session:session.dat" secret.dat
tpm2_flushcontext -t && tpm2_flushcontext -s && tpm2_flushcontext -l
tpm2_startauthsession --policy-session -S session.dat
tpm2_policypcr -S session.dat -l "sha256:23" -L policy.dat
tpm2_encryptdecrypt -Q --iv iv.bin -c aes.ctx -d -o plain.out cipher.out -p"session:session.dat"
tpm2_flushcontext -t && tpm2_flushcontext -s && tpm2_flushcontext -l
tpm2_encodeobject -C primary.ctx -u key.pub -r key.priv -o private.pem
openssl asn1parse -inform PEM -in private.pem
*/
const (
pcr = 23
)
const ()
var (
tpmPath = flag.String("tpm-path", "127.0.0.1:2321", "Path to the TPM device (character device or a Unix socket).")
k = flag.String("key", "private.pem", "PEM file")
encryptedFile = flag.String("encryptedFile", "cipher.out", "ciphertext")
ivFile = flag.String("ivFile", "iv.bin", "IV")
)
var TPMDEVICES = []string{"/dev/tpm0", "/dev/tpmrm0"}
func OpenTPM(path string) (io.ReadWriteCloser, error) {
if slices.Contains(TPMDEVICES, path) {
return tpmutil.OpenTPM(path)
} else if path == "simulator" {
return simulator.GetWithFixedSeedInsecure(1073741825)
} else {
return net.Dial("tcp", path)
}
}
func main() {
flag.Parse()
rwc, err := OpenTPM(*tpmPath)
if err != nil {
log.Fatalf("can't open TPM %q: %v", *tpmPath, err)
}
defer func() {
if err := rwc.Close(); err != nil {
log.Fatalf("can't close TPM %q: %v", *tpmPath, err)
}
}()
rwr := transport.FromReadWriter(rwc)
sel := tpm2.TPMLPCRSelection{
PCRSelections: []tpm2.TPMSPCRSelection{
{
Hash: tpm2.TPMAlgSHA256,
PCRSelect: tpm2.PCClientCompatible.PCRs(pcr),
},
},
}
expectedDigest, err := getExpectedPCRDigest(rwr, sel, tpm2.TPMAlgSHA256)
if err != nil {
log.Printf("ERROR: could not get PolicySession: %v", err)
return
}
// 23.7 TPM2_PolicyPCR https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-3-Commands-01.38.pdf
pcrSelectionSegment := tpm2.Marshal(sel)
pcrDigestSegment := tpm2.Marshal(tpm2.TPM2BDigest{
Buffer: expectedDigest,
})
commandParameter := append(pcrDigestSegment, pcrSelectionSegment...)
// 0020e2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf900000001000b03000080
log.Printf("pcrSelectionSegment %s", hex.EncodeToString(pcrSelectionSegment))
log.Printf("pcrDigestSegment %s", hex.EncodeToString(pcrDigestSegment))
log.Printf("commandParameter %s", hex.EncodeToString(commandParameter))
////
// start a session
c, err := os.ReadFile(*k)
if err != nil {
log.Fatalf("error reading private keyfile: %v", err)
}
key, err := keyfile.Decode(c)
if err != nil {
log.Fatalf("failed decoding key: %v", err)
}
primaryKey, err := tpm2.CreatePrimary{
PrimaryHandle: key.Parent,
InPublic: tpm2.New2B(keyfile.ECCSRK_H2_Template),
}.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)
}()
aesKey, 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: aesKey.ObjectHandle,
}
_, _ = flushContextCmd.Execute(rwr)
}()
// now create a policy session and inject the parameters
sess2, cleanup2, err := tpm2.PolicySession(rwr, tpm2.TPMAlgSHA256, 16, []tpm2.AuthOption{tpm2.Auth([]byte(nil))}...)
if err != nil {
log.Fatalf("setting up policy session: %v", err)
}
defer cleanup2()
l := binary.BigEndian.Uint16(commandParameter[:2])
dgst := commandParameter[:l+2]
d, err := tpm2.Unmarshal[tpm2.TPM2BDigest](dgst)
if err != nil {
log.Fatalf("error generating requestParameters: %v", err)
}
t, err := tpm2.Unmarshal[tpm2.TPMLPCRSelection](commandParameter[l+2:])
if err != nil {
log.Fatalf("error generating requestParameters: %v", err)
}
_, err = tpm2.PolicyPCR{
PolicySession: sess2.Handle(),
PcrDigest: *d,
Pcrs: *t,
}.Execute(rwr)
if err != nil {
log.Fatalf("error generating requestParameters: %v", err)
}
data, err := os.ReadFile(*encryptedFile)
if err != nil {
log.Fatalf("can't read encrypted file details %q: %v", *tpmPath, err)
}
iv, err := os.ReadFile(*ivFile)
if err != nil {
log.Fatalf("can't read encrypted file details %q: %v", *tpmPath, err)
}
keyAuth := tpm2.AuthHandle{
Handle: aesKey.ObjectHandle,
Name: aesKey.Name,
Auth: sess2,
}
encrypted, err := encryptDecryptSymmetric(rwr, keyAuth, iv, data, true)
if err != nil {
log.Fatalf("DecryptSymmetric failed: %s", err)
}
log.Printf("IV: %s", hex.EncodeToString(iv))
log.Printf("Decrypted %s", string(encrypted))
}
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
}
const maxDigestBuffer = 1024
func encryptDecryptSymmetric(rwr transport.TPM, keyAuth tpm2.AuthHandle, iv, data []byte, decrypt bool) ([]byte, error) {
var out, block []byte
for rest := data; len(rest) > 0; {
if len(rest) > maxDigestBuffer {
block, rest = rest[:maxDigestBuffer], rest[maxDigestBuffer:]
} else {
block, rest = rest, nil
}
r, err := tpm2.EncryptDecrypt2{
KeyHandle: keyAuth,
Message: tpm2.TPM2BMaxBuffer{
Buffer: block,
},
Mode: tpm2.TPMAlgCFB,
Decrypt: decrypt,
IV: tpm2.TPM2BIV{
Buffer: iv,
},
}.Execute(rwr)
if err != nil {
return nil, err
}
block = r.OutData.Buffer
iv = r.IV.Buffer
out = append(out, block...)
}
return out, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment