azure-protected-vm-secrets/Tss2Wrapper.cpp (247 lines of code) (raw):
#include "pch.h"
#include <fstream>
#include <string>
#include <vector>
#include <iostream>
#include "DebugInfo.h"
#include <tss2/tss2_esys.h>
#include "TpmError.h"
#include "TssCtx.h"
#include "Tss2Wrapper.h"
#include "LibraryLogger.h"
#include "ReturnCodes.h"
#ifndef PLATFORM_UNIX
#include <windows.h>
#include <../shared/tbs.h>
#pragma comment(lib, "Tbs.lib")
#endif // PLATFORM_UNIX
#define SRKHANDLE 0x81000001
#define KEYHANDLE SRKHANDLE + 3
#define RSA_PUBLIC_EXPONENT 0x00010001
Tss2Wrapper::Tss2Wrapper()
{
this->ctx = std::make_unique<TssCtx>();
}
TPM2_RC Tss2Wrapper::RemoveKey() {
ESYS_TR object_handle = {};
TPM2_RC ret = TSS2_RC_SUCCESS;
// Get Esys object for handle.
ret = Esys_TR_FromTPMPublic(
this->ctx->Get(), KEYHANDLE,
ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
&object_handle);
if (ret != TSS2_RC_SUCCESS) {
// TpmError, Subclass Handles, handlePresentError
throw TpmError(ret, "Failed to get object from handle",
ErrorCode::TpmError_Handles_handlePresentError);
}
// Evict the key
ret = Esys_EvictControl(this->ctx->Get(),
ESYS_TR_RH_OWNER,
object_handle,
ESYS_TR_PASSWORD,
ESYS_TR_NONE,
ESYS_TR_NONE,
KEYHANDLE,
&object_handle);
if (ret != TSS2_RC_SUCCESS) {
// TpmError, Subclass Handles, evictControlError
throw TpmError(ret, "Failed to Evict object at handle",
ErrorCode::TpmError_Handles_evictControlError);
}
return ret;
}
TPM2B_PUBLIC* Tss2Wrapper::GenerateGuestKey()
{
TPM2B_PUBLIC inPub = { 0 };
TPM2B_SENSITIVE_CREATE inPriv = { 0 };
TPM2B_DATA inOutsideInfo = { 0 };
TPML_PCR_SELECTION inPcr = { 0 };
ESYS_TR primaryHandle = ESYS_TR_NONE;
ESYS_TR persistObjHandle = ESYS_TR_NONE;
TPM2B_AUTH authValuePrimary = {
0, // size
{} // buffer
};
TPM2B_SENSITIVE_CREATE inSensitivePrimary = {
0, // size
{ // sensitive
{ // userAuth
0, //size
{0}, // buffer
},
{ // data
0, // size
{0}, // buffer
},
},
};
inSensitivePrimary.sensitive.userAuth = authValuePrimary;
inPub.publicArea.type = TPM2_ALG_RSA;
inPub.publicArea.nameAlg = TPM2_ALG_SHA256;
inPub.publicArea.objectAttributes = (
TPMA_OBJECT_DECRYPT |
TPMA_OBJECT_USERWITHAUTH |
TPMA_OBJECT_FIXEDTPM |
TPMA_OBJECT_FIXEDPARENT |
TPMA_OBJECT_SENSITIVEDATAORIGIN);
inPub.publicArea.unique.rsa.size = 256;
// Set the alg to TPM2_ALG_NULL so the caller can specify the alg such as TPM2_ALG_OAEP or TPM2_ALG_RSAES
inPub.publicArea.parameters.rsaDetail.scheme.scheme = TPM2_ALG_NULL;
inPub.publicArea.parameters.rsaDetail.exponent = RSA_PUBLIC_EXPONENT;
inPub.publicArea.parameters.rsaDetail.keyBits = 2048;
inPub.publicArea.parameters.rsaDetail.symmetric.algorithm = TPM2_ALG_NULL;
inPub.publicArea.authPolicy = { 0 };
inPub.publicArea.authPolicy.size = 0;
TPM2B_PUBLIC* outPub;
// Create primary
TSS2_RC ret = Esys_CreatePrimary(
this->ctx->Get(),
ESYS_TR_RH_OWNER,
ESYS_TR_PASSWORD,
ESYS_TR_NONE,
ESYS_TR_NONE,
&inPriv,
&inPub,
&inOutsideInfo,
&inPcr,
&primaryHandle,
&outPub,
nullptr,
nullptr,
nullptr);
if (ret != TSS2_RC_SUCCESS) {
// TpmError, Subclass Objects, createError
throw TpmError(ret, "Failed to create primary object under storage hierarchy",
ErrorCode::TpmError_Objects_createError);
}
LIBSECRETS_LOG(SecretsLogger::LogLevel::Debug, "Create Pimary",
"Public key info %s",
formatHexBuffer(outPub->publicArea.unique.rsa.buffer, outPub->publicArea.unique.rsa.size).c_str());
ret = Esys_EvictControl(
this->ctx->Get(),
ESYS_TR_RH_OWNER,
primaryHandle,
ESYS_TR_PASSWORD,
ESYS_TR_NONE,
ESYS_TR_NONE,
KEYHANDLE,
&persistObjHandle);
if (ret != TSS2_RC_SUCCESS) {
// TpmError, Subclass Handles, evictControlError
throw TpmError(ret, "Failed to EvictControl key",
ErrorCode::TpmError_Handles_evictControlError);
}
return outPub;
}
bool Tss2Wrapper::IsKeyPresent() {
ESYS_TR object_handle = {};
TPM2_RC ret = TSS2_RC_SUCCESS;
// Get Esys object for handle.
ret = Esys_TR_FromTPMPublic(
this->ctx->Get(), KEYHANDLE,
ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
&object_handle);
if (ret != TSS2_RC_SUCCESS) {
return false;
}
return true;
}
#define RSA_KEY_SIZE 2048
std::vector<unsigned char> Tss2Wrapper::Tss2RsaEncrypt(std::vector<unsigned char> const&plaintextData) {
TSS2_RC r;
ESYS_TR primaryHandle = ESYS_TR_NONE;
ESYS_TR persistObjHandle = ESYS_TR_NONE;
TPM2B_PUBLIC* outPublic = nullptr;
TPM2B_CREATION_DATA* creationData = nullptr;
TPM2B_DIGEST* creationHash = nullptr;
TPMT_TK_CREATION* creationTicket = nullptr;
TPM2B_PUBLIC_KEY_RSA* cipher = nullptr;
TPM2B_PUBLIC_KEY_RSA* plain2 = nullptr;
TPM2B_DATA* null_data = nullptr;
std::vector<unsigned char> retval = std::vector<unsigned char>();
TPM2B_AUTH authValuePrimary = {
0, // size
{} // buffer
};
r = Esys_TR_FromTPMPublic(
this->ctx->Get(), KEYHANDLE,
ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
&primaryHandle);
if (r != TSS2_RC_SUCCESS)
{
// TpmError, Subclass Handles, handlePresentError
throw TpmError(r, "Failed to read tpm object from handle",
ErrorCode::TpmError_Handles_handlePresentError);
}
r = Esys_TR_SetAuth(this->ctx->Get(), primaryHandle,
&authValuePrimary);
if (r != TSS2_RC_SUCCESS)
{
// TpmError, Subclass Auth, setAuthError
throw TpmError(r, "Failed to set auth",
ErrorCode::TpmError_Auth_setAuthError);
}
size_t plain_size = 3;
TPM2B_PUBLIC_KEY_RSA plain = { 0 };
std::copy(plaintextData.begin(), plaintextData.end(), plain.buffer);
plain.size = plaintextData.size();
TPMT_RSA_DECRYPT scheme;
scheme.scheme = TPM2_ALG_RSAES;
r = Esys_RSA_Encrypt(this->ctx->Get(), primaryHandle, ESYS_TR_NONE,
ESYS_TR_NONE, ESYS_TR_NONE, &plain, &scheme,
null_data, &cipher);
if (r != TSS2_RC_SUCCESS)
{
// CryptoError, Subclass TpmRsa, encryptError
throw TpmError(r, "Failed to Encrypt data",
ErrorCode::CryptographyError_TpmRsa_encryptError);
}
Esys_Free(null_data);
retval.insert(retval.end(), cipher->buffer, cipher->buffer + cipher->size);
return retval;
}
std::vector<unsigned char> Tss2Wrapper::Tss2RsaDecrypt(std::vector<unsigned char> const&encryptedData) {
TPM2B_PUBLIC* out_public = 0;
ESYS_TR ephemeral_handle = ESYS_TR_NONE;
std::vector<unsigned char> retval = std::vector<unsigned char>();
ESYS_TR object_handle = {};
ESYS_TR srk_handle = {};
ESYS_TR keyHandle, session;
TSS2_RC r;
ESYS_TR primaryHandle = ESYS_TR_NONE;
TPM2B_PUBLIC_KEY_RSA cipher = { 0 };
TPM2B_PUBLIC_KEY_RSA* plain2 = nullptr;
TPM2B_DATA* null_data = nullptr;
TPM2B_AUTH authValue = {
0, // size
{} // buffer
};
r = Esys_TR_SetAuth(this->ctx->Get(), ESYS_TR_RH_OWNER, &authValue);
if (r != TSS2_RC_SUCCESS)
{
// TpmError, Subclass Auth, setAuthError
throw TpmError(r, "Failed to set auth",
ErrorCode::TpmError_Auth_setAuthError);
}
r = Esys_TR_FromTPMPublic(
this->ctx->Get(), KEYHANDLE,
ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
&primaryHandle);
if (r != TSS2_RC_SUCCESS)
{
// TpmError, Subclass Handles, handlePresentError
throw TpmError(r, "Failed to read tpm object from handle",
ErrorCode::TpmError_Handles_handlePresentError);
}
r = Esys_TR_SetAuth(this->ctx->Get(), primaryHandle,
&authValue);
if (r != TSS2_RC_SUCCESS)
{
// TpmError, Subclass Auth, setAuthError
throw TpmError(r, "Failed to set auth",
ErrorCode::TpmError_Auth_setAuthError);
}
// Set plaintext data
TPM2B_PUBLIC_KEY_RSA plain = { 0 };
std::copy(encryptedData.begin(), encryptedData.end(), cipher.buffer);
cipher.size = encryptedData.size();
// Set scheme
TPMT_RSA_DECRYPT scheme;
scheme.scheme = TPM2_ALG_RSAES;
// Execute decrypt
r = Esys_RSA_Decrypt(this->ctx->Get(), primaryHandle,
ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
&cipher, &scheme, null_data, &plain2);
if (r != TSS2_RC_SUCCESS)
{
// CryptoError, Subclass TpmRsa, decryptError
throw TpmError(r, "Failed to Decrypt data",
ErrorCode::CryptographyError_TpmRsa_decryptError);
}
retval.insert(retval.end(), plain2->buffer, plain2->buffer + plain2->size);
Esys_Free(null_data);
Esys_Free(plain2);
return retval;
}