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
)