diff options
author | B. Stack <bgstack15@gmail.com> | 2023-01-22 13:56:55 -0500 |
---|---|---|
committer | B. Stack <bgstack15@gmail.com> | 2023-01-22 13:56:55 -0500 |
commit | 75e05bc441382db69c842a64c562738cb749214e (patch) | |
tree | 698b60b3b4b914bf7958cf1174d0373909bf1e8f /zen/open_ssl.cpp | |
parent | add upstream 11.29 (diff) | |
download | FreeFileSync-75e05bc441382db69c842a64c562738cb749214e.tar.gz FreeFileSync-75e05bc441382db69c842a64c562738cb749214e.tar.bz2 FreeFileSync-75e05bc441382db69c842a64c562738cb749214e.zip |
add upstream 12.0
Diffstat (limited to 'zen/open_ssl.cpp')
-rw-r--r-- | zen/open_ssl.cpp | 461 |
1 files changed, 212 insertions, 249 deletions
diff --git a/zen/open_ssl.cpp b/zen/open_ssl.cpp index 9278b6dd..af4306b2 100644 --- a/zen/open_ssl.cpp +++ b/zen/open_ssl.cpp @@ -8,15 +8,15 @@ #include <bit> //std::endian (needed for macOS) #include "base64.h" #include "thread.h" +#include "argon2.h" +#include "serialize.h" #include <openssl/pem.h> #include <openssl/err.h> #include <openssl/ssl.h> -#if OPENSSL_VERSION_NUMBER >= 0x30000000L - #include <openssl/core_names.h> - #include <openssl/encoder.h> - #include <openssl/decoder.h> - #include <openssl/param_build.h> -#endif +#include <openssl/core_names.h> +#include <openssl/encoder.h> +#include <openssl/decoder.h> +#include <openssl/param_build.h> using namespace zen; @@ -26,7 +26,7 @@ using namespace zen; #error FFS, we are royally screwed! #endif -static_assert(OPENSSL_VERSION_NUMBER >= 0x10100000L, "OpenSSL version too old"); +static_assert(OPENSSL_VERSION_NUMBER >= 0x30000000L, "OpenSSL version too old"); void zen::openSslInit() @@ -79,7 +79,7 @@ std::wstring formatOpenSSLError(const char* functionName, unsigned long ec) std::wstring formatLastOpenSSLError(const char* functionName) { - const auto ec = ::ERR_peek_last_error(); + const auto ec = ::ERR_peek_last_error(); //"returns latest error code from the thread's error queue without modifying it" - unlike ERR_get_error() ::ERR_clear_error(); //clean up for next OpenSSL operation on this thread return formatOpenSSLError(functionName, ec); } @@ -110,13 +110,13 @@ std::shared_ptr<EVP_PKEY> generateRsaKeyPair(int bits) //throw SysError //================================================================================ -std::shared_ptr<EVP_PKEY> streamToKey(const std::string& keyStream, RsaStreamType streamType, bool publicKey) //throw SysError +std::shared_ptr<EVP_PKEY> streamToKey(const std::string_view keyStream, RsaStreamType streamType, bool publicKey) //throw SysError { switch (streamType) { case RsaStreamType::pkix: { - BIO* bio = ::BIO_new_mem_buf(keyStream.c_str(), static_cast<int>(keyStream.size())); + BIO* bio = ::BIO_new_mem_buf(keyStream.data(), static_cast<int>(keyStream.size())); if (!bio) throw SysError(formatLastOpenSSLError("BIO_new_mem_buf")); ZEN_ON_SCOPE_EXIT(::BIO_free_all(bio)); @@ -134,7 +134,6 @@ std::shared_ptr<EVP_PKEY> streamToKey(const std::string& keyStream, RsaStreamTyp case RsaStreamType::pkcs1: { -#if OPENSSL_VERSION_NUMBER >= 0x30000000L EVP_PKEY* evp = nullptr; auto guardEvp = makeGuard<ScopeGuardRunMode::onExit>([&] { if (evp) ::EVP_PKEY_free(evp); }); @@ -159,46 +158,20 @@ std::shared_ptr<EVP_PKEY> streamToKey(const std::string& keyStream, RsaStreamTyp throw SysError(formatLastOpenSSLError("OSSL_DECODER_CTX_set_passphrase")); #endif - const unsigned char* keyBuf = reinterpret_cast<const unsigned char*>(keyStream.c_str()); + const unsigned char* keyBuf = reinterpret_cast<const unsigned char*>(keyStream.data()); size_t keyLen = keyStream.size(); if (::OSSL_DECODER_from_data(decCtx, &keyBuf, &keyLen) != 1) throw SysError(formatLastOpenSSLError("OSSL_DECODER_from_data")); guardEvp.dismiss(); //pass ownership return std::shared_ptr<EVP_PKEY>(evp, ::EVP_PKEY_free); // -#else - BIO* bio = ::BIO_new_mem_buf(keyStream.c_str(), static_cast<int>(keyStream.size())); - if (!bio) - throw SysError(formatLastOpenSSLError("BIO_new_mem_buf")); - ZEN_ON_SCOPE_EXIT(::BIO_free_all(bio)); - - RSA* rsa = (publicKey ? - ::PEM_read_bio_RSAPublicKey : - ::PEM_read_bio_RSAPrivateKey)(bio, //BIO* bp - nullptr, //RSA** x - nullptr, //pem_password_cb* cb - nullptr); //void* u - if (!rsa) - throw SysError(formatLastOpenSSLError(publicKey ? "PEM_read_bio_RSAPublicKey" : "PEM_read_bio_RSAPrivateKey")); - ZEN_ON_SCOPE_EXIT(::RSA_free(rsa)); - - EVP_PKEY* evp = ::EVP_PKEY_new(); - if (!evp) - throw SysError(formatLastOpenSSLError("EVP_PKEY_new")); - std::shared_ptr<EVP_PKEY> sharedKey(evp, ::EVP_PKEY_free); - - if (::EVP_PKEY_set1_RSA(evp, rsa) != 1) //no ownership transfer (internally ref-counted) - throw SysError(formatLastOpenSSLError("EVP_PKEY_set1_RSA")); - - return sharedKey; -#endif } case RsaStreamType::raw: break; } - auto tmp = reinterpret_cast<const unsigned char*>(keyStream.c_str()); + auto tmp = reinterpret_cast<const unsigned char*>(keyStream.data()); EVP_PKEY* evp = (publicKey ? ::d2i_PublicKey : ::d2i_PrivateKey)(EVP_PKEY_RSA, //int type nullptr, //EVP_PKEY** a &tmp, /*changes tmp pointer itself!*/ //const unsigned char** pp @@ -255,7 +228,6 @@ std::string keyToStream(const EVP_PKEY* evp, RsaStreamType streamType, bool publ case RsaStreamType::pkcs1: { -#if OPENSSL_VERSION_NUMBER >= 0x30000000L const int selection = publicKey ? OSSL_KEYMGMT_SELECT_PUBLIC_KEY : OSSL_KEYMGMT_SELECT_PRIVATE_KEY; OSSL_ENCODER_CTX* encCtx = ::OSSL_ENCODER_CTX_new_for_pkey(evp, //const EVP_PKEY* pkey @@ -276,46 +248,6 @@ std::string keyToStream(const EVP_PKEY* evp, RsaStreamType streamType, bool publ ZEN_ON_SCOPE_EXIT(::OPENSSL_free(keyBuf)); return {reinterpret_cast<const char*>(keyBuf), keyLen}; -#else - //fix OpenSSL API inconsistencies: - auto PEM_write_bio_RSAPrivateKey2 = [](BIO* bio, const RSA* rsa) - { - return ::PEM_write_bio_RSAPrivateKey(bio, //BIO* bp - rsa, //const RSA* x - nullptr, //const EVP_CIPHER* enc - nullptr, //const unsigned char* kstr - 0, //int klen - nullptr, //pem_password_cb* cb - nullptr); //void* u - }; - auto PEM_write_bio_RSAPublicKey2 = [](BIO* bio, const RSA* rsa) { return ::PEM_write_bio_RSAPublicKey(bio, rsa); }; - - BIO* bio = ::BIO_new(BIO_s_mem()); - if (!bio) - throw SysError(formatLastOpenSSLError("BIO_new")); - ZEN_ON_SCOPE_EXIT(::BIO_free_all(bio)); - - const RSA* rsa = ::EVP_PKEY_get0_RSA(evp); //unowned reference! - if (!rsa) - throw SysError(formatLastOpenSSLError("EVP_PKEY_get0_RSA")); - - if ((publicKey ? - PEM_write_bio_RSAPublicKey2 : - PEM_write_bio_RSAPrivateKey2)(bio, rsa) != 1) - throw SysError(formatLastOpenSSLError(publicKey ? "PEM_write_bio_RSAPublicKey" : "PEM_write_bio_RSAPrivateKey")); - //--------------------------------------------- - const int keyLen = BIO_pending(bio); - if (keyLen < 0) - throw SysError(formatLastOpenSSLError("BIO_pending")); - if (keyLen == 0) - throw SysError(formatSystemError("BIO_pending", L"", L"Unexpected failure.")); //no more error details - - std::string keyStream(keyLen, '\0'); - - if (::BIO_read(bio, keyStream.data(), keyLen) != keyLen) - throw SysError(formatLastOpenSSLError("BIO_read")); - return keyStream; -#endif } case RsaStreamType::raw: @@ -333,13 +265,51 @@ std::string keyToStream(const EVP_PKEY* evp, RsaStreamType streamType, bool publ //================================================================================ -std::string createSignature(const std::string& message, EVP_PKEY* privateKey) //throw SysError +std::string createHash(const std::string_view str, const EVP_MD* type) //throw SysError +{ + std::string output(EVP_MAX_MD_SIZE, '\0'); + unsigned int bytesWritten = 0; +#if 1 + //https://www.openssl.org/docs/manmaster/man3/EVP_Digest.html + if (::EVP_Digest(str.data(), //const void* data + str.size(), //size_t count + reinterpret_cast<unsigned char*>(output.data()), //unsigned char* md + &bytesWritten, //unsigned int* size + type, //const EVP_MD* type + nullptr) != 1) //ENGINE* impl + throw SysError(formatLastOpenSSLError("EVP_Digest")); +#else //streaming version + EVP_MD_CTX* mdctx = ::EVP_MD_CTX_new(); + if (!mdctx) + throw SysError(formatSystemError("EVP_MD_CTX_new", L"", L"Unexpected failure.")); //no more error details + ZEN_ON_SCOPE_EXIT(::EVP_MD_CTX_free(mdctx)); + + if (::EVP_DigestInit(mdctx, //EVP_MD_CTX* ctx + type) != 1) //const EVP_MD* type + throw SysError(formatLastOpenSSLError("EVP_DigestInit")); + + if (::EVP_DigestUpdate(mdctx, //EVP_MD_CTX* ctx + str.data(), //const void* + str.size()) != 1) //size_t cnt); + throw SysError(formatLastOpenSSLError("EVP_DigestUpdate")); + + if (::EVP_DigestFinal_ex(mdctx, //EVP_MD_CTX* ctx + reinterpret_cast<unsigned char*>(output.data()), //unsigned char* md + &bytesWritten) != 1) //unsigned int* s + throw SysError(formatLastOpenSSLError("EVP_DigestFinal_ex")); +#endif + output.resize(bytesWritten); + return output; +} + + +std::string createSignature(const std::string_view message, EVP_PKEY* privateKey) //throw SysError { //https://www.openssl.org/docs/manmaster/man3/EVP_DigestSign.html - EVP_MD_CTX* mdctx = ::EVP_MD_CTX_create(); + EVP_MD_CTX* mdctx = ::EVP_MD_CTX_new(); if (!mdctx) - throw SysError(formatSystemError("EVP_MD_CTX_create", L"", L"Unexpected failure.")); //no more error details - ZEN_ON_SCOPE_EXIT(::EVP_MD_CTX_destroy(mdctx)); + throw SysError(formatSystemError("EVP_MD_CTX_new", L"", L"Unexpected failure.")); //no more error details + ZEN_ON_SCOPE_EXIT(::EVP_MD_CTX_free(mdctx)); if (::EVP_DigestSignInit(mdctx, //EVP_MD_CTX* ctx nullptr, //EVP_PKEY_CTX** pctx @@ -349,7 +319,7 @@ std::string createSignature(const std::string& message, EVP_PKEY* privateKey) // throw SysError(formatLastOpenSSLError("EVP_DigestSignInit")); if (::EVP_DigestSignUpdate(mdctx, //EVP_MD_CTX* ctx - message.c_str(), //const void* d + message.data(), //const void* d message.size()) != 1) //size_t cnt throw SysError(formatLastOpenSSLError("EVP_DigestSignUpdate")); @@ -372,13 +342,13 @@ std::string createSignature(const std::string& message, EVP_PKEY* privateKey) // } -void verifySignature(const std::string& message, const std::string& signature, EVP_PKEY* publicKey) //throw SysError +void verifySignature(const std::string_view message, const std::string_view signature, EVP_PKEY* publicKey) //throw SysError { //https://www.openssl.org/docs/manmaster/man3/EVP_DigestVerify.html - EVP_MD_CTX* mdctx = ::EVP_MD_CTX_create(); + EVP_MD_CTX* mdctx = ::EVP_MD_CTX_new(); if (!mdctx) - throw SysError(formatSystemError("EVP_MD_CTX_create", L"", L"Unexpected failure.")); //no more error details - ZEN_ON_SCOPE_EXIT(::EVP_MD_CTX_destroy(mdctx)); + throw SysError(formatSystemError("EVP_MD_CTX_new", L"", L"Unexpected failure.")); //no more error details + ZEN_ON_SCOPE_EXIT(::EVP_MD_CTX_free(mdctx)); if (::EVP_DigestVerifyInit(mdctx, //EVP_MD_CTX* ctx nullptr, //EVP_PKEY_CTX** pctx @@ -388,19 +358,19 @@ void verifySignature(const std::string& message, const std::string& signature, E throw SysError(formatLastOpenSSLError("EVP_DigestVerifyInit")); if (::EVP_DigestVerifyUpdate(mdctx, //EVP_MD_CTX* ctx - message.c_str(), //const void* d + message.data(), //const void* d message.size()) != 1) //size_t cnt throw SysError(formatLastOpenSSLError("EVP_DigestVerifyUpdate")); - if (::EVP_DigestVerifyFinal(mdctx, //EVP_MD_CTX* ctx - reinterpret_cast<const unsigned char*>(signature.c_str()), //const unsigned char* sig - signature.size()) != 1) //size_t siglen + if (::EVP_DigestVerifyFinal(mdctx, //EVP_MD_CTX* ctx + reinterpret_cast<const unsigned char*>(signature.data()), //const unsigned char* sig + signature.size()) != 1) //size_t siglen throw SysError(formatLastOpenSSLError("EVP_DigestVerifyFinal")); } } -std::string zen::convertRsaKey(const std::string& keyStream, RsaStreamType typeFrom, RsaStreamType typeTo, bool publicKey) //throw SysError +std::string zen::convertRsaKey(const std::string_view keyStream, RsaStreamType typeFrom, RsaStreamType typeTo, bool publicKey) //throw SysError { assert(typeFrom != typeTo); std::shared_ptr<EVP_PKEY> evp = streamToKey(keyStream, typeFrom, publicKey); //throw SysError @@ -408,7 +378,7 @@ std::string zen::convertRsaKey(const std::string& keyStream, RsaStreamType typeF } -void zen::verifySignature(const std::string& message, const std::string& signature, const std::string& publicKeyStream, RsaStreamType streamType) //throw SysError +void zen::verifySignature(const std::string_view message, const std::string_view signature, const std::string_view publicKeyStream, RsaStreamType streamType) //throw SysError { std::shared_ptr<EVP_PKEY> publicKey = streamToKey(publicKeyStream, streamType, true /*publicKey*/); //throw SysError ::verifySignature(message, signature, publicKey.get()); //throw SysError @@ -417,11 +387,11 @@ void zen::verifySignature(const std::string& message, const std::string& signatu bool zen::isPuttyKeyStream(const std::string_view keyStream) { - return startsWith(trimCpy(keyStream, true, false), "PuTTY-User-Key-File-"); + return startsWith(trimCpy(keyStream, true, false), "PuTTY-User-Key-File-"); } -std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std::string& passphrase) //throw SysError +std::string zen::convertPuttyKeyToPkix(const std::string_view keyStream, const std::string_view passphrase) //throw SysError { std::vector<std::string_view> lines; @@ -433,25 +403,39 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: //----------- parse PuTTY ppk structure ---------------------------------- auto itLine = lines.begin(); - if (itLine == lines.end() || !startsWith(*itLine, "PuTTY-User-Key-File-2: ")) - throw SysError(L"Unknown key file format"); - const std::string_view algorithm = afterFirst(*itLine, ' ', IfNotFoundReturn::none); - ++itLine; - if (itLine == lines.end() || !startsWith(*itLine, "Encryption: ")) + auto lineStartsWith = [&](const char* str) + { + return itLine != lines.end() && startsWith(*itLine, str); + }; + + const int ppkFormat = [&] + { + if (lineStartsWith("PuTTY-User-Key-File-2: ")) + return 2; + else if (lineStartsWith("PuTTY-User-Key-File-3: ")) + return 3; + else + throw SysError(L"Unknown key file format"); + }(); + + const std::string_view algorithm = afterFirst(*itLine++, ' ', IfNotFoundReturn::none); + + if (!lineStartsWith("Encryption: ")) + throw SysError(L"Missing key encryption"); + const std::string_view keyEncryption = afterFirst(*itLine++, ' ', IfNotFoundReturn::none); + + const bool keyEncrypted = keyEncryption == "aes256-cbc"; + if (!keyEncrypted && keyEncryption != "none") throw SysError(L"Unknown key encryption"); - const std::string_view keyEncryption = afterFirst(*itLine, ' ', IfNotFoundReturn::none); - ++itLine; - if (itLine == lines.end() || !startsWith(*itLine, "Comment: ")) - throw SysError(L"Invalid key comment"); - const std::string_view comment = afterFirst(*itLine, ' ', IfNotFoundReturn::none); - ++itLine; + if (!lineStartsWith("Comment: ")) + throw SysError(L"Missing comment"); + const std::string_view comment = afterFirst(*itLine++, ' ', IfNotFoundReturn::none); - if (itLine == lines.end() || !startsWith(*itLine, "Public-Lines: ")) - throw SysError(L"Invalid key: invalid public lines"); - size_t pubLineCount = stringTo<size_t>(afterFirst(*itLine, ' ', IfNotFoundReturn::none)); - ++itLine; + if (!lineStartsWith("Public-Lines: ")) + throw SysError(L"Missing public lines"); + size_t pubLineCount = stringTo<size_t>(afterFirst(*itLine++, ' ', IfNotFoundReturn::none)); std::string publicBlob64; while (pubLineCount-- != 0) @@ -460,10 +444,55 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: else throw SysError(L"Invalid key: incomplete public lines"); - if (itLine == lines.end() || !startsWith(*itLine, "Private-Lines: ")) - throw SysError(L"Invalid key: invalid private lines"); - size_t privLineCount = stringTo<size_t>(afterFirst(*itLine, ' ', IfNotFoundReturn::none)); - ++itLine; + Argon2Flavor argonFlavor = Argon2Flavor::d; + uint32_t argonMemory = 0; + uint32_t argonPasses = 0; + uint32_t argonParallelism = 0; + std::string argonSalt; + if (ppkFormat >= 3 && keyEncrypted) + { + if (!lineStartsWith("Key-Derivation: ")) + throw SysError(L"Missing Argon2 parameter: Key-Derivation"); + const std::string_view keyDerivation = afterFirst(*itLine++, ' ', IfNotFoundReturn::none); + + argonFlavor = [&] + { + if (keyDerivation == "Argon2d") + return Argon2Flavor::d; + else if (keyDerivation == "Argon2i") + return Argon2Flavor::i; + else if (keyDerivation == "Argon2id") + return Argon2Flavor::id; + else + throw SysError(L"Unexpected Argon2 parameter for Key-Derivation"); + }(); + + if (!lineStartsWith("Argon2-Memory: ")) + throw SysError(L"Missing Argon2 parameter: Argon2-Memory"); + argonMemory = stringTo<uint32_t>(afterFirst(*itLine++, ' ', IfNotFoundReturn::none)); + + if (!lineStartsWith("Argon2-Passes: ")) + throw SysError(L"Missing Argon2 parameter: Argon2-Passes"); + argonPasses = stringTo<uint32_t>(afterFirst(*itLine++, ' ', IfNotFoundReturn::none)); + + if (!lineStartsWith("Argon2-Parallelism: ")) + throw SysError(L"Missing Argon2 parameter: Argon2-Parallelism"); + argonParallelism = stringTo<uint32_t>(afterFirst(*itLine++, ' ', IfNotFoundReturn::none)); + + if (!lineStartsWith("Argon2-Salt: ")) + throw SysError(L"Missing Argon2 parameter: Argon2-Salt"); + const std::string_view argonSaltHex = afterFirst(*itLine++, ' ', IfNotFoundReturn::none); + + if (argonSaltHex.size() % 2 != 0 || !std::all_of(argonSaltHex.begin(), argonSaltHex.end(), isHexDigit<char>)) + throw SysError(L"Invalid Argon2 parameter: Argon2-Salt"); + + for (size_t i = 0; i < argonSaltHex.size(); i += 2) + argonSalt += unhexify(argonSaltHex[i], argonSaltHex[i + 1]); + } + + if (!lineStartsWith("Private-Lines: ")) + throw SysError(L"Missing private lines"); + size_t privLineCount = stringTo<size_t>(afterFirst(*itLine++, ' ', IfNotFoundReturn::none)); std::string privateBlob64; while (privLineCount-- != 0) @@ -472,16 +501,11 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: else throw SysError(L"Invalid key: incomplete private lines"); - if (itLine == lines.end() || !startsWith(*itLine, "Private-MAC: ")) - throw SysError(L"Invalid key: MAC missing"); - const std::string_view macHex = afterFirst(*itLine, ' ', IfNotFoundReturn::none); - ++itLine; + if (!lineStartsWith("Private-MAC: ")) + throw SysError(L"MAC missing"); //apparently "Private-Hash" is/was possible here: maybe with ppk version 1!? + const std::string_view macHex = afterFirst(*itLine++, ' ', IfNotFoundReturn::none); //----------- unpack key file elements --------------------- - const bool keyEncrypted = keyEncryption == "aes256-cbc"; - if (!keyEncrypted && keyEncryption != "none") - throw SysError(L"Unknown key encryption"); - if (macHex.size() % 2 != 0 || !std::all_of(macHex.begin(), macHex.end(), isHexDigit<char>)) throw SysError(L"Invalid key: invalid MAC"); @@ -493,6 +517,8 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: const std::string privateBlobEnc = stringDecodeBase64(privateBlob64); std::string privateBlob; + std::string macKeyFmt3; + if (!keyEncrypted) privateBlob = privateBlobEnc; else @@ -500,23 +526,40 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: if (passphrase.empty()) throw SysError(L"Passphrase required to access private key"); - const auto block1 = std::string("\0\0\0\0", 4) + passphrase; - const auto block2 = std::string("\0\0\0\1", 4) + passphrase; + const EVP_CIPHER* const cipher = EVP_aes_256_cbc(); + std::string decryptKey; + std::string iv; + if (ppkFormat >= 3) + { + decryptKey.resize(::EVP_CIPHER_get_key_length(cipher)); + iv .resize(::EVP_CIPHER_get_iv_length (cipher)); + macKeyFmt3.resize(32); + + const std::string argonBlob = zargon2(argonFlavor, argonMemory, argonPasses, argonParallelism, + static_cast<uint32_t>(decryptKey.size() + iv.size() + macKeyFmt3.size()), passphrase, argonSalt); + MemoryStreamIn streamIn(argonBlob); + readArray(streamIn, decryptKey.data(), decryptKey.size()); // + readArray(streamIn, iv .data(), iv .size()); //throw SysErrorUnexpectedEos + readArray(streamIn, macKeyFmt3.data(), macKeyFmt3.size()); // + } + else + { + decryptKey = createHash(std::string("\0\0\0\0", 4) + passphrase, EVP_sha1()) + //throw SysError + createHash(std::string("\0\0\0\1", 4) + passphrase, EVP_sha1()); // + decryptKey.resize(::EVP_CIPHER_get_key_length(cipher)); //PuTTYgen only uses first 32 bytes as key (== key length of EVP_aes_256_cbc) - unsigned char key[2 * SHA_DIGEST_LENGTH] = {}; - ::SHA1(reinterpret_cast<const unsigned char*>(block1.c_str()), block1.size(), key); //no-fail - ::SHA1(reinterpret_cast<const unsigned char*>(block2.c_str()), block2.size(), key + SHA_DIGEST_LENGTH); // + iv.assign(::EVP_CIPHER_get_iv_length(cipher), 0); //initialization vector is 16-byte-range of zeros (== default for EVP_aes_256_cbc) + } EVP_CIPHER_CTX* cipCtx = ::EVP_CIPHER_CTX_new(); if (!cipCtx) throw SysError(formatSystemError("EVP_CIPHER_CTX_new", L"", L"Unexpected failure.")); //no more error details ZEN_ON_SCOPE_EXIT(::EVP_CIPHER_CTX_free(cipCtx)); - if (::EVP_DecryptInit_ex(cipCtx, //EVP_CIPHER_CTX* ctx - EVP_aes_256_cbc(), //const EVP_CIPHER* type - nullptr, //ENGINE* impl - key, //const unsigned char* key => implied length of 256 bit! - nullptr) != 1) //const unsigned char* iv + if (::EVP_DecryptInit(cipCtx, //EVP_CIPHER_CTX* ctx + cipher, //const EVP_CIPHER* type + reinterpret_cast<const unsigned char*>(decryptKey.c_str()), //const unsigned char* key + reinterpret_cast<const unsigned char*>(iv.c_str())) != 1) //const unsigned char* iv => nullptr = 16-byte zeros for EVP_aes_256_cbc throw SysError(formatLastOpenSSLError("EVP_DecryptInit_ex")); if (::EVP_CIPHER_CTX_set_padding(cipCtx, 0 /*padding*/) != 1) @@ -527,28 +570,27 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: int decLen1 = 0; if (::EVP_DecryptUpdate(cipCtx, //EVP_CIPHER_CTX* ctx - reinterpret_cast<unsigned char*>(privateBlob.data()), //unsigned char* out + reinterpret_cast<unsigned char*>(privateBlob.data()), //unsigned char* out &decLen1, //int* outl reinterpret_cast<const unsigned char*>(privateBlobEnc.c_str()), //const unsigned char* in static_cast<int>(privateBlobEnc.size())) != 1) //int inl throw SysError(formatLastOpenSSLError("EVP_DecryptUpdate")); int decLen2 = 0; - if (::EVP_DecryptFinal_ex(cipCtx, //EVP_CIPHER_CTX* ctx - reinterpret_cast<unsigned char*>(&privateBlob[decLen1]), //unsigned char* outm - &decLen2) != 1) //int* outl + if (::EVP_DecryptFinal(cipCtx, //EVP_CIPHER_CTX* ctx + reinterpret_cast<unsigned char*>(&privateBlob[decLen1]), //unsigned char* outm + &decLen2) != 1) //int* outl throw SysError(formatLastOpenSSLError("EVP_DecryptFinal_ex")); privateBlob.resize(decLen1 + decLen2); } //----------- verify key consistency --------------------- - std::string macKeyBlob = "putty-private-key-file-mac-key"; - if (keyEncrypted) - macKeyBlob += passphrase; - - unsigned char macKey[SHA_DIGEST_LENGTH] = {}; - ::SHA1(reinterpret_cast<const unsigned char*>(macKeyBlob.c_str()), macKeyBlob.size(), macKey); //no-fail + std::string macKey; + if (ppkFormat >= 3) + macKey = macKeyFmt3; + else + macKey = createHash(std::string("putty-private-key-file-mac-key") + (keyEncrypted ? passphrase : ""), EVP_sha1()); //throw SysError auto numToBeString = [](size_t n) -> std::string { @@ -564,18 +606,17 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: numToBeString(privateBlob .size()) + privateBlob; char md[EVP_MAX_MD_SIZE] = {}; unsigned int mdLen = 0; - if (!::HMAC(EVP_sha1(), //const EVP_MD* evp_md - macKey, //const void* key - sizeof(macKey), //int key_len + if (!::HMAC(ppkFormat <= 2 ? EVP_sha1() : EVP_sha256(), //const EVP_MD* evp_md + macKey.c_str(), //const void* key + static_cast<int>(macKey.size()), //int key_len reinterpret_cast<const unsigned char*>(macData.c_str()), //const unsigned char* d - static_cast<int>(macData.size()), //int n - reinterpret_cast<unsigned char*>(md), //unsigned char* md - &mdLen)) //unsigned int* md_len + static_cast<int>(macData.size()), //int n + reinterpret_cast<unsigned char*>(md), //unsigned char* md + &mdLen)) //unsigned int* md_len throw SysError(formatSystemError("HMAC", L"", L"Unexpected failure.")); //no more error details - const bool hashValid = mac == std::string_view(md, mdLen); - if (!hashValid) - throw SysError(formatSystemError("HMAC", L"", keyEncrypted ? L"Validation failed: wrong passphrase or corrupted key" : L"Validation failed: corrupted key")); + if (mac != std::string_view(md, mdLen)) + throw SysError(keyEncrypted ? L"Wrong passphrase (or corrupted key)" : L"Validation failed: corrupted key"); //---------------------------------------------------------- auto extractString = [](auto& it, auto itEnd) @@ -663,17 +704,16 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: throw SysError(formatLastOpenSSLError("BN_mod")); //---------------------------------------------------------- -#if OPENSSL_VERSION_NUMBER >= 0x30000000L OSSL_PARAM_BLD* paramBld = ::OSSL_PARAM_BLD_new(); if (!paramBld) throw SysError(formatLastOpenSSLError("OSSL_PARAM_BLD_new")); ZEN_ON_SCOPE_EXIT(::OSSL_PARAM_BLD_free(paramBld)); - if (::OSSL_PARAM_BLD_push_BN(paramBld, OSSL_PKEY_PARAM_RSA_N, n.get()) != 1) throw SysError(formatLastOpenSSLError("OSSL_PARAM_BLD_push_BN(n)")); - if (::OSSL_PARAM_BLD_push_BN(paramBld, OSSL_PKEY_PARAM_RSA_E, e.get()) != 1) throw SysError(formatLastOpenSSLError("OSSL_PARAM_BLD_push_BN(e)")); - if (::OSSL_PARAM_BLD_push_BN(paramBld, OSSL_PKEY_PARAM_RSA_D, d.get()) != 1) throw SysError(formatLastOpenSSLError("OSSL_PARAM_BLD_push_BN(d)")); - if (::OSSL_PARAM_BLD_push_BN(paramBld, OSSL_PKEY_PARAM_RSA_FACTOR1, p.get()) != 1) throw SysError(formatLastOpenSSLError("OSSL_PARAM_BLD_push_BN(p)")); - if (::OSSL_PARAM_BLD_push_BN(paramBld, OSSL_PKEY_PARAM_RSA_FACTOR2, q.get()) != 1) throw SysError(formatLastOpenSSLError("OSSL_PARAM_BLD_push_BN(q)")); + if (::OSSL_PARAM_BLD_push_BN(paramBld, OSSL_PKEY_PARAM_RSA_N, n.get()) != 1) throw SysError(formatLastOpenSSLError("OSSL_PARAM_BLD_push_BN(n)")); + if (::OSSL_PARAM_BLD_push_BN(paramBld, OSSL_PKEY_PARAM_RSA_E, e.get()) != 1) throw SysError(formatLastOpenSSLError("OSSL_PARAM_BLD_push_BN(e)")); + if (::OSSL_PARAM_BLD_push_BN(paramBld, OSSL_PKEY_PARAM_RSA_D, d.get()) != 1) throw SysError(formatLastOpenSSLError("OSSL_PARAM_BLD_push_BN(d)")); + if (::OSSL_PARAM_BLD_push_BN(paramBld, OSSL_PKEY_PARAM_RSA_FACTOR1, p.get()) != 1) throw SysError(formatLastOpenSSLError("OSSL_PARAM_BLD_push_BN(p)")); + if (::OSSL_PARAM_BLD_push_BN(paramBld, OSSL_PKEY_PARAM_RSA_FACTOR2, q.get()) != 1) throw SysError(formatLastOpenSSLError("OSSL_PARAM_BLD_push_BN(q)")); if (::OSSL_PARAM_BLD_push_BN(paramBld, OSSL_PKEY_PARAM_RSA_EXPONENT1, dmp1.get()) != 1) throw SysError(formatLastOpenSSLError("OSSL_PARAM_BLD_push_BN(dmp1)")); if (::OSSL_PARAM_BLD_push_BN(paramBld, OSSL_PKEY_PARAM_RSA_EXPONENT2, dmq1.get()) != 1) throw SysError(formatLastOpenSSLError("OSSL_PARAM_BLD_push_BN(dmq1)")); if (::OSSL_PARAM_BLD_push_BN(paramBld, OSSL_PKEY_PARAM_RSA_COEFFICIENT1, iqmp.get()) != 1) throw SysError(formatLastOpenSSLError("OSSL_PARAM_BLD_push_BN(iqmp)")); @@ -696,29 +736,7 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: if (::EVP_PKEY_fromdata(evpCtx, &evp, EVP_PKEY_KEYPAIR, sslParams) != 1) throw SysError(formatLastOpenSSLError("EVP_PKEY_fromdata")); ZEN_ON_SCOPE_EXIT(::EVP_PKEY_free(evp)); -#else - RSA* rsa = ::RSA_new(); - if (!rsa) - throw SysError(formatLastOpenSSLError("RSA_new")); - ZEN_ON_SCOPE_EXIT(::RSA_free(rsa)); - - if (::RSA_set0_key(rsa, n.release(), e.release(), d.release()) != 1) //pass BIGNUM ownership - throw SysError(formatLastOpenSSLError("RSA_set0_key")); - - if (::RSA_set0_factors(rsa, p.release(), q.release()) != 1) - throw SysError(formatLastOpenSSLError("RSA_set0_factors")); - - if (::RSA_set0_crt_params(rsa, dmp1.release(), dmq1.release(), iqmp.release()) != 1) - throw SysError(formatLastOpenSSLError("RSA_set0_crt_params")); - - EVP_PKEY* evp = ::EVP_PKEY_new(); - if (!evp) - throw SysError(formatLastOpenSSLError("EVP_PKEY_new")); - ZEN_ON_SCOPE_EXIT(::EVP_PKEY_free(evp)); - if (::EVP_PKEY_set1_RSA(evp, rsa) != 1) //no ownership transfer (internally ref-counted) - throw SysError(formatLastOpenSSLError("EVP_PKEY_set1_RSA")); -#endif return keyToStream(evp, RsaStreamType::pkix, false /*publicKey*/); //throw SysError } //---------------------------------------------------------- @@ -730,7 +748,6 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: std::unique_ptr<BIGNUM, BnFree> pub = extractBigNumPub (); // std::unique_ptr<BIGNUM, BnFree> pri = extractBigNumPriv(); // //---------------------------------------------------------- -#if OPENSSL_VERSION_NUMBER >= 0x30000000L OSSL_PARAM_BLD* paramBld = ::OSSL_PARAM_BLD_new(); if (!paramBld) throw SysError(formatLastOpenSSLError("OSSL_PARAM_BLD_new")); @@ -760,26 +777,7 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: if (::EVP_PKEY_fromdata(evpCtx, &evp, EVP_PKEY_KEYPAIR, sslParams) != 1) throw SysError(formatLastOpenSSLError("EVP_PKEY_fromdata")); ZEN_ON_SCOPE_EXIT(::EVP_PKEY_free(evp)); -#else - DSA* dsa = ::DSA_new(); - if (!dsa) - throw SysError(formatLastOpenSSLError("DSA_new")); - ZEN_ON_SCOPE_EXIT(::DSA_free(dsa)); - - if (::DSA_set0_pqg(dsa, p.release(), q.release(), g.release()) != 1) //pass BIGNUM ownership - throw SysError(formatLastOpenSSLError("DSA_set0_pqg")); - - if (::DSA_set0_key(dsa, pub.release(), pri.release()) != 1) - throw SysError(formatLastOpenSSLError("DSA_set0_key")); - - EVP_PKEY* evp = ::EVP_PKEY_new(); - if (!evp) - throw SysError(formatLastOpenSSLError("EVP_PKEY_new")); - ZEN_ON_SCOPE_EXIT(::EVP_PKEY_free(evp)); - if (::EVP_PKEY_set1_DSA(evp, dsa) != 1) //no ownership transfer (internally ref-counted) - throw SysError(formatLastOpenSSLError("EVP_PKEY_set1_DSA")); -#endif return keyToStream(evp, RsaStreamType::pkix, false /*publicKey*/); //throw SysError } //---------------------------------------------------------- @@ -794,7 +792,6 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: const std::string pointStream = extractStringPub(); std::unique_ptr<BIGNUM, BnFree> pri = extractBigNumPriv(); //throw SysError //---------------------------------------------------------- -#if OPENSSL_VERSION_NUMBER >= 0x30000000L const char* groupName = [&] { if (algoShort == "nistp256") @@ -838,53 +835,7 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: if (::EVP_PKEY_fromdata(evpCtx, &evp, EVP_PKEY_KEYPAIR, sslParams) != 1) throw SysError(formatLastOpenSSLError("EVP_PKEY_fromdata")); ZEN_ON_SCOPE_EXIT(::EVP_PKEY_free(evp)); -#else - const int curveNid = [&] - { - if (algoShort == "nistp256") - return NID_X9_62_prime256v1; //same as SECG secp256r1 - if (algoShort == "nistp384") - return NID_secp384r1; - if (algoShort == "nistp521") - return NID_secp521r1; - throw SysError(L"Unknown elliptic curve: " + utfTo<std::wstring>(algorithm)); - }(); - - EC_KEY* ecKey = ::EC_KEY_new_by_curve_name(curveNid); - if (!ecKey) - throw SysError(formatLastOpenSSLError("EC_KEY_new_by_curve_name")); - ZEN_ON_SCOPE_EXIT(::EC_KEY_free(ecKey)); - - const EC_GROUP* ecGroup = ::EC_KEY_get0_group(ecKey); - if (!ecGroup) - throw SysError(formatLastOpenSSLError("EC_KEY_get0_group")); - - EC_POINT* ecPoint = ::EC_POINT_new(ecGroup); - if (!ecPoint) - throw SysError(formatLastOpenSSLError("EC_POINT_new")); - ZEN_ON_SCOPE_EXIT(::EC_POINT_free(ecPoint)); - - if (::EC_POINT_oct2point(ecGroup, //const EC_GROUP* group - ecPoint, //EC_POINT* p - reinterpret_cast<const unsigned char*>(pointStream.c_str()), //const unsigned char* buf - pointStream.size(), //size_t len - nullptr) != 1) //BN_CTX* ctx - throw SysError(formatLastOpenSSLError("EC_POINT_oct2point")); - - if (::EC_KEY_set_public_key(ecKey, ecPoint) != 1) //no ownership transfer (internally ref-counted) - throw SysError(formatLastOpenSSLError("EC_KEY_set_public_key")); - - if (::EC_KEY_set_private_key(ecKey, pri.get()) != 1) //no ownership transfer (internally ref-counted) - throw SysError(formatLastOpenSSLError("EC_KEY_set_private_key")); - - EVP_PKEY* evp = ::EVP_PKEY_new(); - if (!evp) - throw SysError(formatLastOpenSSLError("EVP_PKEY_new")); - ZEN_ON_SCOPE_EXIT(::EVP_PKEY_free(evp)); - if (::EVP_PKEY_set1_EC_KEY(evp, ecKey) != 1) //no ownership transfer (internally ref-counted) - throw SysError(formatLastOpenSSLError("EVP_PKEY_set1_EC_KEY")); -#endif return keyToStream(evp, RsaStreamType::pkix, false /*publicKey*/); //throw SysError } //---------------------------------------------------------- @@ -904,5 +855,17 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: return keyToStream(evpPriv, RsaStreamType::pkix, false /*publicKey*/); //throw SysError } else - throw SysError(L"Unknown key algorithm: " + utfTo<std::wstring>(algorithm)); + throw SysError(L"Unsupported key algorithm: " + utfTo<std::wstring>(algorithm)); + /* PuTTYgen supports many more (which are not yet supported by libssh2): + - rsa-sha2-256 + - rsa-sha2-512 + - ssh-ed448 + - ssh-dss-cert-v01@openssh.com + - ssh-rsa-cert-v01@openssh.com + - rsa-sha2-256-cert-v01@openssh.com + - rsa-sha2-512-cert-v01@openssh.com + - ssh-ed25519-cert-v01@openssh.com + - ecdsa-sha2-nistp256-cert-v01@openssh.com + - ecdsa-sha2-nistp384-cert-v01@openssh.com + - ecdsa-sha2-nistp521-cert-v01@openssh.com */ } |