cvm-securekey-release-app/AttestationUtil.h (153 lines of code) (raw):
//-------------------------------------------------------------------------------------------------
// <copyright file="AttestationUtil.h" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//-------------------------------------------------------------------------------------------------
#pragma once
#include <vector>
#include <string>
#include <algorithm>
#include <stdarg.h>
#ifdef _MSC_VER
#include <windows.h> // for HRESULT
#include <bcrypt.h>
// #include <fveapi.h>
#else
typedef int HRESULT;
#define FAILED(hr) (((HRESULT)(hr)) < 0)
typedef unsigned long DWORD;
#define GetLastError() errno
#endif
#include <openssl/opensslv.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/pkcs7.h>
#include <openssl/pem.h>
typedef unsigned char BYTE;
typedef unsigned char *PBYTE;
#define CHECK_HR(hr) \
do \
{ \
Check_HResult(__FILE__, __func__, __LINE__, hr); \
} while (0);
inline static void Check_HResult(std::string fileName, std::string funcName, int lineNo, HRESULT hr)
{
if (FAILED(hr))
{
DWORD gle = GetLastError();
fprintf(stderr, "Failed in %s:%s() on line %d. HR=0x%0x", fileName.c_str(), funcName.c_str(), lineNo, hr);
exit(gle);
}
}
#define TRACE_ERROR_EXIT(msg) \
do \
{ \
DWORD gle = GetLastError(); \
fprintf(stderr, "Error occured in %s:%s on line %d. Msg=%s", __FILE__, __func__, __LINE__, msg); \
exit(gle); \
} while (0);
#define TRACE_OUT Util::trace_out
#define OSSL_BN_TRACE_OUT Util::ossl_bn_trace_out
class Util
{
private:
static bool isTraceOn;
static int traceLevel; // 1: enable Util::reduct_log, 2: do nothing.
static size_t lengthMask;
public:
static void set_trace(bool traceOn)
{
isTraceOn = traceOn;
}
static bool get_trace()
{
return isTraceOn;
}
static void set_trace_level(int trLevel)
{
traceLevel = trLevel;
}
static int get_trace_level()
{
return traceLevel;
}
inline static void trace_out(std::string fmt, ...)
{
if (isTraceOn)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt.c_str(), args);
fprintf(stderr, "\n");
va_end(args);
}
}
inline static std::string reduct_log(const std::string &str)
{
std::string retStr(str);
if (traceLevel == 1)
{
double percentage = reduct_log_percentage(str);
size_t lengthMask = retStr.size() * (percentage / 100);
if (retStr.size() > lengthMask)
{
retStr.resize(lengthMask);
retStr.append("...");
}
}
return retStr.c_str();
}
inline static double reduct_log_percentage(const std::string &str)
{
std::string err = "error";
auto it = std::search(str.begin(), str.end(), err.begin(), err.end(),
[](char a, char b)
{ return std::tolower(a) == std::tolower(b); });
if (it != str.end())
return 100;
return 15;
}
inline static void ossl_bn_trace_out(const BIGNUM *bn)
{
if (isTraceOn)
{
BN_print_fp(stderr, bn);
}
}
enum class AkvCredentialSource
{
Imds,
EnvServicePrincipal
};
/// <summary>
/// Convert a base64 encoded string to a vector of bytes.
/// </summary>
/// <param name="base64_data">Base64 encoded string</param>
/// <returns>Vector of BYTES</returns>
static std::vector<BYTE> base64_to_binary(const std::string &base64_data);
/// <summary>
/// Convert a vector of bytes to a base64 encoded string.
/// </summary>
/// <param name="binary_data">Vector of unsinged chars.</param>
/// <returns>Base64 encoded string</returns>
static std::string binary_to_base64(const std::vector<BYTE> &binary_data);
/// <summary>
/// Convert a vector of bytes to a base64url encoded string.
/// </summary>
/// <param name="binary_data">Vector of BYTES.</param>
/// <returns>Base64url encoded string.</returns>
static std::string binary_to_base64url(const std::vector<BYTE> &binary_data);
/// <summary>
/// Convert a vector of bytes to a hex encoded string.
/// </summary>
/// <param name="binary_data">Vector of BYTES.</param>
/// <returns>Hex encoded string.</returns>
static std::string binary_to_hex(const std::vector<BYTE> &binary_data);
/// <summary>
/// Convert a hex encoded string to vector of bytes.
/// </summary>
/// <param name="hex_data">Hex encoded data.</param>
/// <returns>Vector of BYTES.</returns>
static std::vector<BYTE> hex_to_binary(const std::string &hex_data);
/// <summary>
/// Convert a base64url encoded string to a vector of bytes.
/// </summary>
/// <param name="base64url_data">Base64url encoded string.</param>
/// <returns>Vector of BYTES.</returns>
static std::vector<BYTE> base64url_to_binary(const std::string &base64url_data);
/// <summary>
/// Convert a base64 decoded string to string.
/// </summary>
/// <param name="data">Base64 encoded string.</param>
/// <returns>Plain string</returns>
static std::string base64_decode(const std::string &data);
/// <summary>
/// Encode input string for url
/// </summary>
/// <param name="data">input data</param>
/// <returns>Escaped string</returns>
static std::string url_encode(const std::string &data);
/// <summary>
/// Callback for curl perform operation.
/// </summary>
static size_t CurlWriteCallback(char *data, size_t size, size_t nmemb, std::string *buffer);
/// <summary>
/// Get a REST URL for secure key release
/// </summary>
/// <param name="KEKUrl">Key encryption key URL</param>
/// <returns>REST URL string</returns>
static std::string GetKeyVaultSKRurl(const std::string &KEKUrl);
/// <summary>
/// Get secure key release response from KMS (Key Vault or mHSM)
/// </summary>
/// <param name="requestUri">SKR request URL</param>
/// <param name="access_token">Authorization token</param>
/// <param name="attestation_token">Attestation token</param>
/// <param name="nonce">Nonce value to send</param>
/// <returns>SKR reponse from KMS</returns>
static std::string GetKeyVaultResponse(const std::string &requestUri,
const std::string &access_token,
const std::string &attestation_token,
const std::string &nonce);
/// <summary>
/// Retrieve MSI token from IMDS servce
/// </summary>
/// <param name="KEKUrl">Key encryption key URL</param>
/// <returns>MSI token for the resource</returns>
static std::string GetIMDSToken(const std::string &KEKUrl);
// <summary>
/// Retrieve MSI token from Service Principal Credentials available in the Environment Variables
/// </summary>
/// <param name="KEKUrl">Key encryption key URL</param>
/// <returns>MSI token for the resource</returns>
static std::string GetAADToken(const std::string &KEKUrl);
/// <summary>
/// Get attestation token from the attestation service.
/// </summary>
/// <param name="attestation_url">Attestation service URL.</param>
/// <param name="nonce">unique nonce per attestation request.</param>
/// <returns>MAA token</returns>
static std::string GetMAAToken(const std::string &attestation_url, const std::string &nonce);
/// <summary>
/// Split string by delimeter.
/// </summary>
/// <param name="delimiter">Delimeter character.</param>
/// <returns>Vector of strings</returns>
static std::vector<std::string> SplitString(const std::string &s, char delimiter);
/// <summary>
/// Do secure key release (SKR) to get the key encryption key (KEK).
/// </summary>
/// <param name="attestation_url">Attestation service URL.</param>
/// <param name="nonce">unique nonce per attestation request.</param>
/// <param name="KEKUrl">Key encryption key URL</param>
/// <param name="pkey">OpenSSL key representation</param>
/// <param name="akv_credential_source">AkvCredentialSource type for accessing Key Vault</param>
/// <returns>True if successful</returns>
static bool doSKR(const std::string &attestation_url,
const std::string &nonce,
std::string KEKUrl,
EVP_PKEY **pkey,
const Util::AkvCredentialSource &akv_credential_source);
/// <summary>
/// Wrap the symmetric key with the public key of the key encryption key (KEK).
/// </summary>
/// <param name="attestation_url">Attestation service URL.</param>
/// <param name="nonce">unique nonce per attestation request.</param>
/// <param name="plainText">Plain text symmetric key</param>
/// <param name="key_enc_key">KEK</param>
/// <param name="akv_credential_source">AkvCredentialSource type for accessing Key Vault</param>
/// <returns>Wrapped key</returns>
static std::string WrapKey(const std::string &attestation_url,
const std::string &nonce,
const std::string &plainText,
const std::string &key_enc_key,
const Util::AkvCredentialSource &akv_credential_source);
/// <summary>
/// Unwrap the symmetric key using the private key of the key encryption key (KEK).
/// </summary>
/// <param name="attestation_url">Attestation service URL.</param>
/// <param name="nonce">unique nonce per attestation request.</param>
/// <param name="cipherText">Wrapped symmetric key</param>
/// <param name="key_enc_key">KEK</param>
/// <param name="akv_credential_source">AkvCredentialSource type for accessing Key Vault</param>
/// <returns>Plain text symmetric key</returns>
static std::string UnwrapKey(const std::string &attestation_url,
const std::string &nonce,
const std::string &cipherText,
const std::string &key_enc_key,
const Util::AkvCredentialSource &akv_credential_source);
/// <summary>
/// Release the RSA or EC private key from KMS.
/// </summary>
/// <param name="attestation_url">Attestation service URL.</param>
/// <param name="nonce">unique nonce per attestation request.</param>
/// <param name="key_enc_key">KEK</param>
/// <param name="akv_credential_source">AkvCredentialSource type for accessing Key Vault</param>
/// <returns>True if key release succeeds, False otherwise</returns>
static bool ReleaseKey(const std::string &attestation_url,
const std::string &nonce,
const std::string &key_enc_key,
const Util::AkvCredentialSource &akv_credential_source);
};