azure-protected-vm-secrets/Windows/BcryptECDiffieHellman.cpp (618 lines of code) (raw):
#include "..\pch.h"
//#ifndef PLATFORM_UNIX
#define UMDF_USING_NTSTATUS
#include "windows.h"
#ifndef _NTSTATUS_
#include "ntstatus.h"
#endif // !_NTSTATUS_
#include "bcrypt.h"
#include "wincrypt.h"
#include "..\BcryptError.h"
//#endif // !PLATFORM_UNIX
#include "..\ECDiffieHellman.h"
#include "BcryptECDiffieHellman.h"
#include "..\LibraryLogger.h"
#include "..\ReturnCodes.h"
#include <vector>
using namespace SecretsLogger;
// ASN.1 constants. The unknown ASN.1 bytes are referenced in other
// implementations of ECDiffieHellman, but the definitions could not
// be found in the Windows API documentation.
#define EC_UNCOMPRESSED_BLOB 0x04
#define EC_UNKNOWN_ASN_BYTE 0x06
#define EC_UNKNOWN_ASN_BYTE2 0x07
#define EC_PUBLIC_NUM_COMPONENTS 2
#define EC_PRIVATE_NUM_COMPONENTS 3
BcryptECDiffieHellman::BcryptECDiffieHellman()
{
#ifndef PLATFORM_UNIX
NTSTATUS status = STATUS_SUCCESS;
this->hSharedSecret = nullptr;
this->hEccKeyHandle = nullptr;
this->hEcHandle = nullptr;
status = BCryptOpenAlgorithmProvider(&(this->hEcHandle), BCRYPT_ECDH_P256_ALGORITHM, NULL, 0);
if (status != STATUS_SUCCESS) {
// LibraryError, Bcrypt subclass, providerError
throw BcryptError(status, "BCryptOpenAlgorithmProvider failed.\n",
ErrorCode::LibraryError_Bcrypt_providerError);
}
#endif // !PLATFORM_UNIX
}
BcryptECDiffieHellman::~BcryptECDiffieHellman()
{
#ifndef PLATFORM_UNIX
if (this->hSharedSecret != nullptr) {
BCryptDestroySecret(this->hSharedSecret);
}
if (this->hEccKeyHandle != nullptr) {
BCryptDestroyKey(this->hEccKeyHandle);
}
if (this->hEcHandle != nullptr) {
BCryptCloseAlgorithmProvider(this->hEcHandle, 0);
}
#endif // !PLATFORM_UNIX
}
void BcryptECDiffieHellman::GenerateKeyPair() {
#ifndef PLATFORM_UNIX
NTSTATUS status;
status = BCryptGenerateKeyPair(
this->hEcHandle,
&(this->hEccKeyHandle),
256,
0);
if (status != STATUS_SUCCESS) {
// CryptographyError, ECC subclass, keyError
throw BcryptError(status, "BCryptGenerateKeyPair failed.\n",
ErrorCode::CryptographyError_ECC_keyError);
}
status = BCryptFinalizeKeyPair(this->hEccKeyHandle, 0);
if (status != STATUS_SUCCESS) {
// CryptographyError, ECC subclass, keyError
throw BcryptError(status, "BCryptFinalizeKeyPair failed.\n",
ErrorCode::CryptographyError_ECC_keyError);
}
#endif // !PLATFORM_UNIX
}
void BcryptECDiffieHellman::ImportPkcs8PrivateKey(std::vector<unsigned char> const&derPrivateKey)
{
#ifndef PLATFORM_UNIX
PCRYPT_PRIVATE_KEY_INFO privateKeyInfo = nullptr;
DWORD publicKeyInfoLen = 0;
CRYPT_ECC_PRIVATE_KEY_INFO* eccPrivateInfo = nullptr;
DWORD eccKeyInfoLen = 0;
ULONG cbPubKey = 0;
BCRYPT_ECCKEY_BLOB* pekb = nullptr;
NTSTATUS status = STATUS_SUCCESS;
if (!CryptDecodeObjectEx(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
PKCS_PRIVATE_KEY_INFO,
derPrivateKey.data(), derPrivateKey.size(),
CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
NULL,
&privateKeyInfo, &publicKeyInfoLen))
{
// ParsingError, ASN subclass, x509PrivKeyError
throw WinCryptError("CryptDecodeObjectEx 1 failed.", GetLastError(),
ErrorCode::ParsingError_Asn1_x509PrivKeyError);
}
if (!CryptDecodeObjectEx(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
X509_ECC_PRIVATE_KEY,
privateKeyInfo->PrivateKey.pbData, privateKeyInfo->PrivateKey.cbData,
CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
NULL,
&eccPrivateInfo, &eccKeyInfoLen))
{
// ParsingError, ASN subclass, x509PrivKeyError
throw WinCryptError("CryptDecodeObjectEx 2 failed.", GetLastError(),
ErrorCode::ParsingError_Asn1_x509PrivKeyError);
}
if (NULL != eccPrivateInfo->PublicKey.pbData &&
eccPrivateInfo->PublicKey.cbData > 0)
{
if (
EC_UNCOMPRESSED_BLOB != eccPrivateInfo->PublicKey.pbData[0] &&
EC_UNKNOWN_ASN_BYTE != eccPrivateInfo->PublicKey.pbData[0] &&
EC_UNKNOWN_ASN_BYTE2 != eccPrivateInfo->PublicKey.pbData[0]
)
{
// ParsingError, ASN subclass, x509PrivKeyError
throw WinCryptError("Error with public key info", 0,
ErrorCode::ParsingError_Asn1_x509PrivKeyError);
}
cbPubKey = (eccPrivateInfo->PublicKey.cbData - 1) / EC_PUBLIC_NUM_COMPONENTS;
}
else
{
cbPubKey = 0;
}
ULONG cbKey = eccPrivateInfo->PrivateKey.cbData;
if (cbKey < eccPrivateInfo->PrivateKey.cbData ||
cbKey < cbPubKey)
{
LIBSECRETS_LOG(
LogLevel::Info,
"EC Diffie Hellman Import",
"Error with private key info");
}
LIBSECRETS_LOG(
LogLevel::Debug,
"EC Diffie Hellman Import",
"Passed private validation");
ULONG cbKeyBlob = sizeof(BCRYPT_ECCKEY_BLOB) + EC_PRIVATE_NUM_COMPONENTS * cbKey;
pekb = (BCRYPT_ECCKEY_BLOB*)malloc(cbKeyBlob);
if (pekb == nullptr) {
// GenericError, Memory subclass, mallocError
throw std::exception("ECDH - Malloc Fail");
}
ZeroMemory(pekb, cbKeyBlob);
pekb->dwMagic = BCRYPT_ECDH_PRIVATE_P256_MAGIC;
pekb->cbKey = cbKey;
BYTE* pb = NULL;
if (cbPubKey > 0)
{
if (cbKey == cbPubKey)
{
CopyMemory(pekb + 1, eccPrivateInfo->PublicKey.pbData + 1, EC_PUBLIC_NUM_COMPONENTS * cbKey);
}
else
{
ULONG off = 0;
pb = (BYTE*)(pekb + 1);
off = cbKey - cbPubKey;
CopyMemory(pb + off, eccPrivateInfo->PublicKey.pbData + 1, cbPubKey);
CopyMemory(pb + cbKey + off, eccPrivateInfo->PublicKey.pbData + 1 + cbPubKey, cbPubKey);
}
}
pb = (BYTE*)(pekb + 1) + EC_PUBLIC_NUM_COMPONENTS * cbKey;
CopyMemory(pb + (cbKey - eccPrivateInfo->PrivateKey.cbData),
eccPrivateInfo->PrivateKey.pbData,
eccPrivateInfo->PrivateKey.cbData);
if (privateKeyInfo != nullptr) {
LocalFree(privateKeyInfo);
}
if (eccPrivateInfo != nullptr) {
LocalFree(eccPrivateInfo);
}
if (cbKeyBlob != sizeof(BCRYPT_ECCKEY_BLOB) + pekb->cbKey * EC_PRIVATE_NUM_COMPONENTS) {
// ParsingError, ASN subclass, x509PrivKeyError
throw WinCryptError("Invalid key blob", 0,
ErrorCode::ParsingError_Asn1_x509PrivKeyError);
}
status = BCryptImportKeyPair(this->hEcHandle, NULL, BCRYPT_ECCPRIVATE_BLOB, &(this->hEccKeyHandle), (PUCHAR)(pekb), cbKeyBlob, 0);
if (status != STATUS_SUCCESS) {
// LibraryError, Bcrypt subclass, keyError
throw BcryptError(status, "BCryptImportKeyPair 1 failed.\n",
ErrorCode::LibraryError_Bcrypt_keyError);
}
#endif // !PLATFORM_UNIX
}
void BcryptECDiffieHellman::ImportSubjectPublicKeyInfo(std::vector<unsigned char> const&derPublicKey) {
#ifndef PLATFORM_UNIX
DWORD publicKeyInfoLen = 0;
HCRYPTPROV hProv = 0;
HCRYPTKEY hKey = 0;
CERT_PUBLIC_KEY_INFO certPubInfo{};
ULONG cb = 0;
NTSTATUS status;
union {
PVOID pvStructInfo;
PCERT_INFO pCertInfo;
PCERT_PUBLIC_KEY_INFO PublicKeyInfo;
};
if (!CryptDecodeObjectEx(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
X509_PUBLIC_KEY_INFO,
derPublicKey.data(), derPublicKey.size(),
CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
NULL,
&pvStructInfo, &cb))
{
// ParsingError, ASN subclass, x509PubKeyError
throw WinCryptError("CryptDecodeObjectEx 1 failed.", GetLastError(),
ErrorCode::ParsingError_Asn1_x509PubKeyError);
}
LIBSECRETS_LOG(
LogLevel::Debug,
"EC Diffie Hellman Import",
"Public key size: PublicKeyInfo->PublicKey.cbData %d",
PublicKeyInfo->PublicKey.cbData);
CRYPT_ECC_PRIVATE_KEY_INFO* eccPrivateInfo = nullptr;
DWORD eccKeyInfoLen = 0;
ULONG cbPubKey = 0;
if (NULL != PublicKeyInfo->PublicKey.pbData &&
PublicKeyInfo->PublicKey.cbData > 0
)
{
if (
EC_UNCOMPRESSED_BLOB != PublicKeyInfo->PublicKey.pbData[0] &&
EC_UNKNOWN_ASN_BYTE != PublicKeyInfo->PublicKey.pbData[0] &&
EC_UNKNOWN_ASN_BYTE2 != PublicKeyInfo->PublicKey.pbData[0]
)
{
LIBSECRETS_LOG(
LogLevel::Debug,
"EC Diffie Hellman Import",
"Error with public key info: PublicKeyInfo->PublicKey.pbData[0] %x",
PublicKeyInfo->PublicKey.pbData[0]);
}
cbPubKey = (PublicKeyInfo->PublicKey.cbData - 1) / EC_PUBLIC_NUM_COMPONENTS;
}
else
{
cbPubKey = 0;
}
LIBSECRETS_LOG(LogLevel::Debug, "EC Diffie Hellman Import", "Passed private validation", nullptr);
ULONG cbKey = cbPubKey;
ULONG cbKeyBlob = sizeof(BCRYPT_ECCKEY_BLOB) + EC_PUBLIC_NUM_COMPONENTS * cbKey;
BCRYPT_ECCKEY_BLOB* pekb = (BCRYPT_ECCKEY_BLOB*)malloc(cbKeyBlob);
if (pekb == NULL) {
// GenericError, Memory subclass, mallocError
throw std::exception("ECDH - Malloc Failed");
}
ZeroMemory(pekb, cbKeyBlob);
pekb->dwMagic = BCRYPT_ECDH_PUBLIC_P256_MAGIC;
pekb->cbKey = cbKey;
BYTE* pb = NULL;
if (cbPubKey > 0)
{
if (cbKey == cbPubKey)
{
CopyMemory(pekb + 1, PublicKeyInfo->PublicKey.pbData + 1, EC_PUBLIC_NUM_COMPONENTS * cbKey);
}
else
{
ULONG off = 0;
pb = (BYTE*)(pekb + 1);
off = cbKey - cbPubKey;
CopyMemory(pb + off, PublicKeyInfo->PublicKey.pbData + 1, cbPubKey);
CopyMemory(pb + cbKey + off, PublicKeyInfo->PublicKey.pbData + 1 + cbPubKey, cbPubKey);
}
}
if (PublicKeyInfo != nullptr) {
LocalFree(PublicKeyInfo);
}
if (cbKeyBlob != sizeof(BCRYPT_ECCKEY_BLOB) + pekb->cbKey * EC_PUBLIC_NUM_COMPONENTS) {
// ParsingError, ASN subclass, x509PubKeyError
throw WinCryptError("Invalid key blob", 0,
ErrorCode::ParsingError_Asn1_x509PubKeyError);
}
status = BCryptImportKeyPair(this->hEcHandle, NULL, BCRYPT_ECCPUBLIC_BLOB, &(this->hEccKeyHandle), (PUCHAR)(pekb), cbKeyBlob, 0);
if (status != STATUS_SUCCESS) {
// LibraryError, Bcrypt subclass, keyError
throw BcryptError(status, "BCryptImportKeyPair 1 failed.\n",
ErrorCode::LibraryError_Bcrypt_keyError);
}
#endif // !PLATFORM_UNIX
}
std::vector<unsigned char> BcryptECDiffieHellman::ExportPkcs8PrivateKey() const
{
std::vector<unsigned char> result;
#ifndef PLATFORM_UNIX
DWORD privateKeyBlobLen = 0;
NTSTATUS status;
CRYPT_ECC_PRIVATE_KEY_INFO eccPrivateInfo = { 0 };
CRYPT_PRIVATE_KEY_INFO* privateKeyInfo = nullptr;
DWORD privateKeyInfoLen = 0;
BYTE* pbCurveParameters = nullptr;
DWORD cbCurveParameters = 0;
BCRYPT_ECCKEY_BLOB* pekb = nullptr;
// Get the size of the private key blob
status = BCryptExportKey(this->hEccKeyHandle, NULL, BCRYPT_ECCPRIVATE_BLOB, NULL, 0, &privateKeyBlobLen, 0);
if (status != STATUS_SUCCESS) {
// LibraryError, Bcrypt subclass, keyError
throw BcryptError(status, "BCryptExportKey - Get Size - failed.\n",
ErrorCode::LibraryError_Bcrypt_keyError);
}
unsigned char* privateKeyBlob = (unsigned char *)malloc(privateKeyBlobLen);
// Export the private key blob
status = BCryptExportKey(this->hEccKeyHandle, NULL, BCRYPT_ECCPRIVATE_BLOB, privateKeyBlob, privateKeyBlobLen, &privateKeyBlobLen, 0);
if (status != STATUS_SUCCESS) {
// LibraryError, Bcrypt subclass, keyError
throw BcryptError(status, "BCryptExportKey - Export Key - failed.\n",
ErrorCode::LibraryError_Bcrypt_keyError);
}
pekb = (BCRYPT_ECCKEY_BLOB*)privateKeyBlob;
// Get the size of the curve parameters
status = BCryptGetProperty(
this->hEccKeyHandle,
BCRYPT_ECC_PARAMETERS,
NULL, // pbOutput
0, // cbOutput
&cbCurveParameters,
0 // dwFlags
);
if (status != STATUS_SUCCESS) {
// LibraryError, Bcrypt subclass, propertyError
throw BcryptError(status, "BCryptGetProperty Size failed.\n",
ErrorCode::LibraryError_Bcrypt_propertyError);
}
// Get the curve parameters
pbCurveParameters = (BYTE*)malloc(cbCurveParameters);
status = BCryptGetProperty(
this->hEccKeyHandle,
BCRYPT_ECC_PARAMETERS,
pbCurveParameters,
cbCurveParameters,
&cbCurveParameters,
0);
if (status != STATUS_SUCCESS) {
// LibraryError, Bcrypt subclass, propertyError
throw BcryptError(status, "BCryptGetProperty Value failed.\n",
ErrorCode::LibraryError_Bcrypt_propertyError);
}
CRYPT_DATA_BLOB CNGECCBlob = { 0 };
CNGECCBlob.cbData = cbCurveParameters;
CNGECCBlob.pbData = pbCurveParameters;
ULONG cbCurveInfo = 0;
// Get the size of the curve info
if (!CryptEncodeObject(
X509_ASN_ENCODING,
X509_ECC_PARAMETERS,
&CNGECCBlob,
NULL,
&cbCurveInfo))
{
// ParsingError, ASN subclass, x509PrivKeyError
throw WinCryptError("CryptEncodeObject 1 failed.", GetLastError(),
ErrorCode::ParsingError_Asn1_x509PrivKeyError);
}
eccPrivateInfo.dwVersion = CRYPT_ECC_PRIVATE_KEY_INFO_v1;
eccPrivateInfo.szCurveOid = NULL;
eccPrivateInfo.PrivateKey.pbData = (BYTE*)(pekb + 1) + EC_PUBLIC_NUM_COMPONENTS * pekb->cbKey;
eccPrivateInfo.PrivateKey.cbData = pekb->cbKey;
ULONG cbPublicKey;
BYTE* pbPublicKey;
pbPublicKey = (BYTE*)&pekb[1];
cbPublicKey = pekb->cbKey;
eccPrivateInfo.PublicKey.cbData = 1 + 2 * cbPublicKey;
eccPrivateInfo.PublicKey.cUnusedBits = 0;
eccPrivateInfo.PublicKey.pbData = (BYTE*)malloc(eccPrivateInfo.PublicKey.cbData);
ZeroMemory(eccPrivateInfo.PublicKey.pbData, eccPrivateInfo.PublicKey.cbData);
eccPrivateInfo.PublicKey.pbData[0] = EC_UNCOMPRESSED_BLOB;
CopyMemory(
eccPrivateInfo.PublicKey.pbData + 1,
pbPublicKey,
cbPublicKey);
CopyMemory(
eccPrivateInfo.PublicKey.pbData + 1 + cbPublicKey,
pbPublicKey + pekb->cbKey,
cbPublicKey);
CRYPT_BIT_BLOB CryptBitBlob = { 0 };
CryptBitBlob.cbData = 1;
CryptBitBlob.pbData = (BYTE*)malloc(1);
CryptBitBlob.pbData[0] = CERT_KEY_AGREEMENT_KEY_USAGE;
CryptBitBlob.cUnusedBits = 0;
ULONG cbKeyUsage = 0;
ULONG cbEncodedKey = 0;
// Get the size of the key
if (!CryptEncodeObjectEx(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
X509_ECC_PRIVATE_KEY,
(BYTE*)(&eccPrivateInfo),
0,
NULL,
NULL, &cbEncodedKey))
{
// ParsingError, ASN subclass, x509PrivKeyError
throw WinCryptError("CryptEncodeObjectEx 1 failed.", GetLastError(),
ErrorCode::ParsingError_Asn1_x509PrivKeyError);
}
ULONG cbPriKeyInfo = cbPriKeyInfo = sizeof(*privateKeyInfo) + sizeof(szOID_ECC_PUBLIC_KEY);
if (cbKeyUsage > 0)
{
cbPriKeyInfo +=
sizeof(CRYPT_ATTRIBUTES) +
sizeof(CRYPT_ATTRIBUTE) +
sizeof(CRYPT_ATTR_BLOB) +
sizeof(szOID_KEY_USAGE);
}
cbPriKeyInfo += cbCurveInfo;
cbPriKeyInfo += cbKeyUsage;
cbPriKeyInfo += cbEncodedKey;
BYTE* pb = NULL;
privateKeyInfo = (PCRYPT_PRIVATE_KEY_INFO)malloc(cbPriKeyInfo);
if (privateKeyInfo == NULL) {
// GenericError, Memory subclass, mallocError
throw std::exception("ECDH Malloc Fail");
}
ZeroMemory(privateKeyInfo, cbPriKeyInfo);
if (cbKeyUsage > 0)
{
CRYPT_ATTRIBUTES* pAttrList = (CRYPT_ATTRIBUTES*)(privateKeyInfo + 1);
CRYPT_ATTRIBUTE* pAttr = (CRYPT_ATTRIBUTE*)(pAttrList + 1);
CRYPT_ATTR_BLOB* pAttrBlob = (CRYPT_ATTR_BLOB*)(pAttr + 1);
privateKeyInfo->pAttributes = pAttrList;
pAttrList->cAttr = 1;
pAttrList->rgAttr = pAttr;
pAttr->pszObjId = (PSTR)(pAttrBlob + 1);
pAttr->cValue = 1;
pAttr->rgValue = pAttrBlob;
pAttrBlob->cbData = cbKeyUsage;
pAttrBlob->pbData =
(BYTE*)(pAttr->pszObjId + sizeof(szOID_KEY_USAGE));
CopyMemory(
pAttr->pszObjId,
szOID_KEY_USAGE,
sizeof(szOID_KEY_USAGE));
if (!CryptEncodeObject(
X509_ASN_ENCODING,
X509_BITS,
&CryptBitBlob,
pAttrBlob->pbData,
&pAttrBlob->cbData))
{
// ParsingError, ASN subclass, x509PrivKeyError
throw WinCryptError("CryptEncodeObjectEx 2 failed.", GetLastError(),
ErrorCode::ParsingError_Asn1_x509PrivKeyError);
}
pb = pAttrBlob->pbData + pAttrBlob->cbData;
}
else
{
pb = (BYTE*)(privateKeyInfo + 1);
}
privateKeyInfo->Algorithm.Parameters.cbData = cbCurveInfo;
privateKeyInfo->Algorithm.Parameters.pbData = pb + cbEncodedKey;
privateKeyInfo->Algorithm.pszObjId =
(PSTR)(pb + cbEncodedKey + cbCurveInfo);
privateKeyInfo->PrivateKey.cbData = cbEncodedKey;
privateKeyInfo->PrivateKey.pbData = pb;
// Encode the key
if (!CryptEncodeObjectEx(
X509_ASN_ENCODING,
X509_ECC_PRIVATE_KEY,
&eccPrivateInfo,
0,
NULL,
privateKeyInfo->PrivateKey.pbData,
&privateKeyInfo->PrivateKey.cbData))
{
// ParsingError, ASN subclass, x509PrivKeyError
throw WinCryptError("CryptEncodeObjectEx 3 failed.", GetLastError(),
ErrorCode::ParsingError_Asn1_x509PrivKeyError);
}
// Encode the curve parameters
if (!CryptEncodeObject(
X509_ASN_ENCODING,
X509_ECC_PARAMETERS,
&CNGECCBlob,
privateKeyInfo->Algorithm.Parameters.pbData,
&privateKeyInfo->Algorithm.Parameters.cbData))
{
// ParsingError, ASN subclass, x509PrivKeyError
throw WinCryptError("CryptEncodeObjectEx 4 failed.", GetLastError(),
ErrorCode::ParsingError_Asn1_x509PrivKeyError);
}
CopyMemory(
privateKeyInfo->Algorithm.pszObjId,
szOID_ECC_PUBLIC_KEY,
sizeof(szOID_ECC_PUBLIC_KEY));
ULONG derPrivateBytesLen = 0;
unsigned char *derPrivateBytes = nullptr;
// Encode the private key
if (!CryptEncodeObjectEx(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
PKCS_PRIVATE_KEY_INFO,
(BYTE*)privateKeyInfo,
CRYPT_ENCODE_ALLOC_FLAG,
NULL,
&derPrivateBytes, &derPrivateBytesLen))
{
// ParsingError, ASN subclass, x509PrivKeyError
throw WinCryptError("CryptEncodeObjectEx 5 failed.", GetLastError(),
ErrorCode::ParsingError_Asn1_x509PrivKeyError);
}
result = std::vector<unsigned char>(derPrivateBytes, derPrivateBytes + derPrivateBytesLen);
#endif // !PLATFORM_UNIX
return result;
}
std::vector<unsigned char> BcryptECDiffieHellman::ExportSubjectPublicKeyInfo() const
{
std::vector<unsigned char> result;
#ifndef PLATFORM_UNIX
DWORD publicKeyBlobLen = 0;
NTSTATUS status = STATUS_SUCCESS;
CRYPT_ECC_PRIVATE_KEY_INFO eccPrivateInfo = { 0 };
CRYPT_PRIVATE_KEY_INFO privateKeyInfo = { 0 };
DWORD privateKeyInfoLen = 0;
BYTE* pbCurveParameters = nullptr;
DWORD cbCurveParameters = 0;
BCRYPT_ECCKEY_BLOB* pekb = nullptr;
CERT_PUBLIC_KEY_INFO publicKeyInfo = { 0 };
union {
PVOID pvStructInfo;
PCERT_INFO pCertInfo;
PCERT_PUBLIC_KEY_INFO PublicKeyInfo;
};
// Get the size of the public key blob
status = BCryptExportKey(this->hEccKeyHandle, NULL, BCRYPT_ECCPUBLIC_BLOB, NULL, 0, &publicKeyBlobLen, 0);
if (status != STATUS_SUCCESS) {
// LibraryError, Bcrypt subclass, keyError
throw BcryptError(status, "BCryptExportKey - Get Size - failed.\n",
ErrorCode::LibraryError_Bcrypt_keyError);
}
unsigned char* publicKeyBlob = (unsigned char*)malloc(publicKeyBlobLen);
if (publicKeyBlob == NULL) {
// GenericError, Memory subclass, mallocError
throw std::exception("ECDH - Malloc Fail");
}
// Export the public key blob
status = BCryptExportKey(this->hEccKeyHandle, NULL, BCRYPT_ECCPUBLIC_BLOB, publicKeyBlob, publicKeyBlobLen, &publicKeyBlobLen, 0);
if (status != STATUS_SUCCESS) {
// LibraryError, Bcrypt subclass, keyError
throw BcryptError(status, "BCryptExportKey - Export Key - failed.\n",
ErrorCode::LibraryError_Bcrypt_keyError);
}
pekb = (BCRYPT_ECCKEY_BLOB*)publicKeyBlob;
CRYPT_BIT_BLOB CryptBitBlob = { 0 };
CryptBitBlob.cbData = 1;
CryptBitBlob.pbData = (BYTE*)malloc(1);
CryptBitBlob.pbData[0] = CERT_KEY_AGREEMENT_KEY_USAGE;
CryptBitBlob.cUnusedBits = 0;
// Get the size of the curve parameters
status = BCryptGetProperty(
this->hEccKeyHandle,
BCRYPT_ECC_PARAMETERS,
NULL, // pbOutput
0, // cbOutput
&cbCurveParameters,
0 // dwFlags
);
if (status != STATUS_SUCCESS) {
// LibraryError, Bcrypt subclass, propertyError
throw BcryptError(status, "BCryptGetProperty Size failed.\n",
ErrorCode::LibraryError_Bcrypt_propertyError);
}
pbCurveParameters = (BYTE*)malloc(cbCurveParameters);
if (pbCurveParameters == nullptr) {
// GenericError, Memory subclass, mallocError
throw std::exception("ECDH - Malloc Failed");
}
// Get the curve parameters
status = BCryptGetProperty(
this->hEccKeyHandle,
BCRYPT_ECC_PARAMETERS,
pbCurveParameters,
cbCurveParameters,
&cbCurveParameters,
0);
if (status != STATUS_SUCCESS) {
// LibraryError, Bcrypt subclass, propertyError
throw BcryptError(status, "BCryptGetProperty Value failed.\n",
ErrorCode::LibraryError_Bcrypt_propertyError);
}
CRYPT_DATA_BLOB CNGECCBlob = { 0 };
CNGECCBlob.cbData = cbCurveParameters;
CNGECCBlob.pbData = pbCurveParameters;
ULONG cbCurveInfo = 0;
if (!CryptEncodeObject(
X509_ASN_ENCODING,
X509_ECC_PARAMETERS,
&CNGECCBlob,
NULL,
&cbCurveInfo))
{
// ParsingError, ASN subclass, x509PubKeyError
throw WinCryptError("CryptEncodeObject 1 failed.", GetLastError(),
ErrorCode::ParsingError_Asn1_x509PubKeyError);
}
ULONG cbPupKeyInfo = sizeof(publicKeyInfo) + sizeof(szOID_ECC_PUBLIC_KEY);
cbPupKeyInfo += cbCurveInfo;
publicKeyInfo.PublicKey.cbData = pekb->cbKey * EC_PUBLIC_NUM_COMPONENTS + 1;
publicKeyInfo.PublicKey.cUnusedBits = 0;
publicKeyInfo.PublicKey.pbData = (BYTE*)malloc(publicKeyInfo.PublicKey.cbData);
if (publicKeyInfo.PublicKey.pbData == NULL) {
// GenericError, Memory subclass, mallocError
throw std::exception("ECDH - Malloc Fail");
}
ZeroMemory(publicKeyInfo.PublicKey.pbData, publicKeyInfo.PublicKey.cbData);
publicKeyInfo.PublicKey.pbData[0] = EC_UNCOMPRESSED_BLOB;
CopyMemory(
publicKeyInfo.PublicKey.pbData + 1, pekb + 1, pekb->cbKey * EC_PUBLIC_NUM_COMPONENTS);
publicKeyInfo.Algorithm.Parameters.cbData = cbCurveInfo;
publicKeyInfo.Algorithm.Parameters.pbData = (BYTE*)malloc(publicKeyInfo.Algorithm.Parameters.cbData);
if (publicKeyInfo.Algorithm.Parameters.pbData == NULL) {
// GenericError, Memory subclass, mallocError
throw std::exception("ECDH - Malloc Fail");
}
publicKeyInfo.Algorithm.pszObjId = (LPSTR)malloc(sizeof(szOID_ECC_PUBLIC_KEY));
if (publicKeyInfo.Algorithm.pszObjId == NULL) {
// GenericError, Memory subclass, mallocError
throw std::exception("ECDH - Malloc Fail");
}
ZeroMemory(publicKeyInfo.Algorithm.Parameters.pbData, publicKeyInfo.Algorithm.Parameters.cbData);
if (!CryptEncodeObject(
X509_ASN_ENCODING,
X509_ECC_PARAMETERS,
&CNGECCBlob,
publicKeyInfo.Algorithm.Parameters.pbData,
&publicKeyInfo.Algorithm.Parameters.cbData))
{
// ParsingError, ASN subclass, x509PubKeyError
throw WinCryptError("CryptEncodeObject 2 failed.", GetLastError(),
ErrorCode::ParsingError_Asn1_x509PubKeyError);
}
CopyMemory(
publicKeyInfo.Algorithm.pszObjId,
szOID_ECC_PUBLIC_KEY,
sizeof(szOID_ECC_PUBLIC_KEY));
ULONG derPublicBytesLen = 0;
unsigned char* derPublicBytes = nullptr;
if (!CryptEncodeObjectEx(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
X509_PUBLIC_KEY_INFO,
(BYTE*)(&publicKeyInfo),
CRYPT_ENCODE_ALLOC_FLAG,
NULL,
&derPublicBytes, &derPublicBytesLen))
{
// ParsingError, ASN subclass, x509PubKeyError
throw WinCryptError("CryptEncodeObjectEx 1 failed.", GetLastError(),
ErrorCode::ParsingError_Asn1_x509PubKeyError);
}
result = std::vector<unsigned char>(derPublicBytes, derPublicBytes + derPublicBytesLen);
#endif // !PLATFORM_UNIX
return result;
}
BCRYPT_SECRET_HANDLE BcryptECDiffieHellman::DeriveSecret(ECDiffieHellman &otherParty)
{
#ifndef PLATFORM_UNIX
BCRYPT_SECRET_HANDLE hSecret;
NTSTATUS status = BCryptSecretAgreement(this->hEccKeyHandle, otherParty.GetPublicKeyHandle(), &(this->hSharedSecret), 0);
if (status != STATUS_SUCCESS) {
// CryptographyError, ECC subclass, keyGenerationError
throw BcryptError(status, "BCryptSecretAgreement failed.\n",
ErrorCode::CryptographyError_ECC_keyGenError);
}
#endif // !PLATFORM_UNIX
return this->hSharedSecret;
}
BCRYPT_KEY_HANDLE BcryptECDiffieHellman::GetPublicKeyHandle(void) const {
return this->hEccKeyHandle;
}