Skip to content

Instantly share code, notes, and snippets.

@salrashid123
Created May 30, 2024 16:49
Show Gist options
  • Save salrashid123/3b9c1d8c192352390308049348abb4ba to your computer and use it in GitHub Desktop.
Save salrashid123/3b9c1d8c192352390308049348abb4ba to your computer and use it in GitHub Desktop.
go-tpm-tools compatibility with go-tpm-keyfile and go-tpm

sample demonstrating cross-usage/compatiblity between

go-tpm go-tpm-keyfile go-tpm-tools

package main

import (
	"bytes"
	"crypto"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"flag"
	"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-tools/client"
	"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"
)

const ()

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

func main() {
	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)

	log.Printf("======= createPrimary ========")

	data := []byte(*dataToSign)
	digest := sha256.Sum256(data)

	primaryKey, err := tpm2.CreatePrimary{
		PrimaryHandle: tpm2.TPMRHOwner,
		InPublic:      tpm2.New2B(tpm2.RSASRKTemplate),
	}.Execute(rwr)
	if err != nil {
		log.Fatalf("can't create primary %v", err)
	}

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

	log.Printf("primaryKey Name %s\n", base64.StdEncoding.EncodeToString(primaryKey.Name.Buffer))

	// rsa

	log.Printf("======= create key  ========")

	rsaTemplate := tpm2.TPMTPublic{
		Type:    tpm2.TPMAlgRSA,
		NameAlg: tpm2.TPMAlgSHA256,
		ObjectAttributes: tpm2.TPMAObject{
			SignEncrypt:         true,
			FixedTPM:            true,
			FixedParent:         true,
			SensitiveDataOrigin: true,
			UserWithAuth:        true,
		},
		AuthPolicy: tpm2.TPM2BDigest{},
		Parameters: tpm2.NewTPMUPublicParms(
			tpm2.TPMAlgRSA,
			&tpm2.TPMSRSAParms{
				Scheme: tpm2.TPMTRSAScheme{
					Scheme: tpm2.TPMAlgRSASSA,
					Details: tpm2.NewTPMUAsymScheme(
						tpm2.TPMAlgRSASSA,
						&tpm2.TPMSSigSchemeRSASSA{
							HashAlg: tpm2.TPMAlgSHA256,
						},
					),
				},
				KeyBits: 2048,
			},
		),
		Unique: tpm2.NewTPMUPublicID(
			tpm2.TPMAlgRSA,
			&tpm2.TPM2BPublicKeyRSA{
				Buffer: make([]byte, 256),
			},
		),
	}

	rsaKeyResponse, err := tpm2.CreateLoaded{
		ParentHandle: tpm2.AuthHandle{
			Handle: primaryKey.ObjectHandle,
			Name:   primaryKey.Name,
			Auth:   tpm2.PasswordAuth([]byte("")),
		},
		InPublic: tpm2.New2BTemplate(&rsaTemplate),
	}.Execute(rwr)
	if err != nil {
		log.Fatalf("can't create rsa %v", err)
	}

	// write the key to file
	log.Printf("======= writing key to file ========")

	//tkf, err := keyfile.NewLoadableKey(rsaKeyResponse.OutPublic, rsaKeyResponse.OutPrivate, tpm2.TPMHandle(*persistenthandle), false)
	tkf, err := keyfile.NewLoadableKey(rsaKeyResponse.OutPublic, rsaKeyResponse.OutPrivate, primaryKey.ObjectHandle, false)
	if err != nil {
		log.Fatalf("failed to create KeyFile: %v", err)
	}

	b := new(bytes.Buffer)

	err = keyfile.Encode(b, tkf)
	if err != nil {
		log.Fatalf("failed to encode Key: %v", err)
	}

	log.Printf("rsa Key PEM: \n%s\n", b)

	err = os.WriteFile(*out, b.Bytes(), 0644)
	if err != nil {
		log.Fatalf("failed to write private key to file %v", err)
	}

	/// ============================ =================================================================================================

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

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

	rwc.Close()

	/// now rerun the test...but this time load the private key from disk
	log.Printf("======= reopening TPM ========")
	// ===============================================================================================================================
	//rwc, err = tpmutil.OpenTPM(*tpmPath)
	rwc, err = simulator.GetWithFixedSeedInsecure(1073741825)
	if err != nil {
		log.Fatalf("can't open TPM %q: %v", *tpmPath, err)
	}

	rwr = transport.FromReadWriter(rwc)
	log.Printf("======= regenerating primary ========")
	// now recreate everything from scratch
	regenPrimary, err := tpm2.CreatePrimary{
		PrimaryHandle: tpm2.TPMRHOwner,
		InPublic:      tpm2.New2B(tpm2.RSASRKTemplate),
	}.Execute(rwr)
	if err != nil {
		log.Fatalf("can't create primary TPM %q: %v", *tpmPath, err)
	}

	log.Printf("regenerated primary key name %s\n", base64.StdEncoding.EncodeToString(regenPrimary.Name.Buffer))

	ekoutPub, err := regenPrimary.OutPublic.Contents()
	if err != nil {
		log.Fatalf("can't close flush blob %v", err)
	}
	// load the rsa key from disk
	log.Printf("======= reading key from file ========")
	c, err := os.ReadFile(*out)
	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)
	}

	log.Printf("======= reloading key from file ========")

	regenRSAKey, err := tpm2.Load{
		ParentHandle: tpm2.AuthHandle{
			Handle: regenPrimary.ObjectHandle,
			Name:   tpm2.TPM2BName(regenPrimary.Name),
			Auth:   tpm2.PasswordAuth([]byte("")),
		},
		InPublic:  key.Pubkey,
		InPrivate: key.Privkey,
	}.Execute(rwr)
	if err != nil {
		log.Fatalf("can't load rsa key: %v", err)
	}

	flush := tpm2.FlushContext{
		FlushHandle: regenPrimary.ObjectHandle,
	}
	_, err = flush.Execute(rwr)
	if err != nil {
		log.Fatalf("can't close primary  %v", err)
	}

	defer func() {
		flushContextCmd := tpm2.FlushContext{
			FlushHandle: regenRSAKey.ObjectHandle,
		}
		_, _ = flushContextCmd.Execute(rwr)
		// if err != nil {
		// 	log.Fatalf("can't close rsa key handle: %v", err)
		// }
	}()

	// now try to create a signature using a go-tpm-tools.client.Key
	log.Printf("======= constructing handle for go-tpm-tools.client.Key========")

	// note, go-tpm-tools uses the legacy go-tpm library so its a bit awkward
	pHandle := tpmutil.Handle(regenRSAKey.ObjectHandle.HandleValue())

	k, err := client.LoadCachedKey(rwc, pHandle, nil)
	if err != nil {
		log.Fatalf("error loading rsa key%v\n", err)
	}
	defer k.Close()

	r, err := k.GetSigner()
	if err != nil {
		log.Fatalf("Error getting singer %v\n", err)
	}

	s, err := r.Sign(rand.Reader, digest[:], crypto.SHA256)
	if err != nil {
		log.Fatalf("Error signing %v\n", err)
	}
	log.Printf("RSA Signed String: %s\n", base64.StdEncoding.EncodeToString(s))

	// ***************
	ba, err := k.Name().Digest.Encode()
	if err != nil {
		log.Fatalf("Error signing %v\n", err)
	}
	kb := tpm2.TPM2BName{
		Buffer: ba,
	}
	log.Printf("H1 %s\n", hex.EncodeToString(regenRSAKey.Name.Buffer))
	log.Printf("H2 %s\n", hex.EncodeToString(kb.Buffer))

	createEKRsp, err := tpm2.CreatePrimary{
		PrimaryHandle: tpm2.TPMRHEndorsement,
		InPublic:      tpm2.New2B(tpm2.RSAEKTemplate),
	}.Execute(rwr)
	if err != nil {
		log.Fatalf("can't close flush blob %v", err)
	}
	ekoutPub, err = createEKRsp.OutPublic.Contents()
	if err != nil {
		log.Fatalf("can't close flush blob %v", err)
	}
	defer func() {
		flushContextCmd := tpm2.FlushContext{
			FlushHandle: createEKRsp.ObjectHandle,
		}
		_, _ = flushContextCmd.Execute(rwr)
	}()

	rspSign2, err := tpm2.Sign{
		KeyHandle: tpm2.NamedHandle{
			Handle: tpm2.TPMHandle(k.Handle().HandleValue()),
			Name:   kb,
			//Handle: regenRSAKey.ObjectHandle,
			//Name: regenRSAKey.Name,
		},

		Digest: tpm2.TPM2BDigest{
			Buffer: digest[:],
		},
		InScheme: tpm2.TPMTSigScheme{
			Scheme: tpm2.TPMAlgRSASSA,
			Details: tpm2.NewTPMUSigScheme(
				tpm2.TPMAlgRSASSA,
				&tpm2.TPMSSchemeHash{
					HashAlg: tpm2.TPMAlgSHA256,
				},
			),
		},
		Validation: tpm2.TPMTTKHashCheck{
			Tag: tpm2.TPMSTHashCheck,
		},
	}.Execute(rwr, tpm2.HMAC(tpm2.TPMAlgSHA256, 16, tpm2.Auth(nil), tpm2.AESEncryption(128, tpm2.EncryptIn), tpm2.Salted(createEKRsp.ObjectHandle, *ekoutPub)))
	if err != nil {
		log.Fatalf("Failed to Sign: %v", err)
	}

	rsassa2, err := rspSign2.Signature.Signature.RSASSA()
	if err != nil {
		log.Fatalf("Failed to get signature part: %v", err)
	}
	log.Printf("signature from go-tpm-tools key : %s\n", base64.StdEncoding.EncodeToString(rsassa2.Sig.Buffer))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment