From cfdeb6e9795a2fd873571fa974cccc9f54dbeae3 Mon Sep 17 00:00:00 2001 From: Dillon Flamand Date: Thu, 11 Apr 2024 07:36:13 -0700 Subject: [PATCH] feat: TPM2_RSA_Encrypt and TPM2_RSA_Decrypt (#356) --- tpm2/structures.go | 14 ++++ tpm2/test/rsa_encryption_test.go | 137 +++++++++++++++++++++++++++++++ tpm2/tpm2.go | 62 ++++++++++++++ 3 files changed, 213 insertions(+) create mode 100644 tpm2/test/rsa_encryption_test.go diff --git a/tpm2/structures.go b/tpm2/structures.go index fea20779..c0d2645a 100644 --- a/tpm2/structures.go +++ b/tpm2/structures.go @@ -2299,6 +2299,20 @@ type TPMTRSAScheme struct { Details TPMUAsymScheme `gotpm:"tag=Scheme"` } +// TPMIAlgRSADecrypt represents a TPMI_ALG_RSA_DECRYPT. +// See definition in Part 2: Structures, section 11.2.4.3. +type TPMIAlgRSADecrypt = TPMAlgID + +// TPMTRSADecrypt represents a TPMT_RSA_DECRYPT. +// See definition in Part 2: Structures, section 11.2.4.4. +type TPMTRSADecrypt struct { + marshalByReflection + // scheme selector + Scheme TPMIAlgRSADecrypt `gotpm:"nullable"` + // scheme parameters + Details TPMUAsymScheme `gotpm:"tag=Scheme"` +} + // TPM2BPublicKeyRSA represents a TPM2B_PUBLIC_KEY_RSA. // See definition in Part 2: Structures, section 11.2.4.5. type TPM2BPublicKeyRSA TPM2BData diff --git a/tpm2/test/rsa_encryption_test.go b/tpm2/test/rsa_encryption_test.go new file mode 100644 index 00000000..926e23d7 --- /dev/null +++ b/tpm2/test/rsa_encryption_test.go @@ -0,0 +1,137 @@ +package tpm2test + +import ( + "bytes" + "testing" + + . "github.com/google/go-tpm/tpm2" + "github.com/google/go-tpm/tpm2/transport/simulator" +) + +func TestRSAEncryption(t *testing.T) { + theTpm, err := simulator.OpenSimulator() + if err != nil { + t.Fatalf("could not connect to TPM simulator: %v", err) + } + t.Cleanup(func() { + if err := theTpm.Close(); err != nil { + t.Errorf("%v", err) + } + }) + + createPrimaryCmd := CreatePrimary{ + PrimaryHandle: TPMRHOwner, + InPublic: New2B(RSASRKTemplate), + } + createPrimaryRsp, err := createPrimaryCmd.Execute(theTpm) + if err != nil { + t.Fatalf("%v", err) + } + t.Cleanup(func() { + flushContextCmd := FlushContext{FlushHandle: createPrimaryRsp.ObjectHandle} + if _, err := flushContextCmd.Execute(theTpm); err != nil { + t.Errorf("%v", err) + } + }) + + createCmd := Create{ + ParentHandle: NamedHandle{ + Handle: createPrimaryRsp.ObjectHandle, + Name: createPrimaryRsp.Name, + }, + InPublic: New2B(TPMTPublic{ + Type: TPMAlgRSA, + NameAlg: TPMAlgSHA256, + ObjectAttributes: TPMAObject{ + FixedTPM: true, + STClear: false, + FixedParent: true, + SensitiveDataOrigin: true, + UserWithAuth: true, + AdminWithPolicy: false, + NoDA: true, + EncryptedDuplication: false, + Restricted: false, + Decrypt: true, + SignEncrypt: true, + }, + Parameters: NewTPMUPublicParms( + TPMAlgRSA, + &TPMSRSAParms{ + KeyBits: 2048, + }, + ), + Unique: NewTPMUPublicID( + TPMAlgRSA, + &TPM2BPublicKeyRSA{ + Buffer: make([]byte, 256), + }, + ), + }), + } + createRsp, err := createCmd.Execute(theTpm) + if err != nil { + t.Fatalf("%v", err) + } + + loadCmd := Load{ + ParentHandle: NamedHandle{ + Handle: createPrimaryRsp.ObjectHandle, + Name: createPrimaryRsp.Name, + }, + InPrivate: createRsp.OutPrivate, + InPublic: createRsp.OutPublic, + } + loadRsp, err := loadCmd.Execute(theTpm) + if err != nil { + t.Fatalf("%v", err) + } + t.Cleanup(func() { + flushContextCmd := FlushContext{FlushHandle: loadRsp.ObjectHandle} + if _, err := flushContextCmd.Execute(theTpm); err != nil { + t.Errorf("%v", err) + } + }) + + message := []byte("secret") + + encryptCmd := RSAEncrypt{ + KeyHandle: loadRsp.ObjectHandle, + Message: TPM2BPublicKeyRSA{Buffer: message}, + InScheme: TPMTRSADecrypt{ + Scheme: TPMAlgOAEP, + Details: NewTPMUAsymScheme( + TPMAlgOAEP, + &TPMSEncSchemeOAEP{ + HashAlg: TPMAlgSHA256, + }, + ), + }, + } + encryptRsp, err := encryptCmd.Execute(theTpm) + if err != nil { + t.Fatalf("%v", err) + } + + decryptCmd := RSADecrypt{ + KeyHandle: loadRsp.ObjectHandle, + CipherText: TPM2BPublicKeyRSA{Buffer: encryptRsp.OutData.Buffer}, + InScheme: TPMTRSADecrypt{ + Scheme: TPMAlgOAEP, + Details: NewTPMUAsymScheme( + TPMAlgOAEP, + &TPMSEncSchemeOAEP{ + HashAlg: TPMAlgSHA256, + }, + ), + }, + } + decryptRsp, err := decryptCmd.Execute(theTpm) + if err != nil { + t.Fatalf("%v", err) + } + + if !bytes.Equal(message, decryptRsp.Message.Buffer) { + t.Errorf("want %x got %x", message, decryptRsp.Message.Buffer) + } +} diff --git a/tpm2/tpm2.go b/tpm2/tpm2.go index 59828e60..7d63ab6c 100644 --- a/tpm2/tpm2.go +++ b/tpm2/tpm2.go @@ -452,6 +452,68 @@ type CreateLoadedResponse struct { Name TPM2BName } +// RSAEncrypt is the input to TPM2_RSA_Encrypt +// See definition in Part 3, Commands, section 14.2. +type RSAEncrypt struct { + // reference to public portion of RSA key to use for encryption + KeyHandle handle `gotpm:"handle"` + // message to be encrypted + Message TPM2BPublicKeyRSA + // the padding scheme to use if scheme associated with keyHandle is TPM_ALG_NULL + InScheme TPMTRSADecrypt `gotpm:"nullable"` + // optional label L to be associated with the message + Label TPM2BData `gotpm:"optional"` +} + +// Command implements the Command interface. +func (RSAEncrypt) Command() TPMCC { return TPMCCRSAEncrypt } + +// Execute executes the command and returns the response. +func (cmd RSAEncrypt) Execute(t transport.TPM, s ...Session) (*RSAEncryptResponse, error) { + var rsp RSAEncryptResponse + if err := execute[RSAEncryptResponse](t, cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} + +// RSAEncryptResponse is the response from TPM2_RSA_Encrypt +type RSAEncryptResponse struct { + // encrypted output + OutData TPM2BPublicKeyRSA +} + +// RSADecrypt is the input to TPM2_RSA_Decrypt +// See definition in Part 3, Commands, section 14.3. +type RSADecrypt struct { + // RSA key to use for decryption + KeyHandle handle `gotpm:"handle,auth"` + // cipher text to be decrypted + CipherText TPM2BPublicKeyRSA + // the padding scheme to use if scheme associated with keyHandle is TPM_ALG_NULL + InScheme TPMTRSADecrypt `gotpm:"nullable"` + // label whose association with the message is to be verified + Label TPM2BData `gotpm:"optional"` +} + +// Command implements the Command interface. +func (RSADecrypt) Command() TPMCC { return TPMCCRSADecrypt } + +// Execute executes the command and returns the response. +func (cmd RSADecrypt) Execute(t transport.TPM, s ...Session) (*RSADecryptResponse, error) { + var rsp RSADecryptResponse + if err := execute[RSADecryptResponse](t, cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} + +// RSADecryptResponse is the response from TPM2_RSA_Decrypt +type RSADecryptResponse struct { + // decrypted output + Message TPM2BPublicKeyRSA +} + // ECDHZGen is the input to TPM2_ECDHZGen. // See definition in Part 3, Commands, section 14.5 type ECDHZGen struct {