summaryrefslogtreecommitdiff
path: root/zen/open_ssl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'zen/open_ssl.cpp')
-rw-r--r--zen/open_ssl.cpp461
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 */
}
bgstack15