azure-protected-vm-secrets/Linux/OsslECDiffieHellman.cpp (125 lines of code) (raw):

//#include "ECDiffieHellman.h" #include "../LibraryLogger.h" #include "OsslECDiffieHellman.h" #include "OsslError.h" #include <openssl/evp.h> #include <openssl/ec.h> #include <openssl/pem.h> #include <openssl/x509.h> #include <openssl/err.h> #include <stdexcept> #include <iostream> #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 void handleOpenSSLError() { unsigned long errCode; while ((errCode = ERR_get_error())) { char *err = ERR_error_string(errCode, NULL); std::cerr << "OpenSSL Error: " << err << std::endl; } } OsslECDiffieHellman::OsslECDiffieHellman() : keyPair(nullptr, &EVP_PKEY_free), pctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL), &EVP_PKEY_CTX_free) { if (!pctx) { throw std::runtime_error("Failed to create ECDH context"); } } OsslECDiffieHellman::~OsslECDiffieHellman() { } void OsslECDiffieHellman::GenerateKeyPair() { if (EVP_PKEY_paramgen_init(pctx.get()) <= 0) { throw OsslError(ERR_get_error(), "Failed to initialize ECDH parameters"); } if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx.get(), NID_X9_62_prime256v1) <= 0) { throw OsslError(ERR_get_error(), "Failed to set ECDH curve"); } // Generate the parameters EVP_PKEY *params = nullptr; if (EVP_PKEY_paramgen(pctx.get(), &params) <= 0) { throw OsslError(ERR_get_error(), "Failed to generate parameters"); } std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> params_ptr(params, &EVP_PKEY_free); // Create a context for the key generation std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)> kctx(EVP_PKEY_CTX_new(params_ptr.get(), NULL), &EVP_PKEY_CTX_free); if (!kctx) { throw OsslError(ERR_get_error(), "Failed to create context for key generation"); } // Initialize the context for key generation if (EVP_PKEY_keygen_init(kctx.get()) <= 0) { throw OsslError(ERR_get_error(), "Failed to initialize key generation"); } // Generate the key pair EVP_PKEY* keyPairRef = keyPair.get(); if (EVP_PKEY_keygen(kctx.get(), &keyPairRef) <= 0) { throw OsslError(ERR_get_error(), "Failed to generate key pair"); } keyPair.reset(keyPairRef); } void OsslECDiffieHellman::ImportPkcs8PrivateKey(std::vector<unsigned char> const&derPrivateKey) { const unsigned char* keyData = derPrivateKey.data(); EVP_PKEY* privateKey = d2i_PrivateKey(EVP_PKEY_EC, NULL, &keyData, derPrivateKey.size()); if (!privateKey) { throw OsslError(ERR_get_error(), "Failed to import PKCS#8 private key"); } keyPair.reset(privateKey); } void OsslECDiffieHellman::ImportSubjectPublicKeyInfo(std::vector<unsigned char> const&derPublicKey) { const unsigned char* keyData = derPublicKey.data(); EVP_PKEY* publicKey = d2i_PUBKEY(NULL, &keyData, derPublicKey.size()); if (!publicKey) { throw OsslError(ERR_get_error(), "Failed to import SubjectPublicKeyInfo"); } keyPair.reset(publicKey); } std::vector<unsigned char> OsslECDiffieHellman::ExportPkcs8PrivateKey() const { unsigned char* keyData = NULL; int len = i2d_PrivateKey(keyPair.get(), &keyData); if (len <= 0) { throw OsslError(ERR_get_error(), "Failed to export PKCS#8 private key"); } std::vector<unsigned char> privateKey(keyData, keyData + len); OPENSSL_free(keyData); return privateKey; } std::vector<unsigned char> OsslECDiffieHellman::ExportSubjectPublicKeyInfo() const { unsigned char* keyData = NULL; int len = i2d_PUBKEY(keyPair.get(), &keyData); if (len <= 0) { throw OsslError(ERR_get_error(), "Failed to export SubjectPublicKeyInfo"); } std::vector<unsigned char> publicKey(keyData, keyData + len); OPENSSL_free(keyData); return publicKey; } std::vector<unsigned char> OsslECDiffieHellman::DeriveSecret(ECDiffieHellman &key2) { const OsslECDiffieHellman& osslOtherParty = static_cast<const OsslECDiffieHellman&>(key2); EVP_PKEY* peerKey = osslOtherParty.GetPublicKeyHandle(); std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)> derivationCtx(EVP_PKEY_CTX_new(keyPair.get(), NULL), &EVP_PKEY_CTX_free); if (!derivationCtx) { throw OsslError(ERR_get_error(), "Failed to create derivation context"); } if (EVP_PKEY_derive_init(derivationCtx.get()) <= 0) { throw OsslError(ERR_get_error(), "Failed to initialize derivation context"); } if (EVP_PKEY_derive_set_peer(derivationCtx.get(), peerKey) <= 0) { throw OsslError(ERR_get_error(), "Failed to set peer public key"); } size_t secretLen; if (EVP_PKEY_derive(derivationCtx.get(), NULL, &secretLen) <= 0) { throw OsslError(ERR_get_error(), "Failed to determine secret length"); } std::vector<unsigned char> sharedSecret(secretLen); if (EVP_PKEY_derive(derivationCtx.get(), sharedSecret.data(), &secretLen) <= 0) { throw OsslError(ERR_get_error(), "Failed to derive shared secret"); } return sharedSecret; } EVP_PKEY* OsslECDiffieHellman::GetPublicKeyHandle(void) const { return this->keyPair.get(); }