Skip to content

Instantly share code, notes, and snippets.

@salrashid123
Created May 26, 2024 16:19
Show Gist options
  • Save salrashid123/6d355e4f03b124c085f780a9b3555dc8 to your computer and use it in GitHub Desktop.
Save salrashid123/6d355e4f03b124c085f780a9b3555dc8 to your computer and use it in GitHub Desktop.
TPM Keyfile with permanent handle

EC permanent handle as parent:

https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html#section-3.1.8


	// pg26:  7.5.1: https://trustedcomputinggroup.org/wp-content/uploads/TCG-TPM-v2.0-Provisioning-Guidance-Published-v1r1.pdf
	// pg 43: B.4.5 Template H-2: ECC NIST P256 (Storage) https://trustedcomputinggroup.org/wp-content/uploads/TCG-EK-Credential-Profile-V-2.5-R2_published.pdf

	// If a permanent handle (MSO 0x40) is specified then the implementation MUST run TPM2_CreatePrimary on the handle
	// using the TCG specified Elliptic Curve template [TCG-Provision] (section 7.5.1 for the Storage and other seeds or 7.4.1 for the endorsement seed)
	// which refers to the TCG EK Credential Profile [TCG-EK-Profile] . Since there are several possible templates,
	// implementations MUST always use the H template (the one with zero size unique fields).
	// The template used MUST be H-2 (EK Credential Profile section B.4.5) for the NIST P-256 curve if
	// rsaParent is absent or the H-1 (EK Credential Profile section B.4.4)

asically, generate a key using tpm2tss, then use go-tpm-keyfile to sign verify (or viceversa)

also see TPM KeyFiles with go and openssl TSS2 PRIVATE KEY format

tpm2tss-genkey -a rsa -s 2048 mykey.pem

openssl asn1parse -inform PEM -in mykey.pem 

    0:d=0  hl=4 l= 530 cons: SEQUENCE          
    4:d=1  hl=2 l=   6 prim: OBJECT            :2.23.133.10.1.3
   12:d=1  hl=2 l=   3 cons: cont [ 0 ]        
   14:d=2  hl=2 l=   1 prim: BOOLEAN           :255
   17:d=1  hl=2 l=   4 prim: INTEGER           :40000001
   23:d=1  hl=4 l= 280 prim: OCTET STRING      [HEX DUMP]:01160001000B000604720000001000100800000100010100DBD13AE6AEC1305D68F1F1946555EE6EA06A6EB21028D853C9CAEC785ADCDBB70534B749BDDB4DA9C68F2DF7CD1428C636A98FDEE4CC3E360B34464753BE1FE0BD1E545951F058286AE897459AFA9217414BBB3B0B40B13C68DE996F5627B3A6E8069F2E58E520B5C20ED00AD50D2315B93FA429E76C00F39ACC71D7A7C6501920C2E9DFD3062D4B193CB2313042AFC134CFA993B9DE4E7345DEAE0B8B6C62547B4E3C68AA9337E82A1A71EBF66C22169CA86678BB37FB5439164E12E704606D4F8B60DA22E033FC476548AFF160ACC52550A9B6EEE63018268B173FF362E0643F8586790EEE25FD8C75A6D714814852355D71E482FFBC74AB8E6AB2B590F61F
  307:d=1  hl=3 l= 224 prim: OCTET STRING      [HEX DUMP]:00DE0020F033F51E48922EBCC84DE37017587F5D7A3385455BCE3768B12E7F4B6220C5900010ADCF4E266C05016AC63A0B3ACAFD4565E19DEC4B065A5AF54C1F0415DE73C30C081AFDD6963262FA4B5B5AA395E4B8A4700A7B02289FFFF25754AF57B9DB512F2B54CDAC4D2649F452B0089E424B2824D02F7702FD4B7F8895C16FA087A30B67CFF4E2B95DE1ED855A9760CB7009CAEDB30A86C02AB68A68BACF6569A027179373BFC55FD255256A55B32C82042810A4329BDB65F458EA67D912684CE9CD98F5904092E870C921A86B0A1DC8276EE3E3627440830E83C8136D60



echo -n "foo" > data.bin
openssl rsa -engine tpm2tss -inform engine -in mykey.pem -pubout -outform pem -out publickey.pem
openssl dgst -engine tpm2tss -keyform engine -sha256 -sign mykey.pem -out data.bin.sig data.bin
openssl dgst -sha256 -verify publickey.pem -signature data.bin.sig data.bin


xxd -p data.bin.sig 
    01081d83a705f8b52ab09460096cdffa9bf97f09109a4c6c59fb55cdb0bb
    e0bb83e288dc1f55780a92729f026a2e8b18a69e101d66eaafce2366d529
    d04c619fd0f09ba49388adf718206ccc29e338cc7924a44cc4790c6cad48
    f8c2ef646aec2b8115c88122b310c70b7b25f9fb6fff1ebad87981bb4f79
    0f7d9ba1bb1c9248747e9b05cb52e67244ab7bfbd34e675a170650ff3b18
    38b3890bedad1eea4e9cb48b89617b106655d3026aae3affef83372ae917
    8ab274c05e583640a2db36092772613c5e843eacc49a62dfec39b3225ecf
    7fde10eca98fdee6693ffac7f4708ca889f5bc9148d06de20cebbab5f451
    6b1d9295a55bb735a8bc0c746a0e981b




