Skip to content

Instantly share code, notes, and snippets.

@salrashid123
Last active June 11, 2024 13:40
Show Gist options
  • Save salrashid123/92d172a9e40144412883ad926226660d to your computer and use it in GitHub Desktop.
Save salrashid123/92d172a9e40144412883ad926226660d to your computer and use it in GitHub Desktop.
go-tpm-files save/load from persistent handle
  • crate prmiary

  • create key

  • evict primary to persistent handle 0x81000000

  • save key with go-tpm-keyfiles

  • load key using go-tpm-keyfile

module main

go 1.22.2

require (
	github.com/foxboron/go-tpm-keyfiles v0.0.0-20240607201534-c7a43ea1908b
	github.com/google/go-tpm v0.9.1
	github.com/google/go-tpm-tools v0.4.4
)

require (
	github.com/golang/protobuf v1.5.3 // indirect
	github.com/google/go-configfs-tsm v0.2.2 // indirect
	github.com/google/go-sev-guest v0.9.3 // indirect
	github.com/google/go-tdx-guest v0.3.1 // indirect
	github.com/google/logger v1.1.1 // indirect
	github.com/google/uuid v1.3.1 // indirect
	github.com/pborman/uuid v1.2.1 // indirect
	github.com/pkg/errors v0.9.1 // indirect
	go.uber.org/multierr v1.11.0 // indirect
	golang.org/x/crypto v0.19.0 // indirect
	golang.org/x/sys v0.21.0 // indirect
	google.golang.org/protobuf v1.31.0 // indirect
)

  • create.go
package main

import (
	"bytes"
	"encoding/base64"
	"flag"
	"log"
	"os"

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

const ()

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

func main() {
	flag.Parse()

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

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

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

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

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

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

	// *************** evict

	// tpm2_evictcontrol -C o -c 0x81000000

	// https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html#section-3.1.8
	_, err = tpm2.EvictControl{
		Auth: tpm2.TPMRHOwner,
		ObjectHandle: &tpm2.NamedHandle{
			Handle: primaryKey.ObjectHandle,
			Name:   primaryKey.Name,
		},
		PersistentHandle: tpm2.TPMHandle(0x81000000),
	}.Execute(rwc)
	if err != nil {
		log.Fatalf("can't create rsa %v", err)
	}

	// 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(rwc)
	if err != nil {
		log.Fatalf("can't create rsa %v", err)
	}

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

	// write the key to file
	log.Printf("======= writing key to file ========")
	kf := &keyfile.TPMKey{
		Keytype:     keyfile.OIDLoadableKey,
		AuthPolicy:  []*keyfile.TPMAuthPolicy{},
		Parent:      0x81000000,
		Pubkey:      rsaKeyResponse.OutPublic,
		Privkey:     rsaKeyResponse.OutPrivate,
		Description: "key1",
	}
	if err != nil {
		log.Fatalf("failed to create KeyFile: %v", err)
	}

	b := new(bytes.Buffer)

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

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

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

}

  • load.go
package main

import (
	"crypto/sha256"
	"encoding/base64"
	"flag"
	"log"
	"os"

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

const ()

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

func main() {
	flag.Parse()

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

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

	// load the rsa key from disk
	log.Printf("======= reading key from file ========")
	c, err := os.ReadFile(*keyFile)
	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)
	}

	sess := keyfile.NewTPMSession(rwc)

	// pub, err := tpm2.ReadPublic{
	// 	ObjectHandle: key.Parent,
	// }.Execute(rwc)
	// if err != nil {
	// 	log.Fatalf("failed loading key with parent: %v", err)
	// }

	// parentAuthHandle := tpm2.AuthHandle{
	// 	Handle: key.Parent,
	// 	Name:   pub.Name,
	// 	Auth:   tpm2.PasswordAuth(nil),
	// }

	parentAuthHandle := tpm2.AuthHandle{
		Handle: key.Parent,
		Name:   tpm2.HandleName(key.Parent),
		Auth:   tpm2.PasswordAuth(nil),
	}
	ah, err := keyfile.LoadKeyWithParent(sess, parentAuthHandle, key)
	if err != nil {
		log.Fatalf("failed loading key with parent: %v", err)
	}
	defer func() {
		flushContextCmd := tpm2.FlushContext{
			FlushHandle: ah.Handle,
		}
		_, _ = flushContextCmd.Execute(rwc)
	}()

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

	rspSign, err := tpm2.Sign{
		KeyHandle: ah,
		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(rwc)
	if err != nil {
		log.Fatalf("Failed to Sign: %v", err)
	}

	rsassa, err := rspSign.Signature.Signature.RSASSA()
	if err != nil {
		log.Fatalf("Failed to get signature part: %v", err)
	}
	log.Printf("signature  : %s\n", base64.StdEncoding.EncodeToString(rsassa.Sig.Buffer))
}

if you want to use a softwaretpm instead,

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

export TPM2TOOLS_TCTI="swtpm:port=2321"

## or with socat pipe /tmp/vtpm to swtpm tpc listner
sudo socat pty,link=/tmp/vtpm,raw,echo=0 tcp:localhost:2321
sudo chmod go+rw /tmp/vtpm

export TPM2TOOLS_TCTI="device:/tmp/vtpm"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment