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