go run main.go --datatosign="foo" --in mykey.pem 
    2024/05/26 14:54:09 ======= Init  ========
    2024/05/26 14:54:09 =======  primary ========
    2024/05/26 14:54:09  primary key name 000b625f16b731e37fee5d3245e74bcd06cadf4396c440efd86e6b36435905e1d6bc
    2024/05/26 14:54:09 ======= reading key from file ========
    2024/05/26 14:54:09 ======= reloading key from file ========
    2024/05/26 14:54:09 ======= generating signature ========
    2024/05/26 14:54:09 signature: 01081d83a705f8b52ab09460096cdffa9bf97f09109a4c6c59fb55cdb0bbe0bb83e288dc1f55780a92729f026a2e8b18a69e101d66eaafce2366d529d04c619fd0f09ba49388adf718206ccc29e338cc7924a44cc4790c6cad48f8c2ef646aec2b8115c88122b310c70b7b25f9fb6fff1ebad87981bb4f790f7d9ba1bb1c9248747e9b05cb52e67244ab7bfbd34e675a170650ff3b1838b3890bedad1eea4e9cb48b89617b106655d3026aae3affef83372ae9178ab274c05e583640a2db36092772613c5e843eacc49a62dfec39b3225ecf7fde10eca98fdee6693ffac7f4708ca889f5bc9148d06de20cebbab5f4516b1d9295a55bb735a8bc0c746a0e981b

package main

import (
	"crypto"
	"crypto/rsa"
	"crypto/sha256"
	"encoding/hex"
	"flag"
	"log"
	"os"

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

	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 ()

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

	// pg26:  7.5.1: https://trustedcomputinggroup.org/wp-content/uploads/TCG-TPM-v2.0-Provisioning-Guidance-Published-v1r1.pdf
	// pg 43: B.4.5 Template H-2: ECC NIST P256 (Storage) https://trustedcomputinggroup.org/wp-content/uploads/TCG-EK-Credential-Profile-V-2.5-R2_published.pdf

	// If a permanent handle (MSO 0x40) is specified then the implementation MUST run TPM2_CreatePrimary on the handle
	// using the TCG specified Elliptic Curve template [TCG-Provision] (section 7.5.1 for the Storage and other seeds or 7.4.1 for the endorsement seed)
	// which refers to the TCG EK Credential Profile [TCG-EK-Profile] . Since there are several possible templates,
	// implementations MUST always use the H template (the one with zero size unique fields).
	// The template used MUST be H-2 (EK Credential Profile section B.4.5) for the NIST P-256 curve if
	// rsaParent is absent or the H-1 (EK Credential Profile section B.4.4)

	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()

	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("=======  primary ========")

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

	log.Printf(" primary key name %s\n", hex.EncodeToString(primary.Name.Buffer))

	// load the tpm-tss generated rsa key from disk
	log.Printf("======= reading key from file ========")
	c, err := os.ReadFile(*in)
	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: primary.ObjectHandle,
			Name:   tpm2.TPM2BName(primary.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: primary.ObjectHandle,
	}
	_, err = flush.Execute(rwr)
	if err != nil {
		log.Fatalf("can't close primary  %v", err)
	}

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

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

	log.Printf("======= generating signature ========")

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

	pubKey, err := key.PublicKey()
	if err != nil {
		log.Fatalf("Failed to get rsa public key: %v", err)
	}

	rsaPub := pubKey.(*rsa.PublicKey)
	rsassa, err := rspSign.Signature.Signature.RSASSA()
	if err != nil {
		log.Fatalf("Failed to get signature part: %v", err)
	}
	if err := rsa.VerifyPKCS1v15(rsaPub, crypto.SHA256, digest[:], rsassa.Sig.Buffer); err != nil {
		log.Fatalf("Failed to verify signature: %v", err)
	}

	log.Printf("signature: %s\n", hex.EncodeToString(rsassa.Sig.Buffer))

}

module main

go 1.22.0

toolchain go1.22.2

require (
	github.com/google/go-tpm v0.9.1-0.20240510201744-5c2f0887e003
	github.com/google/go-tpm-tools v0.4.4
)

require (
	github.com/foxboron/go-tpm-keyfiles v0.0.0-20240525122353-0883da4eb332 // indirect

	github.com/golang/protobuf v1.5.4 // 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.6.0 // 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.23.0 // indirect
	golang.org/x/sys v0.20.0 // indirect
	google.golang.org/protobuf v1.34.1 // indirect
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment