├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md └── src ├── base-helpers.cpp ├── base-helpers.h ├── dh-tester.cpp ├── dh-tester.h ├── diffie-hellman-boost.cpp ├── diffie-hellman-boost.h ├── diffie-hellman-libressl-bn.cpp ├── diffie-hellman-libressl-bn.h ├── diffie-hellman-libressl-dh.cpp ├── diffie-hellman-libressl-dh.h ├── diffie-hellman-openssl.cpp ├── diffie-hellman-openssl.h ├── diffie-hellman.cpp ├── diffie-hellman.h ├── ec-diffie-hellman-libressl.cpp ├── ec-diffie-hellman-libressl.h ├── ec-diffie-hellman-openssl.cpp ├── ec-diffie-hellman-openssl.h ├── ec-diffie-hellman.cpp ├── ec-diffie-hellman.h ├── ecdh-tester.cpp ├── ecdh-tester.h ├── libressl-helpers.cpp ├── libressl-helpers.h ├── main.cpp ├── openssl-helpers.cpp ├── openssl-helpers.h ├── primes.h └── result.h /.gitignore: -------------------------------------------------------------------------------- 1 | build/* -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | project(diffie-hellman-cpp) 4 | 5 | message(STATUS "Use -DUSE_OPENSSL=ON to compile with OpenSSL 3 support instead of LibreSSL.") 6 | message(STATUS "Use -DINCLUDE_DIRS=\"path1;path2;path3\" to specify additional include directories.") 7 | message(STATUS "Use -DLIBRARY_DIRS=\"path1;path2;path3\" to specify additional library directories.") 8 | 9 | foreach(INCLUDE_DIR IN LISTS INCLUDE_DIRS) 10 | include_directories(${INCLUDE_DIR}) 11 | endforeach() 12 | 13 | foreach(LIBRARY_DIR IN LISTS LIBRARY_DIRS) 14 | link_directories(${LIBRARY_DIR}) 15 | endforeach() 16 | 17 | if (WIN32) 18 | if((NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") OR MSVC_VERSION LESS 1929) 19 | message( 20 | "-------------------------------------\n" 21 | "VS 2019 v16.10 & v16.11 or later is required because the project uses std::format. " 22 | "See: https://devblogs.microsoft.com/cppblog/msvc-cpp20-and-the-std-cpp20-switch.\n" 23 | "-------------------------------------") 24 | message(FATAL_ERROR "See above for details.") 25 | endif() 26 | 27 | add_compile_options("/std:c++latest") 28 | 29 | option(USE_STATIC_MSVC_RUNTIMES "Use /MT instead of /MD in MSVC" OFF) 30 | if(USE_STATIC_MSVC_RUNTIMES) 31 | add_compile_options( 32 | $<$:/MT> 33 | $<$:/MTd> 34 | $<$:/MT> 35 | ) 36 | endif() 37 | else() 38 | if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR CLANG_VERSION_MAJOR LESS 14) 39 | message( 40 | "-------------------------------------\n" 41 | "Clang 14 or later is required because the project uses std::format. " 42 | "https://en.cppreference.com/w/cpp/compiler_support\n" 43 | "-------------------------------------") 44 | message(FATAL_ERROR "See above for details.") 45 | endif() 46 | set(CMAKE_CXX_STANDARD 20) 47 | set(CMAKE_CXX_STANDARD_REQUIRED True) 48 | if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") 49 | add_compile_options("-stdlib=libc++") 50 | add_link_options("-stdlib=libc++") 51 | endif() 52 | endif() 53 | 54 | set(SOURCES 55 | ${SOURCES} 56 | src/base-helpers.cpp 57 | src/diffie-hellman.cpp 58 | src/diffie-hellman-boost.cpp 59 | src/ec-diffie-hellman.cpp 60 | src/dh-tester.cpp 61 | src/ecdh-tester.cpp 62 | src/main.cpp 63 | ) 64 | 65 | set(HEADERS 66 | ${HEADERS} 67 | src/result.h 68 | src/base-helpers.h 69 | src/primes.h 70 | src/diffie-hellman.h 71 | src/diffie-hellman-boost.h 72 | src/ec-diffie-hellman.h 73 | src/dh-tester.h 74 | src/ecdh-tester.h 75 | ) 76 | 77 | option(USE_OPENSSL "Use OpenSSL 3.x.x instead of LibreSSL 3.x.x" OFF) 78 | if(USE_OPENSSL) 79 | set(SOURCES 80 | ${SOURCES} 81 | src/openssl-helpers.cpp 82 | src/diffie-hellman-openssl.cpp 83 | src/ec-diffie-hellman-openssl.cpp 84 | ) 85 | set(HEADERS 86 | ${HEADERS} 87 | src/openssl-helpers.h 88 | src/diffie-hellman-openssl.h 89 | src/ec-diffie-hellman-openssl.h 90 | ) 91 | add_definitions(-DUSE_OPENSSL) 92 | else() 93 | set(SOURCES 94 | ${SOURCES} 95 | src/libressl-helpers.cpp 96 | src/diffie-hellman-libressl-dh.cpp 97 | src/diffie-hellman-libressl-bn.cpp 98 | src/ec-diffie-hellman-libressl.cpp 99 | ) 100 | set(HEADERS 101 | ${HEADERS} 102 | src/libressl-helpers.h 103 | src/diffie-hellman-libressl-dh.h 104 | src/diffie-hellman-libressl-bn.h 105 | src/ec-diffie-hellman-libressl.h 106 | ) 107 | endif() 108 | 109 | 110 | add_executable(${CMAKE_PROJECT_NAME} ${SOURCES} ${HEADERS}) 111 | 112 | if(USE_OPENSSL) 113 | if (MSVC) 114 | target_link_libraries(${CMAKE_PROJECT_NAME} libcrypto) 115 | else() 116 | endif() 117 | else() 118 | if (MSVC) 119 | target_link_libraries(${CMAKE_PROJECT_NAME} crypto-47 Bcrypt Ws2_32) 120 | else() 121 | target_link_libraries(${CMAKE_PROJECT_NAME} crypto pthread) 122 | endif() 123 | endif() 124 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are 5 | // met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above 10 | // copyright notice, this list of conditions and the following disclaimer 11 | // in the documentation and/or other materials provided with the 12 | // distribution. 13 | // * Neither the name of Eugen Hartmann nor the names of the other 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eugen15/diffie-hellman-cpp/436ed068b45e07d09d1e5fb167709f7e96da1312/README.md -------------------------------------------------------------------------------- /src/base-helpers.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include "base-helpers.h" 6 | 7 | static int GetHexValue(unsigned char hexDigit) 8 | { 9 | static constexpr char hexValues[256] = { 10 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 11 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 13 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, 14 | -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, 15 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 16 | -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, 17 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 18 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 19 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 20 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 21 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 22 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 23 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 24 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 25 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26 | }; 27 | return hexValues[hexDigit]; 28 | } 29 | 30 | namespace Base { 31 | 32 | std::string ConvertDataToHex(std::string_view data) { 33 | static const char hexDigits[] = "0123456789ABCDEF"; 34 | 35 | std::string hex; 36 | hex.reserve(data.size() * 2); 37 | 38 | for (unsigned char c : data) { 39 | hex.push_back(hexDigits[c >> 4]); 40 | hex.push_back(hexDigits[c & 15]); 41 | } 42 | 43 | return hex; 44 | } 45 | 46 | Result ConvertHexToData(std::string_view hex, std::string* data) { 47 | // Must be an even number! 48 | if (hex.size() & 1) { 49 | return Result{Result::Fail, "The hex string size must be an even number."}; 50 | } 51 | 52 | data->clear(); 53 | data->reserve(hex.size() / 2); 54 | 55 | auto it = hex.begin(); 56 | while (it != hex.end()) { 57 | int hi = GetHexValue(*it++); 58 | int lo = GetHexValue(*it++); 59 | if (hi == -1 || lo == -1) { 60 | return Result{Result::Fail, "Bad hex digit."}; 61 | } 62 | data->push_back(hi << 4 | lo); 63 | } 64 | 65 | return Result{Result::Success}; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/base-helpers.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "result.h" 12 | 13 | namespace Base { 14 | 15 | /*template 16 | struct DeleterFromFn { 17 | template 18 | constexpr void operator()(T* arg) const { 19 | fn(arg); 20 | } 21 | };*/ 22 | 23 | template 24 | using DeleterFromFn = std::integral_constant; 25 | 26 | // Converts binary data to a hex string. 27 | std::string ConvertDataToHex(std::string_view data); 28 | 29 | // Converts a hex string to binary data. 30 | Result ConvertHexToData(std::string_view hex, std::string* data); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/dh-tester.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "result.h" 11 | #include "base-helpers.h" 12 | #include "diffie-hellman.h" 13 | 14 | #include "dh-tester.h" 15 | 16 | DHTester::DHTester(int primeLengthInBits, int generator) 17 | : primeLengthInBits_(primeLengthInBits) 18 | , generator_(generator) { 19 | } 20 | 21 | void DHTester::Run() { 22 | 23 | std::cout << "--------------------------------------------------------" << std::endl; 24 | std::cout << "PRIME LENGTH (bits): " << primeLengthInBits_ << std::endl; 25 | std::cout << "GENERATOR: " << generator_ << std::endl; 26 | 27 | using Impl = DiffieHellman::Implementation; 28 | 29 | std::vector> tests = { 30 | {Impl::OpenSSL, Impl::OpenSSL}, 31 | {Impl::LibreSSLDH, Impl::LibreSSLDH}, 32 | {Impl::LibreSSLDH, Impl::LibreSSLBN}, 33 | {Impl::LibreSSLBN, Impl::LibreSSLDH}, 34 | {Impl::OpenSSL, Impl::Boost}, 35 | {Impl::Boost, Impl::OpenSSL}, 36 | {Impl::LibreSSLDH, Impl::Boost}, 37 | {Impl::Boost, Impl::LibreSSLDH}, 38 | }; 39 | 40 | for (const auto& test : tests) { 41 | auto alice = DiffieHellman::Create(test.first); 42 | auto bob = DiffieHellman::Create(test.second); 43 | if (alice && bob) { 44 | std::cout << "--------------------------------------------------------" << std::endl; 45 | 46 | std::string testDescription = std::format("ALICE {} <-> BOB {}", 47 | alice->GetImplementationName(), bob->GetImplementationName()); 48 | std::cout << testDescription << std::endl; 49 | 50 | auto result = DoTest(alice.get(), bob.get()); 51 | 52 | std::string resultDescription = (result) ? "success" : result.GetDescription(); 53 | std::cout << "RESULT: " << resultDescription << std::endl << std::endl; 54 | } 55 | } 56 | } 57 | 58 | Result DHTester::DoTest(DiffieHellman* alice, DiffieHellman* bob) { 59 | Result result; 60 | 61 | std::cout << "Generating the prime (may be slow)..." << std::endl; 62 | 63 | std::chrono::high_resolution_clock::time_point primeGenStart = 64 | std::chrono::high_resolution_clock::now(); 65 | 66 | std::string hexPrime, hexGenerator; 67 | if (!(result = alice->GenerateParameters(primeLengthInBits_, generator_)) || 68 | !(result = alice->GetParameters(&hexPrime, &hexGenerator))) { 69 | return result; 70 | } 71 | 72 | std::chrono::high_resolution_clock::time_point primeGenEnd = 73 | std::chrono::high_resolution_clock::now(); 74 | std::int64_t primGenSpentMs = 75 | std::chrono::duration_cast(primeGenEnd - primeGenStart).count(); 76 | 77 | std::cout << "ALICE prime generation spent (ms): " << primGenSpentMs << std::endl; 78 | 79 | PrintObjectLength("ALICE prime length", alice->GetPrimeLength(), hexPrime); 80 | 81 | std::cout << "ALICE prime: " << hexPrime << std::endl; 82 | std::cout << "ALICE generator: " << hexGenerator << std::endl; 83 | 84 | std::string aliceHexPrivateKey, aliceHexPublicKey; 85 | if (!(result = alice->GenerateKeys()) || 86 | !(result = alice->GetPrivateKey(&aliceHexPrivateKey)) || 87 | !(result = alice->GetPublicKey(&aliceHexPublicKey))) { 88 | return result; 89 | } 90 | 91 | PrintObjectLength("ALICE private key length", 92 | alice->GetPrivateKeyLength(), aliceHexPrivateKey); 93 | 94 | std::cout << "ALICE private key: " << aliceHexPrivateKey << std::endl; 95 | 96 | PrintObjectLength("ALICE public key length", 97 | alice->GetPublicKeyLength(), aliceHexPublicKey); 98 | 99 | std::cout << "ALICE public key: " << aliceHexPublicKey << std::endl; 100 | 101 | std::string bobHexPrivateKey, bobHexPublicKey; 102 | if (!(result = bob->SetParameters(hexPrime, hexGenerator)) || 103 | !(result = bob->GenerateKeys()) || 104 | !(result = bob->GetPrivateKey(&bobHexPrivateKey)) || 105 | !(result = bob->GetPublicKey(&bobHexPublicKey))) { 106 | return result; 107 | } 108 | 109 | PrintObjectLength("BOB private key length", 110 | bob->GetPrivateKeyLength(), bobHexPrivateKey); 111 | 112 | std::cout << "BOB private key: " << bobHexPrivateKey << std::endl; 113 | 114 | PrintObjectLength("BOB public key length", 115 | bob->GetPublicKeyLength(), bobHexPrivateKey); 116 | 117 | std::cout << "BOB public key: " << bobHexPublicKey << std::endl; 118 | 119 | std::string aliceHexSharedSecret; 120 | if (!(result = alice->DeriveSharedSecret(bobHexPublicKey, &aliceHexSharedSecret))) { 121 | return result; 122 | } 123 | 124 | std::cout << "ALICE shared secret length (hex digits): " 125 | << aliceHexSharedSecret.size() << std::endl; 126 | std::cout << "ALICE shared secret: " << aliceHexSharedSecret << std::endl; 127 | 128 | std::string bobHexSharedSecret; 129 | if (!(result = bob->DeriveSharedSecret(aliceHexPublicKey, &bobHexSharedSecret))) { 130 | return result; 131 | } 132 | 133 | std::cout << "BOB shared secret length (hex digits): " 134 | << bobHexSharedSecret.size() << std::endl; 135 | std::cout << "BOB shared secret: " << bobHexSharedSecret << std::endl; 136 | 137 | if (aliceHexSharedSecret != bobHexSharedSecret) { 138 | return {Result::Fail, "The shared secrets are different!"}; 139 | } 140 | 141 | return {Result::Success}; 142 | } 143 | 144 | void DHTester::PrintObjectLength(std::string_view comment, 145 | std::tuple bitsBytes, const std::string& hex) { 146 | std::string line = std::format("{} (bits/bytes/hex digits): {}/{}/{}", 147 | comment, std::get<0>(bitsBytes), std::get<1>(bitsBytes), hex.size()); 148 | std::cout << line << std::endl; 149 | } -------------------------------------------------------------------------------- /src/dh-tester.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include "diffie-hellman.h" 10 | 11 | class DHTester final { 12 | public: 13 | // Construct an instance, sets the prime length and generator number. 14 | DHTester(int primeLengthInBits, int generator); 15 | 16 | // Runs predefined tests. 17 | void Run(); 18 | 19 | private: 20 | Result DoTest(DiffieHellman* alice, DiffieHellman* bob); 21 | static void PrintObjectLength(std::string_view comment, 22 | std::tuple bitsBytes, const std::string& hex); 23 | 24 | int primeLengthInBits_ = 512; 25 | int generator_ = 2; 26 | }; -------------------------------------------------------------------------------- /src/diffie-hellman-boost.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "base-helpers.h" 15 | #include "diffie-hellman-boost.h" 16 | 17 | using cpp_int = boost::multiprecision::cpp_int; 18 | 19 | static std::string 20 | ConverCPPIntToHex(const boost::multiprecision::cpp_int& num) { 21 | std::stringstream ss; 22 | ss << std::uppercase << std::hex << num; 23 | std::string hex = ss.str(); 24 | // Add the top 0 if necessary 25 | if (hex.size() % 2) { 26 | hex = "0" + hex; 27 | } 28 | return hex; 29 | } 30 | 31 | static boost::multiprecision::cpp_int 32 | ConveryHexToCPPInt(std::string_view hex) { 33 | return boost::multiprecision::cpp_int{std::string("0x") + std::string(hex)}; 34 | } 35 | 36 | DiffieHellmanBoost::DiffieHellmanBoost() { 37 | } 38 | 39 | DiffieHellmanBoost::~DiffieHellmanBoost() { 40 | } 41 | 42 | std::string_view DiffieHellmanBoost::GetImplementationName() const { 43 | return "boost cpp_int DH"; 44 | } 45 | 46 | Result DiffieHellmanBoost::GenerateParameters(int primeLengthInBits, int generator) { 47 | if (generator <= 1) { 48 | return Result{Result::Fail, "Bad generator value."}; 49 | } 50 | 51 | try { 52 | unsigned int minMillerRabinChecks = GetMillerRabinMinChecks(primeLengthInBits); 53 | 54 | // We must use different generators for the tests and prime generation, 55 | // otherwise we get false positives. 56 | // https://www.boost.org/doc/libs/1_60_0/libs/multiprecision/doc/html/boost_multiprecision/tut/primetest.html 57 | boost::random::mt11213b primeGenerator(clock()); 58 | boost::random::mt19937 testGenerator(clock()); 59 | 60 | std::array mods{}; 61 | 62 | while (true) { 63 | cpp_int prime = ProbableSafePrime(primeLengthInBits, generator, primeGenerator); 64 | if (miller_rabin_test(prime, minMillerRabinChecks, testGenerator)) { 65 | // The value is probably prime, see if (prime - 1) / 2 is also prime. 66 | if (miller_rabin_test((prime - 1) / 2, minMillerRabinChecks, testGenerator)) { 67 | prime_ = std::move(prime); 68 | generator_ = cpp_int{generator}; 69 | break; 70 | } 71 | } 72 | } 73 | } catch (const boost::exception& e) { 74 | return {Result::Fail, boost::diagnostic_information(e)}; 75 | } 76 | 77 | return {Result::Success}; 78 | } 79 | 80 | Result DiffieHellmanBoost::SetParameters(std::string_view hexPrime, std::string_view hexGenerator) { 81 | try { 82 | prime_ = ConveryHexToCPPInt(hexPrime); 83 | generator_ = ConveryHexToCPPInt(hexGenerator); 84 | 85 | } catch (const boost::exception& e) { 86 | return {Result::Fail, boost::diagnostic_information(e)}; 87 | } 88 | 89 | return Result{Result::Success}; 90 | } 91 | Result DiffieHellmanBoost::GetParameters(std::string* hexPrime, std::string* hexGenerator) const { 92 | try { 93 | *hexPrime = ConverCPPIntToHex(prime_); 94 | *hexGenerator = ConverCPPIntToHex(generator_); 95 | 96 | } catch (const boost::exception& e) { 97 | return {Result::Fail, boost::diagnostic_information(e)}; 98 | } 99 | 100 | return Result{Result::Success}; 101 | } 102 | 103 | Result DiffieHellmanBoost::GenerateKeys() { 104 | try { 105 | // It will return prime len - 1 anyway because we always set the top bit. 106 | // For example, 255 for a 256 bit prime. 107 | unsigned int len = boost::multiprecision::msb(prime_); 108 | 109 | // Configure the generator. 110 | cpp_int max = cpp_int{1} << len; 111 | boost::random::uniform_int_distribution generatePrivateKey{0, max}; 112 | boost::random::mt11213b privateKeyGenerator(clock()); 113 | 114 | // Generate the private key. 115 | cpp_int privateKey = generatePrivateKey(privateKeyGenerator); 116 | 117 | // Derive the public key. 118 | cpp_int publicKey = boost::multiprecision::powm(generator_, privateKey, prime_); 119 | 120 | privateKey_ = std::move(privateKey); 121 | publicKey_ = std::move(publicKey); 122 | 123 | } catch (const boost::exception& e) { 124 | return {Result::Fail, boost::diagnostic_information(e)}; 125 | } 126 | 127 | return {Result::Success}; 128 | } 129 | 130 | Result DiffieHellmanBoost::GetPrivateKey(std::string* hexPrivateKey) const { 131 | try { 132 | hexPrivateKey->clear(); 133 | hexPrivateKey->append(ConverCPPIntToHex(privateKey_)); 134 | if (hexPrivateKey->empty()) { 135 | return {Result::Fail, "The private key is empty."}; 136 | } 137 | } catch (const boost::exception& e) { 138 | return {Result::Fail, boost::diagnostic_information(e)}; 139 | } 140 | 141 | return Result{Result::Success}; 142 | } 143 | 144 | Result DiffieHellmanBoost::GetPublicKey(std::string* hexPublicKey) const { 145 | try { 146 | hexPublicKey->clear(); 147 | hexPublicKey->append(ConverCPPIntToHex(publicKey_)); 148 | if (hexPublicKey->empty()) { 149 | return {Result::Fail, "The public key is empty."}; 150 | } 151 | } catch (const boost::exception& e) { 152 | return {Result::Fail, boost::diagnostic_information(e)}; 153 | } 154 | 155 | return Result{Result::Success}; 156 | } 157 | 158 | static Result CheckPublicKey(const boost::multiprecision::cpp_int& prime, 159 | const boost::multiprecision::cpp_int& publicKey) { 160 | try { 161 | if (publicKey <= 1) { 162 | return {Result::Fail, "The public key is too small."}; 163 | } 164 | if (publicKey >= prime) { 165 | return {Result::Fail, "The public key is too large."}; 166 | } 167 | 168 | } catch (const boost::exception& e) { 169 | return {Result::Fail, boost::diagnostic_information(e)}; 170 | } 171 | 172 | return {Result::Success}; 173 | } 174 | 175 | Result DiffieHellmanBoost::DeriveSharedSecret(std::string_view hexPeerPublicKey, 176 | std::string* hexSharedSecret) const { 177 | try { 178 | cpp_int peerPublicKey = ConveryHexToCPPInt(hexPeerPublicKey); 179 | 180 | if (Result result = CheckPublicKey(prime_, peerPublicKey); !result) { 181 | return result; 182 | } 183 | 184 | cpp_int sharedSecret = boost::multiprecision::powm(peerPublicKey, privateKey_, prime_); 185 | *hexSharedSecret = ConverCPPIntToHex(sharedSecret); 186 | 187 | } catch (const boost::exception& e) { 188 | return {Result::Fail, boost::diagnostic_information(e)}; 189 | } 190 | return {Result::Success}; 191 | } 192 | 193 | std::tuple DiffieHellmanBoost::GetPrimeLength() const { 194 | try { 195 | // bytes = (bits - 1) / 8 + 1 196 | return {boost::multiprecision::msb(prime_) + 1, 197 | boost::multiprecision::msb(prime_) / 8 + 1}; 198 | } catch (...) { 199 | } 200 | 201 | return {-1, -1}; 202 | } 203 | 204 | std::tuple DiffieHellmanBoost::GetPrivateKeyLength() const { 205 | try { 206 | return {boost::multiprecision::msb(privateKey_) + 1, 207 | boost::multiprecision::msb(privateKey_) / 8 + 1}; 208 | } catch (...) { 209 | } 210 | 211 | return {-1, -1}; 212 | } 213 | 214 | std::tuple DiffieHellmanBoost::GetPublicKeyLength() const { 215 | try { 216 | return {boost::multiprecision::msb(publicKey_) + 1, 217 | boost::multiprecision::msb(publicKey_) / 8 + 1}; 218 | } catch (...) { 219 | } 220 | 221 | return {-1, -1}; 222 | } 223 | 224 | unsigned int DiffieHellmanBoost::GetMillerRabinMinChecks(int primeLengthInBits) { 225 | if (primeLengthInBits > 2048) { 226 | return 128; 227 | } 228 | return 64; 229 | } 230 | 231 | int DiffieHellmanBoost::GetTrivialDivisionNum(int primeLengthInBits) { 232 | if (primeLengthInBits <= 512) { 233 | return 64; 234 | } else if (primeLengthInBits <= 1024) { 235 | return 128; 236 | } else if (primeLengthInBits <= 2048) { 237 | return 384; 238 | } else if (primeLengthInBits <= 4096) { 239 | return 1024; 240 | } 241 | return Primes.size(); 242 | } 243 | 244 | std::tuple DiffieHellmanBoost::GetAddRem(int generator) { 245 | // See dh_builtin_genparams 246 | // libressl: crypto\dh\dh_gen.c 247 | boost::multiprecision::cpp_int add, rem; 248 | switch (generator) { 249 | case 2: 250 | add = 24; 251 | rem = 11; 252 | break; 253 | case 5: 254 | add = 10; 255 | rem = 3; 256 | break; 257 | default: 258 | add = 2; 259 | rem = 1; 260 | } 261 | return {add, rem}; 262 | } 263 | 264 | cpp_int DiffieHellmanBoost::ProbableSafePrime(int primeLengthInBits, 265 | int generator, boost::random::mt11213b& primeGenerator) { 266 | int trivialDivisionNum = GetTrivialDivisionNum(primeLengthInBits); 267 | std::uint64_t maxDelta = 0xFFFFFFFFFFFFFFFFULL - Primes[trivialDivisionNum - 1]; 268 | 269 | // Prepare cpp_int add/rem for the generator. 270 | auto [add, rem] = GetAddRem(generator); 271 | 272 | // Prepare the top bit. 273 | cpp_int topBit = cpp_int{0x80} << (primeLengthInBits - 8); 274 | 275 | // Prepare the bottom bit. 276 | cpp_int bottomBit{1}; 277 | 278 | // Generation limits. 279 | cpp_int max = cpp_int{1} << primeLengthInBits; 280 | boost::random::uniform_int_distribution generatePrime{0, max}; 281 | 282 | cpp_int rnd; 283 | while (true) { 284 | rnd = generatePrime(primeGenerator); 285 | 286 | // Add top/bottom. 287 | rnd = rnd | topBit | bottomBit; 288 | 289 | // The prime should fulfill the condition prime % add == rem 290 | // in order to suit a given generator. 291 | cpp_int mod = rnd % add; 292 | rnd = rnd - mod + rem; 293 | 294 | // Probably the second condition is overhead? 295 | if (boost::multiprecision::msb(rnd) + 1 < primeLengthInBits || rnd < 5) { 296 | rnd += add; 297 | } 298 | 299 | auto [success, delta] = IncreaseProbabilityOfBeingPrime(primeLengthInBits, rnd, add); 300 | if (success) { 301 | rnd += delta; 302 | break; 303 | } 304 | } 305 | 306 | return rnd; 307 | } 308 | 309 | std::tuple 310 | DiffieHellmanBoost::IncreaseProbabilityOfBeingPrime(int primeLengthInBits, 311 | cpp_int rnd, cpp_int add) { 312 | int trivialDivisionNum = GetTrivialDivisionNum(primeLengthInBits); 313 | 314 | std::uint64_t maxDelta = 0xFFFFFFFFFFFFFFFFULL - Primes[trivialDivisionNum - 1]; 315 | std::uint64_t rnd64bit = rnd.convert_to(); 316 | std::uint64_t add64bit = add.convert_to(); 317 | std::uint64_t delta = 0; 318 | 319 | auto mods = std::make_unique(Primes.size()); 320 | 321 | for (int i = 1; i < trivialDivisionNum; ++i) { 322 | cpp_int mod = rnd % Primes[i]; 323 | mods[i] = mod.convert_to(); 324 | } 325 | 326 | bool continueTesting = true; 327 | while (continueTesting) { 328 | continueTesting = false; 329 | for (int i = 1; i < trivialDivisionNum; ++i) { 330 | // Check if it is a prime. 331 | if (primeLengthInBits <= 31 && delta <= 0x7FFFFFFF) { 332 | std::uint64_t sq = static_cast(Primes[i]) * 333 | static_cast(Primes[i]); 334 | if (sq > rnd64bit + delta) { 335 | return {true, delta}; 336 | } 337 | } 338 | // prime mod p == 1 implies q = (prime - 1) / 2 is divisible by p. 339 | if ((mods[i] + delta) % Primes[i] <= 1) { 340 | delta += add64bit; 341 | if (delta > maxDelta) { 342 | return {false, 0}; 343 | } 344 | continueTesting = true; 345 | break; 346 | } 347 | } 348 | } 349 | 350 | return {true, delta}; 351 | } -------------------------------------------------------------------------------- /src/diffie-hellman-boost.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "result.h" 16 | #include "primes.h" 17 | #include "diffie-hellman.h" 18 | 19 | // Diffie-Hellman implementation based on the C++ boost library. 20 | class DiffieHellmanBoost : public DiffieHellman { 21 | public: 22 | DiffieHellmanBoost(); 23 | ~DiffieHellmanBoost() override; 24 | 25 | std::string_view GetImplementationName() const override; 26 | 27 | Result GenerateParameters(int primeLengthInBits, int generator) override; 28 | 29 | Result SetParameters(std::string_view hexPrime, std::string_view hexGenerator) override; 30 | Result GetParameters(std::string* hexPrime, std::string* hexGenerator) const override; 31 | 32 | Result GenerateKeys() override; 33 | Result GetPrivateKey(std::string* hexPrivateKey) const override; 34 | Result GetPublicKey(std::string* hexPublicKey) const override; 35 | 36 | Result DeriveSharedSecret(std::string_view hexPeerPublicKey, 37 | std::string* hexSharedSecret) const override; 38 | 39 | std::tuple GetPrimeLength() const override; 40 | std::tuple GetPrivateKeyLength() const override; 41 | std::tuple GetPublicKeyLength() const override; 42 | 43 | private: 44 | using cpp_int = boost::multiprecision::cpp_int; 45 | 46 | // The minimum of 64 rounds of Miller - Rabin, which should give a false 47 | // positive rate of 2 ^ -128. If the size of the prime is larger than 2048 48 | // switch to 128 rounds giving a false positive rate of 2 ^ -256. 49 | static unsigned int GetMillerRabinMinChecks(int primeLengthInBits); 50 | 51 | // Gets the number of trial divisions that gives the best speed in 52 | // combination with Miller-Rabin prime test, based on the sized of the prime. 53 | static int GetTrivialDivisionNum(int primeLengthInBits); 54 | 55 | // Returns add/rem for the condition: prime % add == rem 56 | // for the specified generator. 57 | static std::tuple GetAddRem(int generator); 58 | 59 | static cpp_int ProbableSafePrime(int primeLengthInBits, 60 | int generator, boost::random::mt11213b& primeGenerator); 61 | 62 | static std::tuple 63 | IncreaseProbabilityOfBeingPrime(int primeLengthInBits, 64 | cpp_int rnd, cpp_int add); 65 | 66 | cpp_int prime_; 67 | cpp_int generator_; 68 | cpp_int privateKey_; 69 | cpp_int publicKey_; 70 | }; 71 | -------------------------------------------------------------------------------- /src/diffie-hellman-libressl-bn.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include "base-helpers.h" 12 | #include "diffie-hellman-libressl-bn.h" 13 | 14 | DiffieHellmanLibreSSLBN::DiffieHellmanLibreSSLBN() { 15 | } 16 | 17 | DiffieHellmanLibreSSLBN::~DiffieHellmanLibreSSLBN() { 18 | } 19 | 20 | std::string_view DiffieHellmanLibreSSLBN::GetImplementationName() const { 21 | return "LibreSSL BIGNUM DH"; 22 | } 23 | 24 | Result DiffieHellmanLibreSSLBN::GenerateParameters(int primeLengthInBits, int generator) { 25 | ERR_clear_error(); 26 | 27 | if (generator <= 1) { 28 | return {Result::Fail, "Bad generator value."}; 29 | } 30 | 31 | std::unique_ptr> ctx{BN_CTX_new()}; 32 | if (!ctx) { 33 | return {Result::Fail, "Could not create the big number context: {}", 34 | LibreSSL::GetLastErrorString()}; 35 | } 36 | 37 | BN_CTX_start(ctx.get()); 38 | 39 | BIGNUM* bigAdd{BN_CTX_get(ctx.get())}; 40 | BIGNUM* bigRem{BN_CTX_get(ctx.get())}; 41 | if (!bigAdd || !bigRem) { 42 | return {Result::Fail, "BN_CTX_get failed: {}", 43 | LibreSSL::GetLastErrorString()}; 44 | } 45 | 46 | if (generator == DH_GENERATOR_2) { 47 | if (!BN_set_word(bigAdd, 24) || !BN_set_word(bigRem, 11)) { 48 | return {Result::Fail, "BN_set_word failed: {}", 49 | LibreSSL::GetLastErrorString()}; 50 | } 51 | } else if (generator == DH_GENERATOR_5) { 52 | if (!BN_set_word(bigAdd, 10) || !BN_set_word(bigRem, 3)) { 53 | return {Result::Fail, "BN_set_word failed: {}", 54 | LibreSSL::GetLastErrorString()}; 55 | } 56 | } else { 57 | if (!BN_set_word(bigAdd, 2) || !BN_set_word(bigRem, 1)) { 58 | return {Result::Fail, "BN_set_word failed: {}", 59 | LibreSSL::GetLastErrorString()}; 60 | } 61 | } 62 | 63 | prime_.reset(BN_new()); 64 | if (!prime_) { 65 | return {Result::Fail, "Could not create the prime: {}", 66 | LibreSSL::GetLastErrorString()}; 67 | } 68 | 69 | generator_.reset(BN_new()); 70 | if (!generator_) { 71 | return {Result::Fail, "Could not create the generator: {}", 72 | LibreSSL::GetLastErrorString()}; 73 | } 74 | 75 | if (!BN_generate_prime_ex(prime_.get(), primeLengthInBits, 1, bigAdd, bigRem, nullptr)) { 76 | return {Result::Fail, "BN_generate_prime_ex failed: {}", 77 | LibreSSL::GetLastErrorString()}; 78 | } 79 | 80 | if (!BN_set_word(generator_.get(), static_cast(generator))) { 81 | return {Result::Fail, "Could not set the generator: {}", 82 | LibreSSL::GetLastErrorString()}; 83 | } 84 | 85 | BN_CTX_end(ctx.get()); 86 | 87 | return {Result::Success}; 88 | } 89 | 90 | Result DiffieHellmanLibreSSLBN::SetParameters(std::string_view hexPrime, 91 | std::string_view hexGenerator) { 92 | ERR_clear_error(); 93 | 94 | prime_.reset(LibreSSL::ConvertHexToBigNum(hexPrime)); 95 | if (!prime_) { 96 | return {Result::Fail, "Could not convert the hex data to the prime: {}", 97 | LibreSSL::GetLastErrorString()}; 98 | } 99 | 100 | generator_.reset(LibreSSL::ConvertHexToBigNum(hexGenerator)); 101 | if (!generator_) { 102 | return {Result::Fail, "Could not convert the hex data to the generator: {}", 103 | LibreSSL::GetLastErrorString()}; 104 | } 105 | 106 | return {Result::Success}; 107 | } 108 | Result DiffieHellmanLibreSSLBN::GetParameters(std::string* hexPrime, 109 | std::string* hexGenerator) const { 110 | ERR_clear_error(); 111 | 112 | hexPrime->clear(); 113 | hexPrime->append(LibreSSL::ConvertBigNumToHex(prime_.get())); 114 | if (hexPrime->empty()) { 115 | return {Result::Fail, "Could not get prime: {}", 116 | LibreSSL::GetLastErrorString()}; 117 | } 118 | 119 | hexGenerator->clear(); 120 | hexGenerator->append(LibreSSL::ConvertBigNumToHex(generator_.get())); 121 | if (hexGenerator->empty()) { 122 | return {Result::Fail, "Could not get generator: {}", 123 | LibreSSL::GetLastErrorString()}; 124 | } 125 | 126 | return {Result::Success}; 127 | } 128 | 129 | Result DiffieHellmanLibreSSLBN::GenerateKeys() { 130 | ERR_clear_error(); 131 | 132 | if (BN_num_bits(prime_.get()) > OPENSSL_DH_MAX_MODULUS_BITS) { 133 | return {Result::Fail, "Modulus too large."}; 134 | } 135 | 136 | std::unique_ptr> ctx{BN_CTX_new()}; 137 | if (!ctx) { 138 | return {Result::Fail, "Could not create the big number context: {}", 139 | LibreSSL::GetLastErrorString()}; 140 | } 141 | 142 | std::unique_ptr> privateKey{BN_new()}; 143 | if (!privateKey) { 144 | return {Result::Fail, "Could not create the private key: {}", 145 | LibreSSL::GetLastErrorString()}; 146 | } 147 | 148 | std::unique_ptr> publicKey{BN_new()}; 149 | if (!publicKey) { 150 | return {Result::Fail, "Could not create the public key: {}", 151 | LibreSSL::GetLastErrorString()}; 152 | } 153 | 154 | unsigned int len = BN_num_bits(prime_.get()) - 1; 155 | if (!BN_rand(privateKey.get(), len, 0, 0)) { 156 | return {Result::Fail, "Could not generate the private key: {}", 157 | LibreSSL::GetLastErrorString()}; 158 | } 159 | 160 | if (!BN_mod_exp(publicKey.get(), generator_.get(), privateKey.get(), prime_.get(), ctx.get())) { 161 | return {Result::Fail, "Could not derive the public key from the private key: {}", 162 | LibreSSL::GetLastErrorString()}; 163 | } 164 | 165 | publicKey_ = std::move(publicKey); 166 | privateKey_ = std::move(privateKey); 167 | 168 | return {Result::Success}; 169 | } 170 | 171 | Result DiffieHellmanLibreSSLBN::GetPrivateKey(std::string* hexPrivateKey) const { 172 | ERR_clear_error(); 173 | 174 | hexPrivateKey->clear(); 175 | hexPrivateKey->append(LibreSSL::ConvertBigNumToHex(privateKey_.get())); 176 | if (hexPrivateKey->empty()) { 177 | return {Result::Fail, "The private key is empty."}; 178 | } 179 | 180 | return {Result::Success}; 181 | } 182 | 183 | Result DiffieHellmanLibreSSLBN::GetPublicKey(std::string* hexPublicKey) const { 184 | ERR_clear_error(); 185 | 186 | hexPublicKey->clear(); 187 | hexPublicKey->append(LibreSSL::ConvertBigNumToHex(publicKey_.get())); 188 | if (hexPublicKey->empty()) { 189 | return {Result::Fail, "The public key is empty."}; 190 | } 191 | 192 | return {Result::Success}; 193 | } 194 | 195 | static Result CheckPublicKey(BIGNUM* p, BIGNUM* publicKey) { 196 | std::unique_ptr> q(BN_new()); 197 | if (!q) { 198 | return {Result::Fail, "BN_new faled: {}", LibreSSL::GetLastErrorString()}; 199 | } 200 | 201 | BN_set_word(q.get(), 1); 202 | if (BN_cmp(publicKey, q.get()) <= 0) { 203 | return {Result::Fail, "The public key is too small."}; 204 | } 205 | 206 | BN_copy(q.get(), p); 207 | if (BN_cmp(publicKey, q.get()) >= 0) { 208 | return {Result::Fail, "The public key is too large."}; 209 | } 210 | 211 | return (Result::Success); 212 | } 213 | 214 | Result DiffieHellmanLibreSSLBN::DeriveSharedSecret( 215 | std::string_view hexPeerPublicKey, std::string* hexSharedSecret) const { 216 | ERR_clear_error(); 217 | 218 | if (BN_num_bits(prime_.get()) > OPENSSL_DH_MAX_MODULUS_BITS) { 219 | return {Result::Fail, "Modulus too large."}; 220 | } 221 | 222 | std::unique_ptr> peerPublicKey(LibreSSL::ConvertHexToBigNum(hexPeerPublicKey)); 223 | if (!peerPublicKey) { 224 | return {Result::Fail, "Could not convert the hex data to the public key: {}", 225 | LibreSSL::GetLastErrorString()}; 226 | } 227 | 228 | if (Result result = CheckPublicKey(prime_.get(), peerPublicKey.get()); !result) { 229 | return result; 230 | } 231 | 232 | std::unique_ptr> ctx{BN_CTX_new()}; 233 | if (!ctx) { 234 | return {Result::Fail, "Could not create the big number context: {}", 235 | LibreSSL::GetLastErrorString()}; 236 | } 237 | 238 | BN_CTX_start(ctx.get()); 239 | 240 | BIGNUM* tmp{BN_CTX_get(ctx.get())}; 241 | if (!tmp) { 242 | return {Result::Fail, "Could not create the temporary big number: {}", 243 | LibreSSL::GetLastErrorString()}; 244 | } 245 | 246 | if (!BN_mod_exp(tmp, peerPublicKey.get(), privateKey_.get(), prime_.get(), ctx.get())) { 247 | return {Result::Fail, "Could not derive the shared secret: {}", 248 | LibreSSL::GetLastErrorString()}; 249 | } 250 | 251 | int sharedSecretSize = BN_num_bytes(prime_.get()); 252 | if (sharedSecretSize <= 0) { 253 | return {Result::Fail, "Could not get shared secret size"}; 254 | } 255 | 256 | std::string sharedSecret; 257 | sharedSecret.resize(sharedSecretSize); 258 | 259 | if (!BN_bn2bin(tmp, static_cast(static_cast(&sharedSecret.front())))) { 260 | return {Result::Fail, "Could not extract the shared secret: {}", 261 | LibreSSL::GetLastErrorString()}; 262 | } 263 | 264 | BN_CTX_end(ctx.get()); 265 | 266 | *hexSharedSecret = Base::ConvertDataToHex(sharedSecret); 267 | 268 | return {Result::Success}; 269 | } 270 | 271 | std::tuple DiffieHellmanLibreSSLBN::GetPrimeLength() const { 272 | if (!prime_) { 273 | return {-1, -1}; 274 | } 275 | return {BN_num_bits(prime_.get()), BN_num_bytes(prime_.get())}; 276 | } 277 | 278 | std::tuple DiffieHellmanLibreSSLBN::GetPrivateKeyLength() const { 279 | if (!privateKey_) { 280 | return {-1, -1}; 281 | } 282 | return {BN_num_bits(privateKey_.get()), BN_num_bytes(privateKey_.get())}; 283 | } 284 | 285 | std::tuple DiffieHellmanLibreSSLBN::GetPublicKeyLength() const { 286 | if (!publicKey_) { 287 | return {-1, -1}; 288 | } 289 | return {BN_num_bits(publicKey_.get()), BN_num_bytes(publicKey_.get())}; 290 | } 291 | -------------------------------------------------------------------------------- /src/diffie-hellman-libressl-bn.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include "result.h" 14 | #include "libressl-helpers.h" 15 | #include "diffie-hellman.h" 16 | 17 | // Diffie-Hellman implementation based on libressl BIGNUM. 18 | // The intermediate libressl DH layer is not used. 19 | class DiffieHellmanLibreSSLBN : public DiffieHellman { 20 | public: 21 | 22 | DiffieHellmanLibreSSLBN(); 23 | virtual ~DiffieHellmanLibreSSLBN(); 24 | 25 | std::string_view GetImplementationName() const override; 26 | 27 | Result GenerateParameters(int primeLengthInBits, int generator) override; 28 | 29 | Result SetParameters(std::string_view hexPrime, std::string_view hexGenerator) override; 30 | Result GetParameters(std::string* hexPrime, std::string* hexGenerator) const override; 31 | 32 | Result GenerateKeys() override; 33 | Result GetPrivateKey(std::string* hexPrivateKey) const override; 34 | Result GetPublicKey(std::string* hexPublicKey) const override; 35 | 36 | Result DeriveSharedSecret(std::string_view hexPeerPublicKey, 37 | std::string* hexSharedSecret) const override; 38 | 39 | std::tuple GetPrimeLength() const override; 40 | std::tuple GetPrivateKeyLength() const override; 41 | std::tuple GetPublicKeyLength() const override; 42 | 43 | private: 44 | std::unique_ptr> prime_; 45 | std::unique_ptr> generator_; 46 | std::unique_ptr> privateKey_; 47 | std::unique_ptr> publicKey_; 48 | 49 | }; 50 | -------------------------------------------------------------------------------- /src/diffie-hellman-libressl-dh.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "libressl-helpers.h" 13 | #include "diffie-hellman-libressl-dh.h" 14 | 15 | DiffieHellmanLibreSSLDH::DiffieHellmanLibreSSLDH() 16 | : dh_(DH_new()) { 17 | } 18 | 19 | DiffieHellmanLibreSSLDH::~DiffieHellmanLibreSSLDH() { 20 | } 21 | 22 | std::string_view DiffieHellmanLibreSSLDH::GetImplementationName() const { 23 | return "LibreSSL DH"; 24 | } 25 | 26 | Result DiffieHellmanLibreSSLDH::GenerateParameters(int primeLengthInBits, int generator) { 27 | ERR_clear_error(); 28 | 29 | if (!DH_generate_parameters_ex(dh_.get(), primeLengthInBits, generator, nullptr)) { 30 | return {Result::Fail, "Could not generate DH parameters: {}", 31 | LibreSSL::GetLastErrorString()}; 32 | } 33 | 34 | int codes = 0; 35 | if (!DH_check(dh_.get(), &codes)) { 36 | return {Result::Fail, "Could not check DH parameters: {}", 37 | LibreSSL::GetLastErrorString()}; 38 | } 39 | 40 | if (codes != 0) { 41 | return {Result::Fail, "Wrong DH parameters; codes: {}", codes}; 42 | } 43 | 44 | return {Result::Success}; 45 | } 46 | 47 | Result DiffieHellmanLibreSSLDH::SetParameters(std::string_view hexPrime, std::string_view hexGenerator) { 48 | ERR_clear_error(); 49 | 50 | std::unique_ptr> prime(LibreSSL::ConvertHexToBigNum(hexPrime)); 51 | if (!prime) { 52 | return {Result::Fail, "Could not convert the hex data to the prime: {}", 53 | LibreSSL::GetLastErrorString()}; 54 | } 55 | 56 | std::unique_ptr> generator(LibreSSL::ConvertHexToBigNum(hexGenerator)); 57 | if (!generator) { 58 | return {Result::Fail, "Could not convert the hex data to the generator: {}", 59 | LibreSSL::GetLastErrorString()}; 60 | } 61 | 62 | if (!DH_set0_pqg(dh_.get(), prime.get(), nullptr, generator.get())) { 63 | return {Result::Fail, "Could not set the prime and generator: {}", 64 | LibreSSL::GetLastErrorString()}; 65 | } 66 | 67 | (void)prime.release(); 68 | (void)generator.release(); 69 | 70 | return {Result::Success}; 71 | } 72 | 73 | Result DiffieHellmanLibreSSLDH::GetParameters(std::string* hexPrime, std::string* hexGenerator) const { 74 | ERR_clear_error(); 75 | 76 | const BIGNUM* prime = nullptr; 77 | const BIGNUM* generator = nullptr; 78 | 79 | DH_get0_pqg(dh_.get(), &prime, nullptr, &generator); 80 | if (!prime) { 81 | return {Result::Fail, "The prime is nullptr"}; 82 | } 83 | if (!generator) { 84 | return {Result::Fail, "The generator is nullptr"}; 85 | } 86 | 87 | hexPrime->clear(); 88 | hexPrime->append(LibreSSL::ConvertBigNumToHex(prime)); 89 | if (hexPrime->empty()) { 90 | return {Result::Fail, "Could not get prime: {}", 91 | LibreSSL::GetLastErrorString()}; 92 | } 93 | 94 | hexGenerator->clear(); 95 | hexGenerator->append(LibreSSL::ConvertBigNumToHex(generator)); 96 | if (hexGenerator->empty()) { 97 | return {Result::Fail, "Could not get generator: {}", 98 | LibreSSL::GetLastErrorString()}; 99 | } 100 | 101 | return {Result::Success}; 102 | } 103 | 104 | Result DiffieHellmanLibreSSLDH::GenerateKeys() { 105 | ERR_clear_error(); 106 | 107 | if (!DH_generate_key(dh_.get())) { 108 | return {Result::Fail, "Could not generate the DH key pair: {}", 109 | LibreSSL::GetLastErrorString()}; 110 | } 111 | 112 | return {Result::Success}; 113 | } 114 | 115 | Result DiffieHellmanLibreSSLDH::GetPrivateKey(std::string* hexPrivateKey) const { 116 | ERR_clear_error(); 117 | 118 | hexPrivateKey->clear(); 119 | hexPrivateKey->append(LibreSSL::ConvertBigNumToHex(dh_->priv_key)); 120 | if (hexPrivateKey->empty()) { 121 | return {Result::Fail, "The private key is empty."}; 122 | } 123 | 124 | return {Result::Success}; 125 | } 126 | 127 | Result DiffieHellmanLibreSSLDH::GetPublicKey(std::string* hexPublicKey) const { 128 | ERR_clear_error(); 129 | 130 | const BIGNUM* publicKey = nullptr; 131 | DH_get0_key(dh_.get(), &publicKey, nullptr); 132 | if (!publicKey) { 133 | return {Result::Fail, "The public key is null"}; 134 | } 135 | 136 | hexPublicKey->clear(); 137 | hexPublicKey->append(LibreSSL::ConvertBigNumToHex(publicKey)); 138 | if (hexPublicKey->empty()) { 139 | return {Result::Fail, "The public key is empty."}; 140 | } 141 | 142 | return {Result::Success}; 143 | } 144 | 145 | Result DiffieHellmanLibreSSLDH::DeriveSharedSecret( 146 | std::string_view hexPeerPublicKey, std::string* hexSharedSecret) const { 147 | ERR_clear_error(); 148 | 149 | std::unique_ptr> peerPublicKey( 150 | LibreSSL::ConvertHexToBigNum(hexPeerPublicKey)); 151 | if (!peerPublicKey) { 152 | return {Result::Fail, "Could not convert the hex data to the public key: {}", 153 | LibreSSL::GetLastErrorString()}; 154 | } 155 | 156 | int sharedSecretSize = DH_size(dh_.get()); 157 | if (sharedSecretSize <= 0) { 158 | return {Result::Fail, "It looks like the DH object is not configured."}; 159 | } 160 | 161 | std::string sharedSecret; 162 | sharedSecret.resize(sharedSecretSize); 163 | 164 | sharedSecretSize = DH_compute_key( 165 | static_cast(static_cast(&sharedSecret.front())), 166 | peerPublicKey.get(), dh_.get()); 167 | if (sharedSecretSize <= 0) { 168 | return {Result::Fail, "Could not derive the shared secret: {}", 169 | LibreSSL::GetLastErrorString()}; 170 | } 171 | 172 | if (sharedSecretSize < sharedSecret.size()) { 173 | sharedSecret.resize(sharedSecretSize); 174 | } 175 | 176 | *hexSharedSecret = Base::ConvertDataToHex(sharedSecret); 177 | 178 | return {Result::Success}; 179 | } 180 | 181 | std::tuple DiffieHellmanLibreSSLDH::GetPrimeLength() const { 182 | return {DH_bits(dh_.get()), DH_size(dh_.get())}; 183 | } 184 | 185 | std::tuple DiffieHellmanLibreSSLDH::GetPrivateKeyLength() const { 186 | if (!dh_->priv_key) { 187 | return {-1, -1}; 188 | } 189 | 190 | return {BN_num_bits(dh_->priv_key), BN_num_bytes(dh_->priv_key)}; 191 | } 192 | 193 | std::tuple DiffieHellmanLibreSSLDH::GetPublicKeyLength() const { 194 | const BIGNUM* publicKey = nullptr; 195 | DH_get0_key(dh_.get(), &publicKey, nullptr); 196 | if (!publicKey) { 197 | return {-1, -1}; 198 | } 199 | 200 | return {BN_num_bits(publicKey), BN_num_bytes(publicKey)}; 201 | } 202 | 203 | -------------------------------------------------------------------------------- /src/diffie-hellman-libressl-dh.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "result.h" 13 | #include "base-helpers.h" 14 | #include "libressl-helpers.h" 15 | #include "diffie-hellman.h" 16 | 17 | // Diffie-Hellman implementation based on libressl DH (just a regular one). 18 | class DiffieHellmanLibreSSLDH : public DiffieHellman { 19 | public: 20 | DiffieHellmanLibreSSLDH(); 21 | ~DiffieHellmanLibreSSLDH() override; 22 | 23 | std::string_view GetImplementationName() const override; 24 | 25 | Result GenerateParameters(int primeLengthInBits, int generator) override; 26 | 27 | Result SetParameters(std::string_view hexPrime, std::string_view hexGenerator) override; 28 | Result GetParameters(std::string* hexPrime, std::string* hexGenerator) const override; 29 | 30 | Result GenerateKeys() override; 31 | Result GetPrivateKey(std::string* hexPrivateKey) const override; 32 | Result GetPublicKey(std::string* hexPublicKey) const override; 33 | 34 | Result DeriveSharedSecret(std::string_view hexPeerPublicKey, 35 | std::string* hexSharedSecret) const override; 36 | 37 | std::tuple GetPrimeLength() const override; 38 | std::tuple GetPrivateKeyLength() const override; 39 | std::tuple GetPublicKeyLength() const override; 40 | 41 | private: 42 | std::unique_ptr> dh_; 43 | 44 | }; 45 | -------------------------------------------------------------------------------- /src/diffie-hellman-openssl.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "base-helpers.h" 14 | #include "openssl-helpers.h" 15 | #include "diffie-hellman-openssl.h" 16 | 17 | DiffieHellmanOpenSSL::DiffieHellmanOpenSSL() { 18 | } 19 | 20 | DiffieHellmanOpenSSL::~DiffieHellmanOpenSSL() { 21 | 22 | } 23 | 24 | std::string_view DiffieHellmanOpenSSL::GetImplementationName() const { 25 | return "OpenSSL DH"; 26 | } 27 | 28 | Result DiffieHellmanOpenSSL::GenerateParameters(int primeLengthInBits, int generator) { 29 | ERR_clear_error(); 30 | 31 | if (generator <= 1) { 32 | return {Result::Fail, "Couldn't generate params: bad generator value."}; 33 | } 34 | 35 | std::unique_ptr> bigAdd{BN_new()}; 36 | std::unique_ptr> bigRem{BN_new()}; 37 | if (!bigAdd || !bigRem) { 38 | return {Result::Fail, "Couldn't generate params: BN_CTX_get failed: {}", 39 | OpenSSL::GetLastErrorString()}; 40 | } 41 | 42 | if (generator == DH_GENERATOR_2) { 43 | if (!BN_set_word(bigAdd.get(), 24) || !BN_set_word(bigRem.get(), 23)) { 44 | return {Result::Fail, "Couldn't generate params: BN_set_word failed: {}", 45 | OpenSSL::GetLastErrorString()}; 46 | } 47 | } else if (generator == DH_GENERATOR_5) { 48 | if (!BN_set_word(bigAdd.get(), 60) || !BN_set_word(bigRem.get(), 59)) { 49 | return {Result::Fail, "Couldn't generate params: BN_set_word failed: {}", 50 | OpenSSL::GetLastErrorString()}; 51 | } 52 | } else { 53 | if (!BN_set_word(bigAdd.get(), 12) || !BN_set_word(bigRem.get(), 11)) { 54 | return {Result::Fail, "Couldn't generate params: BN_set_word failed: {}", 55 | OpenSSL::GetLastErrorString()}; 56 | } 57 | } 58 | 59 | prime_.reset(BN_new()); 60 | if (!prime_) { 61 | return {Result::Fail, "Couldn't generate params: prime BN_new failed: {}", 62 | OpenSSL::GetLastErrorString()}; 63 | } 64 | 65 | generator_.reset(BN_new()); 66 | if (!generator_) { 67 | return {Result::Fail, "Couldn't generate params: generator BN_new failed: {}", 68 | OpenSSL::GetLastErrorString()}; 69 | } 70 | 71 | if (!BN_generate_prime_ex(prime_.get(), primeLengthInBits, 1, 72 | bigAdd.get(), bigRem.get(), nullptr)) { 73 | return {Result::Fail, 74 | "Couldn't generate params: generator BN_generate_prime_ex failed: {}", 75 | OpenSSL::GetLastErrorString()}; 76 | } 77 | 78 | if (!BN_set_word(generator_.get(), static_cast(generator))) { 79 | return {Result::Fail, 80 | "Couldn't generate params: generator BN_set_word(genrator): {}", 81 | OpenSSL::GetLastErrorString()}; 82 | } 83 | 84 | return {Result::Success}; 85 | } 86 | 87 | Result DiffieHellmanOpenSSL::SetParameters(std::string_view hexPrime, std::string_view hexGenerator) { 88 | ERR_clear_error(); 89 | 90 | prime_.reset(OpenSSL::ConvertHexToBigNum(hexPrime)); 91 | if (!prime_) { 92 | return {Result::Fail, 93 | "Couldn't set params: ConvertHexToBigNum(hexPrime) failed: {}", 94 | OpenSSL::GetLastErrorString()}; 95 | } 96 | 97 | generator_.reset(OpenSSL::ConvertHexToBigNum(hexGenerator)); 98 | if (!generator_) { 99 | return {Result::Fail, 100 | "Couldn't set params: ConvertHexToBigNum(hexGenerator) failed: {}", 101 | OpenSSL::GetLastErrorString()}; 102 | } 103 | 104 | return {Result::Success}; 105 | } 106 | 107 | Result DiffieHellmanOpenSSL::GetParameters(std::string* hexPrime, std::string* hexGenerator) const { 108 | ERR_clear_error(); 109 | 110 | *hexPrime = OpenSSL::ConvertBigNumToHex(prime_.get()); 111 | if (hexPrime->empty()) { 112 | return {Result::Fail, 113 | "Couldn't get params: ConvertBigNumToHex(prime) failed: {}", 114 | OpenSSL::GetLastErrorString()}; 115 | } 116 | 117 | *hexGenerator = OpenSSL::ConvertBigNumToHex(generator_.get()); 118 | if (hexGenerator->empty()) { 119 | return {Result::Fail, 120 | "Couldn't get params: ConvertBigNumToHex(generator) failed: {}", 121 | OpenSSL::GetLastErrorString()}; 122 | } 123 | 124 | return {Result::Success}; 125 | } 126 | 127 | Result DiffieHellmanOpenSSL::GenerateKeys() { 128 | ERR_clear_error(); 129 | 130 | std::unique_ptr> domainParamKey; 131 | Result result = CreateDomainParameterKey(&domainParamKey); 132 | if (!result) { 133 | return result; 134 | } 135 | 136 | std::unique_ptr> keyGenCtx{ 137 | EVP_PKEY_CTX_new_from_pkey(nullptr, domainParamKey.get(), nullptr)}; 138 | if (!keyGenCtx) { 139 | return {Result::Fail, 140 | "Couldn't generate: EVP_PKEY_CTX_new_from_pkey failed: {}", 141 | OpenSSL::GetLastErrorString()}; 142 | } 143 | 144 | if (EVP_PKEY_keygen_init(keyGenCtx.get()) <= 0) { 145 | return {Result::Fail, "Couldn't generate: EVP_PKEY_keygen_init failed: {}", 146 | OpenSSL::GetLastErrorString()}; 147 | } 148 | 149 | EVP_PKEY* keyPair = nullptr; 150 | if (EVP_PKEY_generate(keyGenCtx.get(), &keyPair) <= 0) { 151 | return {Result::Fail, "Couldn't generate: EVP_PKEY_generate failed: {}", 152 | OpenSSL::GetLastErrorString()}; 153 | } 154 | 155 | keyPair_.reset(keyPair); 156 | 157 | return {Result::Success}; 158 | } 159 | 160 | Result DiffieHellmanOpenSSL::GetPrivateKey(std::string* hexPrivateKey) const { 161 | ERR_clear_error(); 162 | 163 | BIGNUM* privateKey = nullptr; 164 | if (!EVP_PKEY_get_bn_param(keyPair_.get(), OSSL_PKEY_PARAM_PRIV_KEY, &privateKey)) { 165 | return {Result::Fail, 166 | "Could not get the private key: EVP_PKEY_get_bn_param failed: {}", 167 | OpenSSL::GetLastErrorString()}; 168 | } 169 | 170 | std::unique_ptr> privateKeyHolder{privateKey}; 171 | 172 | *hexPrivateKey = OpenSSL::ConvertBigNumToHex(privateKey); 173 | if (hexPrivateKey->empty()) { 174 | return {Result::Fail, 175 | "Could not get the private key: ConvertBigNumToHex failed: {}", 176 | OpenSSL::GetLastErrorString()}; 177 | } 178 | 179 | return {Result::Success}; 180 | } 181 | 182 | Result DiffieHellmanOpenSSL::GetPublicKey(std::string* hexPublicKey) const { 183 | ERR_clear_error(); 184 | 185 | BIGNUM* publicKey = nullptr; 186 | if (!EVP_PKEY_get_bn_param(keyPair_.get(), OSSL_PKEY_PARAM_PUB_KEY, &publicKey)) { 187 | return {Result::Fail, 188 | "Could not get the public key: EVP_PKEY_get_bn_param failed: {}", 189 | OpenSSL::GetLastErrorString()}; 190 | } 191 | 192 | std::unique_ptr> publicKeyHolder{publicKey}; 193 | 194 | *hexPublicKey = OpenSSL::ConvertBigNumToHex(publicKey); 195 | if (hexPublicKey->empty()) { 196 | return {Result::Fail, 197 | "Could not get the public key: ConvertBigNumToHex failed: {}", 198 | OpenSSL::GetLastErrorString()}; 199 | } 200 | 201 | return {Result::Success}; 202 | } 203 | 204 | Result DiffieHellmanOpenSSL::DeriveSharedSecret(std::string_view hexPeerPublicKey, 205 | std::string* hexSharedSecret) const { 206 | ERR_clear_error(); 207 | 208 | std::unique_ptr> peerPublicKey; 209 | Result result = CreatePeerPublicKey(hexPeerPublicKey, &peerPublicKey); 210 | if (!result) { 211 | return result; 212 | } 213 | 214 | std::unique_ptr> 215 | derivationCtx{EVP_PKEY_CTX_new(keyPair_.get(), nullptr)}; 216 | if (!derivationCtx) { 217 | return {Result::Fail, 218 | "Couldn't derive: EVP_PKEY_CTX_new failed: {}", 219 | OpenSSL::GetLastErrorString()}; 220 | } 221 | 222 | if (EVP_PKEY_derive_init(derivationCtx.get()) <= 0) { 223 | return {Result::Fail, 224 | "Couldn't derive: EVP_PKEY_derive_init failed: {}", 225 | OpenSSL::GetLastErrorString()}; 226 | } 227 | 228 | if (EVP_PKEY_derive_set_peer(derivationCtx.get(), peerPublicKey.get()) <= 0) { 229 | return {Result::Fail, 230 | "Couldn't derive: EVP_PKEY_derive_set_peer failed: {}", 231 | OpenSSL::GetLastErrorString()}; 232 | } 233 | 234 | size_t len = 0; 235 | if (EVP_PKEY_derive(derivationCtx.get(), nullptr, &len) <= 0) { 236 | return {Result::Fail, 237 | "Couldn't derive: EVP_PKEY_derive: {}", 238 | OpenSSL::GetLastErrorString()}; 239 | } 240 | 241 | if (len == 0) { 242 | return {Result::Fail, "Couldn't derive: share secret length is zero."}; 243 | } 244 | 245 | std::string sharedSecret; 246 | sharedSecret.resize(len); 247 | 248 | if (EVP_PKEY_derive(derivationCtx.get(), 249 | static_cast(static_cast(&sharedSecret.front())), 250 | &len) <= 0) { 251 | return {Result::Fail, 252 | "Couldn't derive: EVP_PKEY_derive failed: {}", 253 | OpenSSL::GetLastErrorString()}; 254 | } 255 | 256 | *hexSharedSecret = Base::ConvertDataToHex(sharedSecret); 257 | 258 | return {Result::Success}; 259 | } 260 | 261 | std::tuple DiffieHellmanOpenSSL::GetPrimeLength() const { 262 | if (!prime_) { 263 | return {-1, -1}; 264 | } 265 | return {BN_num_bits(prime_.get()), BN_num_bytes(prime_.get())}; 266 | } 267 | 268 | std::tuple DiffieHellmanOpenSSL::GetPrivateKeyLength() const { 269 | BIGNUM* privateKey = nullptr; 270 | if (!EVP_PKEY_get_bn_param(keyPair_.get(), OSSL_PKEY_PARAM_PRIV_KEY, &privateKey)) { 271 | return {-1, -1}; 272 | } 273 | 274 | std::unique_ptr> privateKeyHolder{privateKey}; 275 | return {BN_num_bits(privateKey), BN_num_bytes(privateKey)}; 276 | } 277 | 278 | std::tuple DiffieHellmanOpenSSL::GetPublicKeyLength() const { 279 | BIGNUM* publicKey = nullptr; 280 | if (!EVP_PKEY_get_bn_param(keyPair_.get(), OSSL_PKEY_PARAM_PUB_KEY, &publicKey)) { 281 | return {-1, -1}; 282 | } 283 | 284 | std::unique_ptr> publicKeyHolder{publicKey}; 285 | return {BN_num_bits(publicKey), BN_num_bytes(publicKey)}; 286 | } 287 | 288 | Result DiffieHellmanOpenSSL::CreatePeerPublicKey(std::string_view hexPeerPublicKey, 289 | std::unique_ptr>* peerPubKey) const { 290 | std::unique_ptr> 291 | bigNumPeerPublicKey{OpenSSL::ConvertHexToBigNum(hexPeerPublicKey)}; 292 | if (!bigNumPeerPublicKey) { 293 | return {Result::Fail, 294 | "Couldn't create the peer public key: ConvertHexToBigNum failed: {}", 295 | OpenSSL::GetLastErrorString()}; 296 | } 297 | 298 | std::unique_ptr> 299 | paramBuild{OSSL_PARAM_BLD_new()}; 300 | if (!paramBuild) { 301 | return {Result::Fail, 302 | "Couldn't create the peer public key: OSSL_PARAM_BLD_new failed: {}", 303 | OpenSSL::GetLastErrorString()}; 304 | } 305 | 306 | if (!OSSL_PARAM_BLD_push_BN(paramBuild.get(), 307 | OSSL_PKEY_PARAM_PUB_KEY, bigNumPeerPublicKey.get())) { 308 | return {Result::Fail, 309 | "Couldn't create the peer public key: OSSL_PARAM_BLD_push_BN(pub key) failed: {}", 310 | OpenSSL::GetLastErrorString()}; 311 | } 312 | 313 | if (!OSSL_PARAM_BLD_push_BN(paramBuild.get(), OSSL_PKEY_PARAM_FFC_P, prime_.get()) || 314 | !OSSL_PARAM_BLD_push_BN(paramBuild.get(), OSSL_PKEY_PARAM_FFC_G, generator_.get())) { 315 | return {Result::Fail, 316 | "Couldn't create the peer public key: OSSL_PARAM_BLD_push_BN(p or g) failed: {}", 317 | OpenSSL::GetLastErrorString()}; 318 | } 319 | 320 | std::unique_ptr> 321 | param{OSSL_PARAM_BLD_to_param(paramBuild.get())}; 322 | if (!param) { 323 | return {Result::Fail, 324 | "Couldn't create the peer public key: OSSL_PARAM_BLD_to_param failed: {}", 325 | OpenSSL::GetLastErrorString()}; 326 | } 327 | 328 | std::unique_ptr> 329 | peerPublicKeyCtx{EVP_PKEY_CTX_new_from_name(nullptr, "DHX", nullptr)}; 330 | if (!peerPublicKeyCtx) { 331 | return {Result::Fail, 332 | "Couldn't create the peer public key: EVP_PKEY_CTX_new_from_name failed: {}", 333 | OpenSSL::GetLastErrorString()}; 334 | } 335 | 336 | if (EVP_PKEY_fromdata_init(peerPublicKeyCtx.get()) <= 0) { 337 | return {Result::Fail, 338 | "Couldn't create the peer public key: EVP_PKEY_fromdata_init failed: {}", 339 | OpenSSL::GetLastErrorString()}; 340 | } 341 | 342 | EVP_PKEY* tmp = nullptr; 343 | if (EVP_PKEY_fromdata(peerPublicKeyCtx.get(), &tmp, 344 | EVP_PKEY_PUBLIC_KEY, param.get()) <= 0) { 345 | return {Result::Fail, 346 | "Couldn't create the peer public key: EVP_PKEY_fromdata failed: {}", 347 | OpenSSL::GetLastErrorString()}; 348 | } 349 | 350 | peerPubKey->reset(tmp); 351 | 352 | return {Result::Success}; 353 | } 354 | 355 | Result DiffieHellmanOpenSSL::CreateDomainParameterKey( 356 | std::unique_ptr>* domainParamKey) const 357 | { 358 | std::unique_ptr> paramBuild{ 359 | OSSL_PARAM_BLD_new()}; 360 | if (!paramBuild) { 361 | return {Result::Fail, 362 | "Couldn't create the domain param key: OSSL_PARAM_BLD_new failed: {}", 363 | OpenSSL::GetLastErrorString()}; 364 | } 365 | 366 | if (!OSSL_PARAM_BLD_push_BN(paramBuild.get(), OSSL_PKEY_PARAM_FFC_P, prime_.get()) || 367 | !OSSL_PARAM_BLD_push_BN(paramBuild.get(), OSSL_PKEY_PARAM_FFC_G, generator_.get())) { 368 | return {Result::Fail, 369 | "Couldn't create the domain param key: OSSL_PARAM_BLD_push_BN failed: {}", 370 | OpenSSL::GetLastErrorString()}; 371 | } 372 | 373 | std::unique_ptr> 374 | param{OSSL_PARAM_BLD_to_param(paramBuild.get())}; 375 | if (!param) { 376 | return {Result::Fail, 377 | "Couldn't create the domain param key: OSSL_PARAM_BLD_to_param failed: {}", 378 | OpenSSL::GetLastErrorString()}; 379 | } 380 | 381 | std::unique_ptr> 382 | domainParamKeyCtx{EVP_PKEY_CTX_new_from_name(nullptr, "DHX", nullptr)}; 383 | if (!domainParamKeyCtx) { 384 | return {Result::Fail, 385 | "Couldn't create the domain param key: EVP_PKEY_CTX_new_from_name failed: {}", 386 | OpenSSL::GetLastErrorString()}; 387 | } 388 | 389 | if (EVP_PKEY_fromdata_init(domainParamKeyCtx.get()) <= 0) { 390 | return {Result::Fail, 391 | "Couldn't create the domain param key: EVP_PKEY_fromdata_init failed: {}", 392 | OpenSSL::GetLastErrorString()}; 393 | } 394 | 395 | EVP_PKEY* tmp = nullptr; 396 | if (EVP_PKEY_fromdata(domainParamKeyCtx.get(), &tmp, 397 | EVP_PKEY_KEY_PARAMETERS, param.get()) <= 0) { 398 | return {Result::Fail, 399 | "Couldn't create the domain param key: EVP_PKEY_fromdata failed: {}", 400 | OpenSSL::GetLastErrorString()}; 401 | } 402 | 403 | domainParamKey->reset(tmp); 404 | 405 | return {Result::Success}; 406 | 407 | } -------------------------------------------------------------------------------- /src/diffie-hellman-openssl.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include "result.h" 14 | #include "openssl-helpers.h" 15 | #include "diffie-hellman.h" 16 | 17 | // Diffie-Hellman implementation based on OpenSSL 3.x.x. 18 | class DiffieHellmanOpenSSL : public DiffieHellman { 19 | public: 20 | DiffieHellmanOpenSSL(); 21 | ~DiffieHellmanOpenSSL() override; 22 | 23 | std::string_view GetImplementationName() const override; 24 | 25 | Result GenerateParameters(int primeLengthInBits, int generator) override; 26 | 27 | Result SetParameters(std::string_view hexPrime, std::string_view hexGenerator) override; 28 | Result GetParameters(std::string* hexPrime, std::string* hexGenerator) const override; 29 | 30 | Result GenerateKeys() override; 31 | Result GetPrivateKey(std::string* hexPrivateKey) const override; 32 | Result GetPublicKey(std::string* hexPublicKey) const override; 33 | 34 | Result DeriveSharedSecret(std::string_view hexPeerPublicKey, 35 | std::string* hexSharedSecret) const override; 36 | 37 | std::tuple GetPrimeLength() const override; 38 | std::tuple GetPrivateKeyLength() const override; 39 | std::tuple GetPublicKeyLength() const override; 40 | 41 | private: 42 | 43 | Result CreatePeerPublicKey(std::string_view hexPeerPublicKey, 44 | std::unique_ptr>* peerPubKey) const; 45 | 46 | Result CreateDomainParameterKey( 47 | std::unique_ptr>* domainParamKey) const; 48 | 49 | std::unique_ptr> prime_; 50 | std::unique_ptr> generator_; 51 | std::unique_ptr> keyPair_; 52 | }; 53 | -------------------------------------------------------------------------------- /src/diffie-hellman.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #if defined(USE_OPENSSL) 6 | #include "openssl-helpers.h" 7 | #include "diffie-hellman-openssl.h" 8 | #else 9 | #include "libressl-helpers.h" 10 | #include "diffie-hellman-libressl-dh.h" 11 | #include "diffie-hellman-libressl-bn.h" 12 | #endif 13 | 14 | #include "diffie-hellman-boost.h" 15 | #include "diffie-hellman.h" 16 | 17 | std::unique_ptr DiffieHellman::Create(Implementation impl) { 18 | switch (impl) { 19 | #if defined(USE_OPENSSL) 20 | case Implementation::OpenSSL: return std::make_unique(); 21 | #else 22 | case Implementation::LibreSSLDH: return std::make_unique(); 23 | case Implementation::LibreSSLBN: return std::make_unique(); 24 | #endif 25 | case Implementation::Boost: return std::make_unique(); 26 | default: break; 27 | } 28 | return std::unique_ptr(); 29 | } 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/diffie-hellman.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "result.h" 12 | 13 | // The Diffie-Hellman interface. 14 | class DiffieHellman { 15 | public: 16 | 17 | enum class Implementation { 18 | Undefined = 0, 19 | OpenSSL, // Based on OpenSSL DH 20 | LibreSSLDH, // Based on LibreSSL DH 21 | LibreSSLBN, // Based on LibreSSL BIGNUM 22 | Boost // Based on boost 23 | }; 24 | 25 | // Create an instance. 26 | static std::unique_ptr Create(Implementation impl); 27 | 28 | // Default constructor. 29 | DiffieHellman() = default; 30 | 31 | // For derived classes. 32 | virtual ~DiffieHellman() {} 33 | 34 | // Gets implementation name. 35 | virtual std::string_view GetImplementationName() const = 0; 36 | 37 | // Generates a safe prime with the specified length and 38 | // sets the generator. 39 | virtual Result GenerateParameters(int primeLengthInBits, int generator) = 0; 40 | 41 | // Sets the prime and the generator. 42 | // They must be specified in hex format. 43 | virtual Result SetParameters(std::string_view hexPrime, std::string_view hexGenerator) = 0; 44 | 45 | // Gets the prime and the generator. 46 | virtual Result GetParameters(std::string* hexPrime, std::string* hexGenerator) const = 0; 47 | 48 | // Generate a private key and then derives a public key. 49 | virtual Result GenerateKeys() = 0; 50 | 51 | // Gets the private key. 52 | virtual Result GetPrivateKey(std::string* hexPrivateKey) const = 0; 53 | 54 | // Gets the public key. 55 | virtual Result GetPublicKey(std::string* hexPublicKey) const = 0; 56 | 57 | // Derives the shared secret based on this objects values and the peer public key. 58 | virtual Result DeriveSharedSecret(std::string_view hexPeerPublicKey, std::string* hexSharedSecret) const = 0; 59 | 60 | // Gets the lengths in bits and in bytes (for debug). 61 | // Returns {-1, -1} if anything fails. 62 | virtual std::tuple GetPrimeLength() const = 0; 63 | virtual std::tuple GetPrivateKeyLength() const = 0; 64 | virtual std::tuple GetPublicKeyLength() const = 0; 65 | 66 | private: 67 | // Non-copyable. 68 | DiffieHellman(const DiffieHellman&) = delete; 69 | DiffieHellman& operator=(const DiffieHellman&) = delete; 70 | }; 71 | -------------------------------------------------------------------------------- /src/ec-diffie-hellman-libressl.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include "libressl-helpers.h" 11 | #include "ec-diffie-hellman-libressl.h" 12 | 13 | ECDiffieHellmanLibreSSL::ECDiffieHellmanLibreSSL() { 14 | 15 | } 16 | 17 | ECDiffieHellmanLibreSSL::~ECDiffieHellmanLibreSSL() { 18 | 19 | } 20 | 21 | std::string_view ECDiffieHellmanLibreSSL::GetImplementationName() const { 22 | return "LibreSSL ECDH"; 23 | } 24 | 25 | Result ECDiffieHellmanLibreSSL::GetSupportedCurves( 26 | std::map* curves) const { 27 | ERR_clear_error(); 28 | size_t count = EC_get_builtin_curves(nullptr, 0); 29 | if (count <= 0) { 30 | return {Result::Fail, 31 | "Couldn't get built-in curve list: 0 count."}; 32 | } 33 | 34 | auto buffer = std::make_unique(count); 35 | if (EC_get_builtin_curves(buffer.get(), count) != count) { 36 | return {Result::Fail, 37 | "Couldn't get built-in curve list: EC_get_builtin_curves failed."}; 38 | } 39 | 40 | for (int i = 0; i < count; ++i) { 41 | const char* shortName = OBJ_nid2sn(buffer[i].nid); 42 | const char* comment = buffer[i].comment; 43 | if (shortName && std::strlen(shortName) > 0) { 44 | curves->emplace(shortName, 45 | CurveInfo{buffer[i].nid, shortName, (comment) ? comment : ""}); 46 | } 47 | } 48 | 49 | return {Result::Success}; 50 | } 51 | 52 | Result ECDiffieHellmanLibreSSL::SetCurveName(std::string_view curveName) { 53 | std::map curves; 54 | Result result = GetSupportedCurves(&curves); 55 | if (!result) { 56 | return result; 57 | } 58 | 59 | auto it = curves.find(curveName.data()); 60 | if (it == curves.end()) { 61 | return {Result::Fail, "The curve is not supported."}; 62 | } 63 | 64 | curveName_ = (*it).second.name_; 65 | curveId_ = (*it).second.internalId_; 66 | 67 | return {Result::Success}; 68 | } 69 | 70 | Result ECDiffieHellmanLibreSSL::GetCurveName(std::string* curveName) const { 71 | if (curveName_.empty()) { 72 | return {Result::Fail, "The curve name is not set."}; 73 | } 74 | *curveName = curveName_; 75 | return {Result::Success}; 76 | } 77 | 78 | Result ECDiffieHellmanLibreSSL::GenerateKeys() { 79 | ERR_clear_error(); 80 | 81 | // Create the key parameter context. 82 | std::unique_ptr> 83 | keyParamCtx{EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr)}; 84 | if (!keyParamCtx) { 85 | return {Result::Fail, 86 | "Couldn't generate: EVP_PKEY_CTX_new_id failed: {}", 87 | LibreSSL::GetLastErrorString()}; 88 | } 89 | 90 | // Initialize the key parameter context. 91 | if (EVP_PKEY_paramgen_init(keyParamCtx.get()) <= 0) { 92 | return {Result::Fail, 93 | "Couldn't generate: EVP_PKEY_paramgen_init failed: {}", 94 | LibreSSL::GetLastErrorString()}; 95 | } 96 | 97 | // Set the curve name to the key parameter context. 98 | if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(keyParamCtx.get(), curveId_) <= 0) { 99 | return {Result::Fail, 100 | "Couldn't generate: EVP_PKEY_CTX_set_ec_paramgen_curve_nid failed: {}", 101 | LibreSSL::GetLastErrorString()}; 102 | } 103 | 104 | // Generate key parameters. 105 | EVP_PKEY* keyParam = nullptr; 106 | if (EVP_PKEY_paramgen(keyParamCtx.get(), &keyParam) <= 0) { 107 | return {Result::Fail, 108 | "Couldn't generate: EVP_PKEY_paramgen failed: {}", 109 | LibreSSL::GetLastErrorString()}; 110 | } 111 | 112 | // keyParam auto free 113 | std::unique_ptr> 114 | keyParamHolder(keyParam); 115 | 116 | // Create the key generation context. 117 | std::unique_ptr> 118 | keyPairGenerationCtx{EVP_PKEY_CTX_new(keyParam, nullptr)}; 119 | if (!keyPairGenerationCtx) { 120 | return {Result::Fail, 121 | "Couldn't generate: EVP_PKEY_CTX_new failed: {}", 122 | LibreSSL::GetLastErrorString()}; 123 | } 124 | 125 | // Initialize the key generation context. 126 | if (EVP_PKEY_keygen_init(keyPairGenerationCtx.get()) <= 0) { 127 | return {Result::Fail, 128 | "Couldn't generate: EVP_PKEY_keygen_init failed: {}", 129 | LibreSSL::GetLastErrorString()}; 130 | } 131 | 132 | // Genearate keys. 133 | EVP_PKEY* keyPair = nullptr; 134 | if (EVP_PKEY_keygen(keyPairGenerationCtx.get(), &keyPair) <= 0) { 135 | return {Result::Fail, 136 | "Couldn't generate: EVP_PKEY_keygen failed: {}", 137 | LibreSSL::GetLastErrorString()}; 138 | } 139 | 140 | // Set the key pair. 141 | keyPair_.reset(keyPair); 142 | 143 | return {Result::Success}; 144 | } 145 | 146 | Result ECDiffieHellmanLibreSSL::GetPrivateKey(std::string* hexPrivateKey) const { 147 | ERR_clear_error(); 148 | 149 | const EC_KEY* ecKey = EVP_PKEY_get0_EC_KEY(keyPair_.get()); 150 | if (!ecKey) { 151 | return {Result::Fail, 152 | "Couldn't get the private key: EVP_PKEY_get0_EC_KEY failed: {}", 153 | LibreSSL::GetLastErrorString()}; 154 | } 155 | 156 | const BIGNUM* privateKey = EC_KEY_get0_private_key(ecKey); 157 | if (!privateKey) { 158 | return {Result::Fail, 159 | "Couldn't get the private key: EVP_PKEY_get0_EC_KEY failed: {}", 160 | LibreSSL::GetLastErrorString()}; 161 | } 162 | 163 | *hexPrivateKey = LibreSSL::ConvertBigNumToHex(privateKey); 164 | return {Result::Success}; 165 | } 166 | 167 | Result ECDiffieHellmanLibreSSL::GetPublicKey(std::string* hexPublicKey) const { 168 | ERR_clear_error(); 169 | 170 | const EC_KEY* ecKey = EVP_PKEY_get0_EC_KEY(keyPair_.get()); 171 | if (!ecKey) { 172 | return {Result::Fail, 173 | "Couldn't get the public key: EVP_PKEY_get0_EC_KEY failed: {}", 174 | LibreSSL::GetLastErrorString()}; 175 | } 176 | 177 | const EC_GROUP* ecGroup = EC_KEY_get0_group(ecKey); 178 | 179 | std::unique_ptr> hexECPoint{ 180 | EC_POINT_point2hex(ecGroup, EC_KEY_get0_public_key(ecKey), 181 | POINT_CONVERSION_COMPRESSED, nullptr) }; 182 | if (!hexECPoint) { 183 | return {Result::Fail, 184 | "Couldn't get the public key: EC_POINT_point2hex failed: {}", 185 | LibreSSL::GetLastErrorString()}; 186 | } 187 | 188 | *hexPublicKey = hexECPoint.get(); 189 | return {Result::Success}; 190 | } 191 | 192 | Result ECDiffieHellmanLibreSSL::DeriveSharedSecret(std::string_view hexPeerPublicKey, 193 | std::string* hexSharedSecret) const { 194 | ERR_clear_error(); 195 | 196 | const EC_GROUP* ecGroup = EC_GROUP_new_by_curve_name(curveId_); 197 | 198 | std::unique_ptr> 199 | peerPublicECPoint{EC_POINT_hex2point(ecGroup, 200 | hexPeerPublicKey.data(), nullptr, nullptr)}; 201 | if (!peerPublicECPoint) { 202 | return {Result::Fail, 203 | "Couldn't derive: EC_POINT_hex2point failed: {}", 204 | LibreSSL::GetLastErrorString()}; 205 | } 206 | 207 | std::unique_ptr> 208 | peerPublicECKey{EC_KEY_new_by_curve_name(curveId_)}; 209 | if (!peerPublicECKey) { 210 | return {Result::Fail, 211 | "Couldn't derive: EC_KEY_new_by_curve_name failed: {}", 212 | LibreSSL::GetLastErrorString()}; 213 | } 214 | 215 | if (!EC_KEY_set_public_key(peerPublicECKey.get(), peerPublicECPoint.get())) { 216 | return {Result::Fail, 217 | "Couldn't derive: EC_KEY_set_public_key failed: {}", 218 | LibreSSL::GetLastErrorString()}; 219 | } 220 | 221 | std::unique_ptr> 222 | peerPublicKey{EVP_PKEY_new()}; 223 | if (!peerPublicKey) { 224 | return {Result::Fail, 225 | "Couldn't derive: EVP_PKEY_new failed: {}", 226 | LibreSSL::GetLastErrorString()}; 227 | } 228 | 229 | if (!EVP_PKEY_set1_EC_KEY(peerPublicKey.get(), peerPublicECKey.get())) { 230 | return {Result::Fail, 231 | "Couldn't derive: EVP_PKEY_set1_EC_KEY failed: {}", 232 | LibreSSL::GetLastErrorString()}; 233 | } 234 | 235 | std::unique_ptr> 236 | derivationCtx{EVP_PKEY_CTX_new(keyPair_.get(), nullptr) }; 237 | if (!derivationCtx) { 238 | return {Result::Fail, 239 | "Couldn't derive: EVP_PKEY_CTX_new failed: {}", 240 | LibreSSL::GetLastErrorString()}; 241 | } 242 | 243 | if (EVP_PKEY_derive_init(derivationCtx.get()) <= 0) { 244 | return {Result::Fail, 245 | "Couldn't derive: EVP_PKEY_derive_init failed: {}", 246 | LibreSSL::GetLastErrorString()}; 247 | } 248 | 249 | if (EVP_PKEY_derive_set_peer(derivationCtx.get(), peerPublicKey.get()) <= 0) { 250 | return {Result::Fail, 251 | "Couldn't derive: EVP_PKEY_derive_set_peer failed: {}", 252 | LibreSSL::GetLastErrorString()}; 253 | } 254 | 255 | size_t sharedSecretLen = 0; 256 | if (EVP_PKEY_derive(derivationCtx.get(), nullptr, &sharedSecretLen) <= 0) { 257 | return {Result::Fail, 258 | "Couldn't derive: EVP_PKEY_derive returned zero shared secret length: {}", 259 | LibreSSL::GetLastErrorString()}; 260 | } 261 | 262 | // Prepare the shared secret container. 263 | std::string sharedSecret; 264 | sharedSecret.resize(sharedSecretLen); 265 | 266 | // Derive the shared secret. 267 | if (EVP_PKEY_derive(derivationCtx.get(), 268 | static_cast(static_cast(&sharedSecret.front())), 269 | &sharedSecretLen) <= 0) { 270 | return {Result::Fail, 271 | "Couldn't derive: EVP_PKEY_derive failed: {}", 272 | LibreSSL::GetLastErrorString()}; 273 | } 274 | 275 | // Convert to a hex string. 276 | *hexSharedSecret = Base::ConvertDataToHex(sharedSecret); 277 | 278 | return {Result::Success}; 279 | } 280 | -------------------------------------------------------------------------------- /src/ec-diffie-hellman-libressl.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include "base-helpers.h" 11 | #include "ec-diffie-hellman.h" 12 | 13 | #include 14 | 15 | // Elliptic-curve Diffie-Hellman implementation based on OpenSSL 3.x.x. 16 | class ECDiffieHellmanLibreSSL : public ECDiffieHellman { 17 | public: 18 | ECDiffieHellmanLibreSSL(); 19 | ~ECDiffieHellmanLibreSSL() override; 20 | 21 | std::string_view GetImplementationName() const override; 22 | 23 | Result GetSupportedCurves(std::map* curves) const override; 25 | 26 | Result SetCurveName(std::string_view curveName) override; 27 | Result GetCurveName(std::string* curveName) const override; 28 | 29 | Result GenerateKeys() override; 30 | Result GetPrivateKey(std::string* hexPrivateKey) const override; 31 | Result GetPublicKey(std::string* hexPublicKey) const override; 32 | 33 | Result DeriveSharedSecret(std::string_view hexPeerPublicKey, 34 | std::string* hexSharedSecret) const override; 35 | 36 | private: 37 | std::string curveName_; 38 | int curveId_ = NID_undef; 39 | std::unique_ptr> keyPair_; 40 | }; 41 | -------------------------------------------------------------------------------- /src/ec-diffie-hellman-openssl.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "openssl-helpers.h" 13 | #include "ec-diffie-hellman-openssl.h" 14 | 15 | ECDiffieHellmanOpenSSL::ECDiffieHellmanOpenSSL() { 16 | 17 | } 18 | 19 | ECDiffieHellmanOpenSSL::~ECDiffieHellmanOpenSSL() { 20 | 21 | } 22 | 23 | std::string_view ECDiffieHellmanOpenSSL::GetImplementationName() const { 24 | return "OpenSSL ECDH"; 25 | } 26 | 27 | Result ECDiffieHellmanOpenSSL::GetSupportedCurves( 28 | std::map* curves) const { 29 | ERR_clear_error(); 30 | size_t count = EC_get_builtin_curves(nullptr, 0); 31 | if (count <= 0) { 32 | return {Result::Fail, 33 | "Couldn't get built-in curve list: 0 count."}; 34 | } 35 | 36 | auto buffer = std::make_unique(count); 37 | if (EC_get_builtin_curves(buffer.get(), count) != count) { 38 | return {Result::Fail, 39 | "Couldn't get built-in curve list: EC_get_builtin_curves failed."}; 40 | } 41 | 42 | for (int i = 0; i < count; ++i) { 43 | const char* shortName = OBJ_nid2sn(buffer[i].nid); 44 | const char* comment = buffer[i].comment; 45 | if (shortName && std::strlen(shortName) > 0) { 46 | curves->emplace(shortName, 47 | CurveInfo{buffer[i].nid, shortName, (comment) ? comment : ""}); 48 | } 49 | } 50 | 51 | return {Result::Success}; 52 | } 53 | 54 | Result ECDiffieHellmanOpenSSL::SetCurveName(std::string_view curveName) { 55 | std::map curves; 56 | Result result = GetSupportedCurves(&curves); 57 | if (!result) { 58 | return result; 59 | } 60 | 61 | // Check if the curve is in the list. 62 | if (!curves.count(curveName.data())) { 63 | return {Result::Fail, "The curve is not supported."}; 64 | } 65 | 66 | curveName_ = curveName; 67 | return {Result::Success}; 68 | } 69 | 70 | Result ECDiffieHellmanOpenSSL::GetCurveName(std::string* curveName) const { 71 | if (curveName_.empty()) { 72 | return {Result::Fail, "The curve name is not set."}; 73 | } 74 | *curveName = curveName_; 75 | return {Result::Success}; 76 | } 77 | 78 | Result ECDiffieHellmanOpenSSL::GenerateKeys() { 79 | ERR_clear_error(); 80 | 81 | // First, create an OSSL_PARAM_BLD. 82 | std::unique_ptr> 83 | paramBuild{OSSL_PARAM_BLD_new()}; 84 | if (!paramBuild) { 85 | return {Result::Fail, 86 | "Couldn't generate: OSSL_PARAM_BLD_new failed: {}", 87 | OpenSSL::GetLastErrorString()}; 88 | } 89 | 90 | // Push the curve name to the OSSL_PARAM_BLD. 91 | if (!OSSL_PARAM_BLD_push_utf8_string(paramBuild.get(), 92 | OSSL_PKEY_PARAM_GROUP_NAME, curveName_.data(), 0)) { 93 | return {Result::Fail, 94 | "Couldn't generate: OSSL_PARAM_BLD_push_utf8_string failed: {}", 95 | OpenSSL::GetLastErrorString()}; 96 | } 97 | 98 | // Convert OSSL_PARAM_BLD to OSSL_PARAM. 99 | std::unique_ptr> 100 | params{OSSL_PARAM_BLD_to_param(paramBuild.get())}; 101 | if (!params) { 102 | return {Result::Fail, 103 | "Couldn't generate: OSSL_PARAM_BLD_to_param failed: {}", 104 | OpenSSL::GetLastErrorString()}; 105 | } 106 | 107 | // Create the EC key generation context. 108 | std::unique_ptr> 109 | ctx{EVP_PKEY_CTX_new_from_name(nullptr, "EC", nullptr)}; 110 | if (!ctx) { 111 | return {Result::Fail, 112 | "Couldn't generate: EVP_PKEY_CTX_new_from_name failed: {}", 113 | OpenSSL::GetLastErrorString()}; 114 | } 115 | 116 | // Initialize the key generation context. 117 | if (EVP_PKEY_keygen_init(ctx.get()) <= 0) { 118 | return {Result::Fail, 119 | "Couldn't generate: EVP_PKEY_keygen_init failed: {}", 120 | OpenSSL::GetLastErrorString()}; 121 | } 122 | 123 | // Set the parameters which include the curve name. 124 | if (!EVP_PKEY_CTX_set_params(ctx.get(), params.get())) { 125 | return {Result::Fail, 126 | "Couldn't generate: EVP_PKEY_CTX_set_params failed: {}", 127 | OpenSSL::GetLastErrorString()}; 128 | } 129 | 130 | // Generate a key pair. 131 | EVP_PKEY* keyPair = nullptr; 132 | if (EVP_PKEY_generate(ctx.get(), &keyPair) <= 0) { 133 | return {Result::Fail, 134 | "Couldn't generate: EVP_PKEY_generate failed: {}", 135 | OpenSSL::GetLastErrorString()}; 136 | } 137 | 138 | keyPair_.reset(keyPair); 139 | 140 | return {Result::Success}; 141 | } 142 | 143 | Result ECDiffieHellmanOpenSSL::GetPrivateKey(std::string* hexPrivateKey) const { 144 | ERR_clear_error(); 145 | 146 | // The private key is stored as a BIGNUM object. 147 | BIGNUM* privateKey = nullptr; 148 | if (!EVP_PKEY_get_bn_param(keyPair_.get(), OSSL_PKEY_PARAM_PRIV_KEY, &privateKey)) { 149 | return {Result::Fail, 150 | "Could not get the private key: EVP_PKEY_get_bn_param failed: {}", 151 | OpenSSL::GetLastErrorString()}; 152 | } 153 | 154 | std::unique_ptr> privateKeyHolder{privateKey}; 155 | 156 | // Convert the BIGNUM to a hex string. 157 | *hexPrivateKey = OpenSSL::ConvertBigNumToHex(privateKey); 158 | if (hexPrivateKey->empty()) { 159 | return {Result::Fail, 160 | "Could not get the private key: ConvertBigNumToHex failed: {}", 161 | OpenSSL::GetLastErrorString()}; 162 | } 163 | 164 | return {Result::Success}; 165 | } 166 | 167 | Result ECDiffieHellmanOpenSSL::GetPublicKey(std::string* hexPublicKey) const { 168 | ERR_clear_error(); 169 | 170 | // The public key is stored as a byte array. 171 | // Get the array size. 172 | size_t keyLength = 0; 173 | if (!EVP_PKEY_get_octet_string_param(keyPair_.get(), OSSL_PKEY_PARAM_PUB_KEY, 174 | nullptr, 0, &keyLength)) { 175 | return {Result::Fail, 176 | "Couldn't get the public key length: EVP_PKEY_get_octet_string_param failed: {}", 177 | OpenSSL::GetLastErrorString()}; 178 | } 179 | 180 | // Get the key. 181 | auto publicKey = std::make_unique(keyLength); 182 | if (!EVP_PKEY_get_octet_string_param(keyPair_.get(), OSSL_PKEY_PARAM_PUB_KEY, 183 | publicKey.get(), keyLength, &keyLength)) { 184 | return {Result::Fail, 185 | "Couldn't get the public key: EVP_PKEY_get_octet_string_param failed: {}", 186 | OpenSSL::GetLastErrorString()}; 187 | } 188 | 189 | // Convert the byte array key to a hex string. 190 | *hexPublicKey = Base::ConvertDataToHex( 191 | std::string_view(static_cast( 192 | static_cast(publicKey.get())), keyLength)); 193 | return {Result::Success}; 194 | } 195 | 196 | Result ECDiffieHellmanOpenSSL::DeriveSharedSecret(std::string_view hexPeerPublicKey, 197 | std::string* hexSharedSecret) const { 198 | ERR_clear_error(); 199 | Result result; 200 | 201 | // First, you have to create the peer public key object. 202 | // It takes several calls, so it is done in a separate function. 203 | std::unique_ptr> peerPublicKey; 204 | result = CreatePeerPublicKey(hexPeerPublicKey, &peerPublicKey); 205 | if (!result) { 206 | return result; 207 | } 208 | 209 | // Create the derivation context. 210 | std::unique_ptr> 211 | derivationCtx{EVP_PKEY_CTX_new(keyPair_.get(), nullptr)}; 212 | if (!derivationCtx) { 213 | return {Result::Fail, 214 | "Couldn't derive the shared secret: EVP_PKEY_CTX_new failed: {}", 215 | OpenSSL::GetLastErrorString()}; 216 | } 217 | 218 | // Initialize the derivation context. 219 | if (EVP_PKEY_derive_init(derivationCtx.get()) <= 0) { 220 | return {Result::Fail, 221 | "Couldn't derive the shared secret: EVP_PKEY_derive_init failed: {}", 222 | OpenSSL::GetLastErrorString()}; 223 | } 224 | 225 | // Set the peer public key object. 226 | if (EVP_PKEY_derive_set_peer(derivationCtx.get(), peerPublicKey.get()) <= 0) { 227 | return {Result::Fail, 228 | "Couldn't derive the shared secret: EVP_PKEY_derive_set_peer failed: {}", 229 | OpenSSL::GetLastErrorString()}; 230 | } 231 | 232 | // Get the shared secret length. 233 | size_t sharedSecretLength = 0; 234 | if (EVP_PKEY_derive(derivationCtx.get(), nullptr, &sharedSecretLength) <= 0) { 235 | return {Result::Fail, 236 | "Couldn't derive the shared secret: EVP_PKEY_derive failed: {}", 237 | OpenSSL::GetLastErrorString()}; 238 | } 239 | 240 | if (sharedSecretLength == 0) { 241 | return {Result::Fail, 242 | "Couldn't derive the shared secret: zero length shared secret."}; 243 | } 244 | 245 | 246 | std::string sharedSecret; 247 | sharedSecret.resize(sharedSecretLength); 248 | 249 | // Derive the shared secret. 250 | if (EVP_PKEY_derive(derivationCtx.get(), 251 | static_cast(static_cast( 252 | &sharedSecret.front())), &sharedSecretLength) <= 0) { 253 | return {Result::Fail, 254 | "Couldn't derive the shared secret: EVP_PKEY_derive failed: {}", 255 | OpenSSL::GetLastErrorString()}; 256 | } 257 | 258 | // Convert to a hex string. 259 | *hexSharedSecret = Base::ConvertDataToHex(sharedSecret); 260 | 261 | return {Result::Success}; 262 | } 263 | 264 | Result ECDiffieHellmanOpenSSL::CreatePeerPublicKey(std::string_view hexPeerPublicKey, 265 | std::unique_ptr>* peerPublicKey) const { 266 | Result result; 267 | 268 | // First, we sould create an OSSL_PARAM_BLD with the curve name 269 | // and the raw peer public key. 270 | std::unique_ptr> 271 | paramBuild{OSSL_PARAM_BLD_new()}; 272 | if (!paramBuild) { 273 | return {Result::Fail, 274 | "Couldn't create the peer public key: OSSL_PARAM_BLD_new failed: {}", 275 | OpenSSL::GetLastErrorString()}; 276 | } 277 | 278 | // Set the curve name. 279 | if (!OSSL_PARAM_BLD_push_utf8_string(paramBuild.get(), OSSL_PKEY_PARAM_GROUP_NAME, 280 | curveName_.data(), 0)) { 281 | return {Result::Fail, 282 | "Couldn't create the peer public key: OSSL_PARAM_BLD_new failed: {}", 283 | OpenSSL::GetLastErrorString()}; 284 | } 285 | 286 | // Convert the peer hex public key to raw data. 287 | std::string binPeerPublicKey; 288 | result = Base::ConvertHexToData(hexPeerPublicKey, &binPeerPublicKey); 289 | if (!result) { 290 | return result; 291 | } 292 | 293 | // Set the raw peer public key. 294 | if (!OSSL_PARAM_BLD_push_octet_string(paramBuild.get(), OSSL_PKEY_PARAM_PUB_KEY, 295 | binPeerPublicKey.data(), binPeerPublicKey.size())) { 296 | return {Result::Fail, 297 | "Couldn't create the peer public key: OSSL_PARAM_BLD_push_octet_string failed: {}", 298 | OpenSSL::GetLastErrorString()}; 299 | } 300 | 301 | // Convert the OSSL_PARAM_BLD to OSSL_PARAM. 302 | std::unique_ptr> 303 | params{OSSL_PARAM_BLD_to_param(paramBuild.get())}; 304 | if (!params) { 305 | return {Result::Fail, 306 | "Couldn't create the peer public key: OSSL_PARAM_BLD_to_param failed: {}", 307 | OpenSSL::GetLastErrorString()}; 308 | } 309 | 310 | // Create a EVP_PKEY context. 311 | std::unique_ptr> 312 | peerPublicKeyCtx{EVP_PKEY_CTX_new_from_name(nullptr, "EC", nullptr)}; 313 | if (!peerPublicKeyCtx) { 314 | return {Result::Fail, 315 | "Couldn't create the peer public key: OSSL_PARAM_BLD_to_param failed: {}", 316 | OpenSSL::GetLastErrorString()}; 317 | } 318 | 319 | // Initialize the context. 320 | if (EVP_PKEY_fromdata_init(peerPublicKeyCtx.get()) <= 0) { 321 | return {Result::Fail, 322 | "Couldn't create the peer public key: EVP_PKEY_fromdata_init failed: {}", 323 | OpenSSL::GetLastErrorString()}; 324 | } 325 | 326 | // Create the peer public key object. 327 | EVP_PKEY* tmp = nullptr; 328 | if (EVP_PKEY_fromdata(peerPublicKeyCtx.get(), &tmp, EVP_PKEY_PUBLIC_KEY, params.get()) <= 0) { 329 | return {Result::Fail, 330 | "Couldn't create the peer public key: EVP_PKEY_fromdata failed: {}", 331 | OpenSSL::GetLastErrorString()}; 332 | } 333 | 334 | peerPublicKey->reset(tmp); 335 | 336 | return {Result::Success}; 337 | } 338 | -------------------------------------------------------------------------------- /src/ec-diffie-hellman-openssl.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include "base-helpers.h" 11 | #include "ec-diffie-hellman.h" 12 | 13 | #include 14 | 15 | // Elliptic-curve Diffie-Hellman implementation based on OpenSSL 3.x.x. 16 | class ECDiffieHellmanOpenSSL : public ECDiffieHellman { 17 | public: 18 | ECDiffieHellmanOpenSSL(); 19 | ~ECDiffieHellmanOpenSSL() override; 20 | 21 | std::string_view GetImplementationName() const override; 22 | 23 | Result GetSupportedCurves(std::map* curves) const override; 25 | 26 | Result SetCurveName(std::string_view curveName) override; 27 | Result GetCurveName(std::string* curveName) const override; 28 | 29 | Result GenerateKeys() override; 30 | Result GetPrivateKey(std::string* hexPrivateKey) const override; // for debug 31 | Result GetPublicKey(std::string* hexPublicKey) const override; 32 | 33 | Result DeriveSharedSecret(std::string_view hexPeerPublicKey, 34 | std::string* hexSharedSecret) const override; 35 | 36 | private: 37 | Result CreatePeerPublicKey(std::string_view hexPeerPublicKey, 38 | std::unique_ptr>* peerPublicKey) const; 39 | 40 | std::string curveName_; 41 | std::unique_ptr> keyPair_; 42 | }; 43 | -------------------------------------------------------------------------------- /src/ec-diffie-hellman.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | 6 | #if defined(USE_OPENSSL) 7 | #include "ec-diffie-hellman-openssl.h" 8 | #else 9 | #include "ec-diffie-hellman-libressl.h" 10 | #endif 11 | 12 | #include "ec-diffie-hellman.h" 13 | 14 | std::unique_ptr ECDiffieHellman::Create(Implementation impl) { 15 | switch (impl) { 16 | #if defined(USE_OPENSSL) 17 | case Implementation::OpenSSL: return std::make_unique(); 18 | #else 19 | case Implementation::LibreSSL: return std::make_unique(); 20 | #endif 21 | default: break; 22 | } 23 | return std::unique_ptr(); 24 | } 25 | -------------------------------------------------------------------------------- /src/ec-diffie-hellman.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "result.h" 13 | 14 | // The Elliptic-curve Diffie-Hellman interface. 15 | class ECDiffieHellman { 16 | public: 17 | 18 | enum class Implementation { 19 | Undefined = 0, 20 | OpenSSL, // Based on OpenSSL 3 21 | LibreSSL, // Based on LibreSSL 3 22 | }; 23 | 24 | struct CurveInfo final { 25 | int internalId_ = 0; 26 | std::string name_; 27 | std::string comment_; 28 | }; 29 | 30 | // Create an instance. 31 | static std::unique_ptr Create(Implementation impl); 32 | 33 | // Default constructor. 34 | ECDiffieHellman() = default; 35 | 36 | // For derived classes. 37 | virtual ~ECDiffieHellman() {} 38 | 39 | // Gets implementation name. 40 | virtual std::string_view GetImplementationName() const = 0; 41 | 42 | // Gets supported curves. 43 | // Performance is not important for this project, 44 | // so std::map is used to show a sorted list instead of std::unordered_map. 45 | virtual Result GetSupportedCurves(std::map* curves) const = 0; 47 | 48 | // Sets the curve name. 49 | virtual Result SetCurveName(std::string_view curveName) = 0; 50 | 51 | // Gets the curve name. 52 | virtual Result GetCurveName(std::string* curveName) const = 0; 53 | 54 | // Generate a private key and then derives a public key. 55 | virtual Result GenerateKeys() = 0; 56 | 57 | // Gets the private key (for debug) 58 | virtual Result GetPrivateKey(std::string* hexPrivateKey) const = 0; 59 | 60 | // Gets the public key. 61 | virtual Result GetPublicKey(std::string* hexPublicKey) const = 0; 62 | 63 | // Derives the shared secret based on this objects values and the peer public key. 64 | virtual Result DeriveSharedSecret(std::string_view hexPeerPublicKey, 65 | std::string* hexSharedSecret) const = 0; 66 | 67 | private: 68 | // Non-copyable. 69 | ECDiffieHellman(const ECDiffieHellman&) = delete; 70 | ECDiffieHellman& operator=(const ECDiffieHellman&) = delete; 71 | }; 72 | -------------------------------------------------------------------------------- /src/ecdh-tester.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "result.h" 11 | #include "base-helpers.h" 12 | #include "ec-diffie-hellman.h" 13 | 14 | #include "ecdh-tester.h" 15 | 16 | ECDHTester::ECDHTester(std::string_view curveName) 17 | : curveName_(curveName.data()) { 18 | } 19 | 20 | void ECDHTester::Run() { 21 | 22 | std::cout << "--------------------------------------------------------" << std::endl; 23 | std::cout << "CURVE NAME: " << curveName_ << std::endl; 24 | 25 | using Impl = ECDiffieHellman::Implementation; 26 | 27 | std::vector> tests = { 28 | {Impl::OpenSSL, Impl::OpenSSL}, 29 | {Impl::LibreSSL, Impl::LibreSSL}, 30 | }; 31 | 32 | for (const auto& test : tests) { 33 | auto alice = ECDiffieHellman::Create(test.first); 34 | auto bob = ECDiffieHellman::Create(test.second); 35 | if (alice && bob) { 36 | std::cout << "--------------------------------------------------------" << std::endl; 37 | 38 | std::string testDescription = std::format("ALICE {} <-> BOB {}", 39 | alice->GetImplementationName(), bob->GetImplementationName()); 40 | std::cout << testDescription << std::endl; 41 | 42 | auto result = DoTest(alice.get(), bob.get()); 43 | 44 | std::string resultDescription = (result) ? "success" : result.GetDescription(); 45 | std::cout << "RESULT: " << resultDescription << std::endl << std::endl; 46 | } 47 | } 48 | } 49 | 50 | Result ECDHTester::DoTest(ECDiffieHellman* alice, ECDiffieHellman* bob) { 51 | Result result; 52 | 53 | // Alice initialization. 54 | 55 | if (!(result = alice->SetCurveName(curveName_))) { 56 | return result; 57 | } 58 | 59 | if (!(result = alice->GenerateKeys())) { 60 | return result; 61 | } 62 | 63 | std::string aliceHexPublicKey; 64 | if (!(result = alice->GetPublicKey(&aliceHexPublicKey))) { 65 | return result; 66 | } 67 | 68 | std::cout << "ALICE public key length (hex digits): " << aliceHexPublicKey.size() << std::endl; 69 | std::cout << "ALICE public key: " << aliceHexPublicKey << std::endl; 70 | 71 | std::string aliceHexPrivateKey; 72 | if (!(result = alice->GetPrivateKey(&aliceHexPrivateKey))) { 73 | return result; 74 | } 75 | 76 | std::cout << "ALICE private key: " << aliceHexPrivateKey << std::endl; 77 | 78 | // Bob initialization. 79 | 80 | if (!(result = bob->SetCurveName(curveName_))) { 81 | return result; 82 | } 83 | 84 | if (!(result = bob->GenerateKeys())) { 85 | return result; 86 | } 87 | 88 | std::string bobHexPublicKey; 89 | if (!(result = bob->GetPublicKey(&bobHexPublicKey))) { 90 | return result; 91 | } 92 | 93 | std::cout << "BOB public key length (hex digits): " << bobHexPublicKey.size() << std::endl; 94 | std::cout << "BOB public key: " << bobHexPublicKey << std::endl; 95 | 96 | std::string bobHexPrivateKey; 97 | if (!(result = bob->GetPrivateKey(&bobHexPrivateKey))) { 98 | return result; 99 | } 100 | 101 | std::cout << "BOB private key: " << bobHexPrivateKey << std::endl; 102 | 103 | // Derivation 104 | 105 | std::string aliceHexSharedSecret; 106 | if (!(result = alice->DeriveSharedSecret(bobHexPublicKey, &aliceHexSharedSecret))) { 107 | return result; 108 | } 109 | 110 | std::cout << "ALICE shared secret: " << aliceHexSharedSecret << std::endl; 111 | 112 | std::string bobHexSharedSecret; 113 | if (!(result = bob->DeriveSharedSecret(aliceHexPublicKey, &bobHexSharedSecret))) { 114 | return result; 115 | } 116 | 117 | std::cout << "BOB shared secret: " << bobHexSharedSecret << std::endl; 118 | 119 | if (aliceHexSharedSecret != bobHexSharedSecret) { 120 | return {Result::Fail, "The shared secrets are different!"}; 121 | } 122 | 123 | return {Result::Success}; 124 | } 125 | 126 | void ECDHTester::ShowSupportedCurves() { 127 | std::cout << "--------------------------------------------------------" << std::endl; 128 | 129 | // The project does not have a custom ECDH implementation 130 | // to test vs OpenSSL or LibreSSL, so we just enumerate 131 | // supported curves of OpenSSL or LIbreSSL. 132 | #if defined(USE_OPENSSL) 133 | auto ecdh = ECDiffieHellman::Create(ECDiffieHellman::Implementation::OpenSSL); 134 | #else 135 | auto ecdh = ECDiffieHellman::Create(ECDiffieHellman::Implementation::LibreSSL); 136 | #endif 137 | std::map curves; 138 | Result result = ecdh->GetSupportedCurves(&curves); 139 | if (result) { 140 | PrintCurvesInfo(curves); 141 | } 142 | } 143 | 144 | void ECDHTester::PrintCurvesInfo(const std::map& curves) { 145 | for (const auto& [curveName, curveInfo] : curves) { 146 | PrintCurveInfo(curveInfo); 147 | } 148 | } 149 | 150 | void ECDHTester::PrintCurveInfo(const ECDiffieHellman::CurveInfo& curveInfo) { 151 | std::cout << std::left << std::setw(32) << curveInfo.name_ << curveInfo.comment_ << std::endl; 152 | } -------------------------------------------------------------------------------- /src/ecdh-tester.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include "ec-diffie-hellman.h" 10 | 11 | class ECDHTester final { 12 | public: 13 | // Construct an instance, sets the prime length and generator number. 14 | ECDHTester(std::string_view curveName); 15 | 16 | // Runs predefined tests. 17 | void Run(); 18 | 19 | // Show supported curves. 20 | static void ShowSupportedCurves(); 21 | 22 | private: 23 | 24 | Result DoTest(ECDiffieHellman* alice, ECDiffieHellman* bob); 25 | 26 | static void PrintCurvesInfo(const std::map& curves); 27 | static void PrintCurveInfo(const ECDiffieHellman::CurveInfo& curveInfo); 28 | 29 | std::string curveName_; 30 | }; -------------------------------------------------------------------------------- /src/libressl-helpers.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | 6 | #include 7 | #include 8 | 9 | #include "libressl-helpers.h" 10 | 11 | namespace LibreSSL { 12 | 13 | std::tuple GetLastError() { 14 | unsigned long lastError = ERR_peek_last_error(); 15 | if (lastError == 0) { 16 | return {0, ""}; 17 | } 18 | char errorString[256]; 19 | ERR_error_string_n(lastError, errorString, sizeof(errorString)); 20 | return {lastError, errorString}; 21 | } 22 | 23 | std::string GetLastErrorString() { 24 | return std::get<1>(GetLastError()); 25 | } 26 | 27 | BIGNUM* ConvertHexToBigNum(std::string_view hexBigNum) { 28 | BIGNUM* bn = nullptr; 29 | if (!BN_hex2bn(&bn, hexBigNum.data())) { 30 | return nullptr; 31 | } 32 | return bn; 33 | } 34 | 35 | std::string ConvertBigNumToHex(const BIGNUM* bigNum) { 36 | char* tmpHexBigNum = BN_bn2hex(bigNum); 37 | if (!tmpHexBigNum) { 38 | return std::string(); 39 | } 40 | std::string hexBigNum(tmpHexBigNum); 41 | OPENSSL_free(tmpHexBigNum); 42 | return hexBigNum; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/libressl-helpers.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | namespace LibreSSL { 14 | 15 | // BN_free 16 | // "If a is a NULL pointer, no action occurs." 17 | // https://man.openbsd.org/BN_new.3 18 | 19 | // DH_free 20 | // "If dh is a NULL pointer, no action occurs." 21 | // https://man.openbsd.org/DH_new.3 22 | 23 | 24 | // BM_CTX_free 25 | // "If c is a NULL pointer, no action occurs." 26 | // https://man.openbsd.org/BN_CTX_new.3 27 | 28 | // Gets current thread libressl last error (both numberic and text). 29 | std::tuple GetLastError(); 30 | // Gets current thread libressl last text error message. 31 | std::string GetLastErrorString(); 32 | 33 | // Converts a hex string to BIGNUM. 34 | // Returns nullptr if convertion fails. 35 | BIGNUM* ConvertHexToBigNum(std::string_view hexBigNum); 36 | 37 | // Converts BIGNUM to a hex string. 38 | // Returns an empty string if convertion fails. 39 | std::string ConvertBigNumToHex(const BIGNUM* bigNum); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | 8 | #include "dh-tester.h" 9 | #include "ecdh-tester.h" 10 | 11 | void ShowUsageHint() { 12 | static const int firstColumnWidth = 37; 13 | 14 | #if defined(USE_OPENSSL) 15 | std::cout << "Compiled with the OpenSSL support." << std::endl; 16 | #else 17 | std::cout << "Compiled with the LibreSSL support." << std::endl; 18 | #endif 19 | 20 | std::cout << "USAGE: diffie-hellman-cpp {dh [PRIME_LENGTH_IN_BITS] " 21 | "[GENERATOR] | ecdh [CURVE_NAME] | ecdh-curves}" << std::endl; 22 | std::cout << "EXAMPLES:" << std::endl; 23 | std::cout << std::left << std::setw(firstColumnWidth) << " diffie-hellman-cpp ecdh" 24 | << "ECDH key exchange with the secp384r1 curve." << std::endl; 25 | std::cout << std::left << std::setw(firstColumnWidth) << " diffie-hellman-cpp ecdh secp521r1" 26 | << "ECDH key exchange with the secp521r1 curve." << std::endl; 27 | std::cout << std::left << std::setw(firstColumnWidth) << " diffie-hellman-cpp ecdh-curves" 28 | << "Enumerates supported ECDH curves." << std::endl; 29 | std::cout << std::left << std::setw(firstColumnWidth) << " diffie-hellman-cpp dh" 30 | << "DH key exchange with a 512 bit prime; the generator is 2." << std::endl; 31 | std::cout << std::left << std::setw(firstColumnWidth) << " diffie-hellman-cpp dh 512 5" 32 | << "DH key exchange with a 512 bit prime; the generator is 5." << std::endl; 33 | std::cout << std::left << std::setw(firstColumnWidth) << " diffie-hellman-cpp dh 256" 34 | << "DH key exchange with a 256 bit prime (too small for OpenSSL!)." << std::endl; 35 | } 36 | 37 | int main(int argc, char* argv[]) { 38 | ShowUsageHint(); 39 | 40 | if (argc > 1) { 41 | if (std::string_view(argv[1]) == "ecdh-curves") { 42 | // Show supported ECDH curves. 43 | ECDHTester::ShowSupportedCurves(); 44 | } else if (std::string_view(argv[1]) == "ecdh") { 45 | // Test ECDH key exchange. 46 | std::string curveName{"secp384r1"}; 47 | if (argc > 2) { 48 | curveName = argv[2]; 49 | } 50 | 51 | ECDHTester::ECDHTester(curveName).Run(); 52 | 53 | } else if (std::string_view(argv[1]) == "dh") { 54 | // Test DH exchange. 55 | try { 56 | int primeLengthInBits = (argc > 2) ? std::stoi(argv[2]) : 512; 57 | int generator = (argc > 3) ? std::stoi(argv[3]) : 2; 58 | 59 | DHTester{primeLengthInBits, generator}.Run(); 60 | 61 | } catch (const std::exception& e) { 62 | std::cout << e.what() << std::endl; 63 | return 1; 64 | } 65 | 66 | } 67 | } 68 | 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /src/openssl-helpers.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | 6 | #include 7 | #include 8 | 9 | #include "openssl-helpers.h" 10 | 11 | namespace OpenSSL { 12 | 13 | std::tuple GetLastError() { 14 | unsigned long lastError = ERR_peek_last_error(); 15 | if (lastError == 0) { 16 | return {0, ""}; 17 | } 18 | char errorString[256]; 19 | ERR_error_string_n(lastError, errorString, sizeof(errorString)); 20 | return {lastError, errorString}; 21 | } 22 | 23 | std::string GetLastErrorString() { 24 | return std::get<1>(GetLastError()); 25 | } 26 | 27 | BIGNUM* ConvertHexToBigNum(std::string_view hexBigNum) { 28 | BIGNUM* bn = nullptr; 29 | if (!BN_hex2bn(&bn, hexBigNum.data())) { 30 | return nullptr; 31 | } 32 | return bn; 33 | } 34 | 35 | std::string ConvertBigNumToHex(const BIGNUM* bigNum) { 36 | char* tmpHexBigNum = BN_bn2hex(bigNum); 37 | if (!tmpHexBigNum) { 38 | return std::string(); 39 | } 40 | std::string hexBigNum(tmpHexBigNum); 41 | OPENSSL_free(tmpHexBigNum); 42 | return hexBigNum; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/openssl-helpers.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include "result.h" 14 | #include "base-helpers.h" 15 | 16 | namespace OpenSSL { 17 | 18 | // BN_free 19 | // "If a is NULL, nothing is done." 20 | // https://www.openssl.org/docs/man3.0/man3/BN_free.html 21 | 22 | // DH_free 23 | // "If dh is NULL nothing is done." 24 | // https://www.openssl.org/docs/man3.0/man3/DH_free.html 25 | 26 | // Gets current thread libressl last error (both numberic and text). 27 | std::tuple GetLastError(); 28 | // Gets current thread libressl last text error message. 29 | std::string GetLastErrorString(); 30 | 31 | // Converts a hex string to BIGNUM. 32 | BIGNUM* ConvertHexToBigNum(std::string_view hexBigNum); 33 | 34 | // Converts BIGNUM to a hex string. 35 | // Returns an empty string if convertion fails. 36 | std::string ConvertBigNumToHex(const BIGNUM* bigNum); 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/primes.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | 8 | static constexpr std::array Primes{ 9 | 2, 3, 5, 7, 11, 13, 17, 19, 10 | 23, 29, 31, 37, 41, 43, 47, 53, 11 | 59, 61, 67, 71, 73, 79, 83, 89, 12 | 97, 101, 103, 107, 109, 113, 127, 131, 13 | 137, 139, 149, 151, 157, 163, 167, 173, 14 | 179, 181, 191, 193, 197, 199, 211, 223, 15 | 227, 229, 233, 239, 241, 251, 257, 263, 16 | 269, 271, 277, 281, 283, 293, 307, 311, 17 | 313, 317, 331, 337, 347, 349, 353, 359, 18 | 367, 373, 379, 383, 389, 397, 401, 409, 19 | 419, 421, 431, 433, 439, 443, 449, 457, 20 | 461, 463, 467, 479, 487, 491, 499, 503, 21 | 509, 521, 523, 541, 547, 557, 563, 569, 22 | 571, 577, 587, 593, 599, 601, 607, 613, 23 | 617, 619, 631, 641, 643, 647, 653, 659, 24 | 661, 673, 677, 683, 691, 701, 709, 719, 25 | 727, 733, 739, 743, 751, 757, 761, 769, 26 | 773, 787, 797, 809, 811, 821, 823, 827, 27 | 829, 839, 853, 857, 859, 863, 877, 881, 28 | 883, 887, 907, 911, 919, 929, 937, 941, 29 | 947, 953, 967, 971, 977, 983, 991, 997, 30 | 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 31 | 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 32 | 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 33 | 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 34 | 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 35 | 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 36 | 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 37 | 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 38 | 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 39 | 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 40 | 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 41 | 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 42 | 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 43 | 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 44 | 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 45 | 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 46 | 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 47 | 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 48 | 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 49 | 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 50 | 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 51 | 2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 52 | 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 53 | 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 54 | 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 55 | 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 56 | 2591, 2593, 2609, 2617, 2621, 2633, 2647, 2657, 57 | 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 58 | 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 59 | 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 60 | 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 61 | 2879, 2887, 2897, 2903, 2909, 2917, 2927, 2939, 62 | 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 63 | 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, 64 | 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 65 | 3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, 66 | 3229, 3251, 3253, 3257, 3259, 3271, 3299, 3301, 67 | 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 68 | 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, 69 | 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 70 | 3499, 3511, 3517, 3527, 3529, 3533, 3539, 3541, 71 | 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, 72 | 3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671, 73 | 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 74 | 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 75 | 3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863, 76 | 3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 77 | 3929, 3931, 3943, 3947, 3967, 3989, 4001, 4003, 78 | 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, 79 | 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 80 | 4133, 4139, 4153, 4157, 4159, 4177, 4201, 4211, 81 | 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 82 | 4261, 4271, 4273, 4283, 4289, 4297, 4327, 4337, 83 | 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 84 | 4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 85 | 4483, 4493, 4507, 4513, 4517, 4519, 4523, 4547, 86 | 4549, 4561, 4567, 4583, 4591, 4597, 4603, 4621, 87 | 4637, 4639, 4643, 4649, 4651, 4657, 4663, 4673, 88 | 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, 89 | 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 90 | 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 91 | 4919, 4931, 4933, 4937, 4943, 4951, 4957, 4967, 92 | 4969, 4973, 4987, 4993, 4999, 5003, 5009, 5011, 93 | 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, 94 | 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 95 | 5171, 5179, 5189, 5197, 5209, 5227, 5231, 5233, 96 | 5237, 5261, 5273, 5279, 5281, 5297, 5303, 5309, 97 | 5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 98 | 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443, 99 | 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 100 | 5519, 5521, 5527, 5531, 5557, 5563, 5569, 5573, 101 | 5581, 5591, 5623, 5639, 5641, 5647, 5651, 5653, 102 | 5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711, 103 | 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791, 104 | 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 105 | 5851, 5857, 5861, 5867, 5869, 5879, 5881, 5897, 106 | 5903, 5923, 5927, 5939, 5953, 5981, 5987, 6007, 107 | 6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073, 108 | 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133, 109 | 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 110 | 6217, 6221, 6229, 6247, 6257, 6263, 6269, 6271, 111 | 6277, 6287, 6299, 6301, 6311, 6317, 6323, 6329, 112 | 6337, 6343, 6353, 6359, 6361, 6367, 6373, 6379, 113 | 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473, 114 | 6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 115 | 6569, 6571, 6577, 6581, 6599, 6607, 6619, 6637, 116 | 6653, 6659, 6661, 6673, 6679, 6689, 6691, 6701, 117 | 6703, 6709, 6719, 6733, 6737, 6761, 6763, 6779, 118 | 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, 119 | 6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 120 | 6911, 6917, 6947, 6949, 6959, 6961, 6967, 6971, 121 | 6977, 6983, 6991, 6997, 7001, 7013, 7019, 7027, 122 | 7039, 7043, 7057, 7069, 7079, 7103, 7109, 7121, 123 | 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 124 | 7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 125 | 7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349, 126 | 7351, 7369, 7393, 7411, 7417, 7433, 7451, 7457, 127 | 7459, 7477, 7481, 7487, 7489, 7499, 7507, 7517, 128 | 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, 129 | 7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 130 | 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 131 | 7699, 7703, 7717, 7723, 7727, 7741, 7753, 7757, 132 | 7759, 7789, 7793, 7817, 7823, 7829, 7841, 7853, 133 | 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919, 134 | 7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009, 135 | 8011, 8017, 8039, 8053, 8059, 8069, 8081, 8087, 136 | 8089, 8093, 8101, 8111, 8117, 8123, 8147, 8161, 137 | 8167, 8171, 8179, 8191, 8209, 8219, 8221, 8231, 138 | 8233, 8237, 8243, 8263, 8269, 8273, 8287, 8291, 139 | 8293, 8297, 8311, 8317, 8329, 8353, 8363, 8369, 140 | 8377, 8387, 8389, 8419, 8423, 8429, 8431, 8443, 141 | 8447, 8461, 8467, 8501, 8513, 8521, 8527, 8537, 142 | 8539, 8543, 8563, 8573, 8581, 8597, 8599, 8609, 143 | 8623, 8627, 8629, 8641, 8647, 8663, 8669, 8677, 144 | 8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731, 145 | 8737, 8741, 8747, 8753, 8761, 8779, 8783, 8803, 146 | 8807, 8819, 8821, 8831, 8837, 8839, 8849, 8861, 147 | 8863, 8867, 8887, 8893, 8923, 8929, 8933, 8941, 148 | 8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011, 149 | 9013, 9029, 9041, 9043, 9049, 9059, 9067, 9091, 150 | 9103, 9109, 9127, 9133, 9137, 9151, 9157, 9161, 151 | 9173, 9181, 9187, 9199, 9203, 9209, 9221, 9227, 152 | 9239, 9241, 9257, 9277, 9281, 9283, 9293, 9311, 153 | 9319, 9323, 9337, 9341, 9343, 9349, 9371, 9377, 154 | 9391, 9397, 9403, 9413, 9419, 9421, 9431, 9433, 155 | 9437, 9439, 9461, 9463, 9467, 9473, 9479, 9491, 156 | 9497, 9511, 9521, 9533, 9539, 9547, 9551, 9587, 157 | 9601, 9613, 9619, 9623, 9629, 9631, 9643, 9649, 158 | 9661, 9677, 9679, 9689, 9697, 9719, 9721, 9733, 159 | 9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791, 160 | 9803, 9811, 9817, 9829, 9833, 9839, 9851, 9857, 161 | 9859, 9871, 9883, 9887, 9901, 9907, 9923, 9929, 162 | 9931, 9941, 9949, 9967, 9973, 10007, 10009, 10037, 163 | 10039, 10061, 10067, 10069, 10079, 10091, 10093, 10099, 164 | 10103, 10111, 10133, 10139, 10141, 10151, 10159, 10163, 165 | 10169, 10177, 10181, 10193, 10211, 10223, 10243, 10247, 166 | 10253, 10259, 10267, 10271, 10273, 10289, 10301, 10303, 167 | 10313, 10321, 10331, 10333, 10337, 10343, 10357, 10369, 168 | 10391, 10399, 10427, 10429, 10433, 10453, 10457, 10459, 169 | 10463, 10477, 10487, 10499, 10501, 10513, 10529, 10531, 170 | 10559, 10567, 10589, 10597, 10601, 10607, 10613, 10627, 171 | 10631, 10639, 10651, 10657, 10663, 10667, 10687, 10691, 172 | 10709, 10711, 10723, 10729, 10733, 10739, 10753, 10771, 173 | 10781, 10789, 10799, 10831, 10837, 10847, 10853, 10859, 174 | 10861, 10867, 10883, 10889, 10891, 10903, 10909, 10937, 175 | 10939, 10949, 10957, 10973, 10979, 10987, 10993, 11003, 176 | 11027, 11047, 11057, 11059, 11069, 11071, 11083, 11087, 177 | 11093, 11113, 11117, 11119, 11131, 11149, 11159, 11161, 178 | 11171, 11173, 11177, 11197, 11213, 11239, 11243, 11251, 179 | 11257, 11261, 11273, 11279, 11287, 11299, 11311, 11317, 180 | 11321, 11329, 11351, 11353, 11369, 11383, 11393, 11399, 181 | 11411, 11423, 11437, 11443, 11447, 11467, 11471, 11483, 182 | 11489, 11491, 11497, 11503, 11519, 11527, 11549, 11551, 183 | 11579, 11587, 11593, 11597, 11617, 11621, 11633, 11657, 184 | 11677, 11681, 11689, 11699, 11701, 11717, 11719, 11731, 185 | 11743, 11777, 11779, 11783, 11789, 11801, 11807, 11813, 186 | 11821, 11827, 11831, 11833, 11839, 11863, 11867, 11887, 187 | 11897, 11903, 11909, 11923, 11927, 11933, 11939, 11941, 188 | 11953, 11959, 11969, 11971, 11981, 11987, 12007, 12011, 189 | 12037, 12041, 12043, 12049, 12071, 12073, 12097, 12101, 190 | 12107, 12109, 12113, 12119, 12143, 12149, 12157, 12161, 191 | 12163, 12197, 12203, 12211, 12227, 12239, 12241, 12251, 192 | 12253, 12263, 12269, 12277, 12281, 12289, 12301, 12323, 193 | 12329, 12343, 12347, 12373, 12377, 12379, 12391, 12401, 194 | 12409, 12413, 12421, 12433, 12437, 12451, 12457, 12473, 195 | 12479, 12487, 12491, 12497, 12503, 12511, 12517, 12527, 196 | 12539, 12541, 12547, 12553, 12569, 12577, 12583, 12589, 197 | 12601, 12611, 12613, 12619, 12637, 12641, 12647, 12653, 198 | 12659, 12671, 12689, 12697, 12703, 12713, 12721, 12739, 199 | 12743, 12757, 12763, 12781, 12791, 12799, 12809, 12821, 200 | 12823, 12829, 12841, 12853, 12889, 12893, 12899, 12907, 201 | 12911, 12917, 12919, 12923, 12941, 12953, 12959, 12967, 202 | 12973, 12979, 12983, 13001, 13003, 13007, 13009, 13033, 203 | 13037, 13043, 13049, 13063, 13093, 13099, 13103, 13109, 204 | 13121, 13127, 13147, 13151, 13159, 13163, 13171, 13177, 205 | 13183, 13187, 13217, 13219, 13229, 13241, 13249, 13259, 206 | 13267, 13291, 13297, 13309, 13313, 13327, 13331, 13337, 207 | 13339, 13367, 13381, 13397, 13399, 13411, 13417, 13421, 208 | 13441, 13451, 13457, 13463, 13469, 13477, 13487, 13499, 209 | 13513, 13523, 13537, 13553, 13567, 13577, 13591, 13597, 210 | 13613, 13619, 13627, 13633, 13649, 13669, 13679, 13681, 211 | 13687, 13691, 13693, 13697, 13709, 13711, 13721, 13723, 212 | 13729, 13751, 13757, 13759, 13763, 13781, 13789, 13799, 213 | 13807, 13829, 13831, 13841, 13859, 13873, 13877, 13879, 214 | 13883, 13901, 13903, 13907, 13913, 13921, 13931, 13933, 215 | 13963, 13967, 13997, 13999, 14009, 14011, 14029, 14033, 216 | 14051, 14057, 14071, 14081, 14083, 14087, 14107, 14143, 217 | 14149, 14153, 14159, 14173, 14177, 14197, 14207, 14221, 218 | 14243, 14249, 14251, 14281, 14293, 14303, 14321, 14323, 219 | 14327, 14341, 14347, 14369, 14387, 14389, 14401, 14407, 220 | 14411, 14419, 14423, 14431, 14437, 14447, 14449, 14461, 221 | 14479, 14489, 14503, 14519, 14533, 14537, 14543, 14549, 222 | 14551, 14557, 14561, 14563, 14591, 14593, 14621, 14627, 223 | 14629, 14633, 14639, 14653, 14657, 14669, 14683, 14699, 224 | 14713, 14717, 14723, 14731, 14737, 14741, 14747, 14753, 225 | 14759, 14767, 14771, 14779, 14783, 14797, 14813, 14821, 226 | 14827, 14831, 14843, 14851, 14867, 14869, 14879, 14887, 227 | 14891, 14897, 14923, 14929, 14939, 14947, 14951, 14957, 228 | 14969, 14983, 15013, 15017, 15031, 15053, 15061, 15073, 229 | 15077, 15083, 15091, 15101, 15107, 15121, 15131, 15137, 230 | 15139, 15149, 15161, 15173, 15187, 15193, 15199, 15217, 231 | 15227, 15233, 15241, 15259, 15263, 15269, 15271, 15277, 232 | 15287, 15289, 15299, 15307, 15313, 15319, 15329, 15331, 233 | 15349, 15359, 15361, 15373, 15377, 15383, 15391, 15401, 234 | 15413, 15427, 15439, 15443, 15451, 15461, 15467, 15473, 235 | 15493, 15497, 15511, 15527, 15541, 15551, 15559, 15569, 236 | 15581, 15583, 15601, 15607, 15619, 15629, 15641, 15643, 237 | 15647, 15649, 15661, 15667, 15671, 15679, 15683, 15727, 238 | 15731, 15733, 15737, 15739, 15749, 15761, 15767, 15773, 239 | 15787, 15791, 15797, 15803, 15809, 15817, 15823, 15859, 240 | 15877, 15881, 15887, 15889, 15901, 15907, 15913, 15919, 241 | 15923, 15937, 15959, 15971, 15973, 15991, 16001, 16007, 242 | 16033, 16057, 16061, 16063, 16067, 16069, 16073, 16087, 243 | 16091, 16097, 16103, 16111, 16127, 16139, 16141, 16183, 244 | 16187, 16189, 16193, 16217, 16223, 16229, 16231, 16249, 245 | 16253, 16267, 16273, 16301, 16319, 16333, 16339, 16349, 246 | 16361, 16363, 16369, 16381, 16411, 16417, 16421, 16427, 247 | 16433, 16447, 16451, 16453, 16477, 16481, 16487, 16493, 248 | 16519, 16529, 16547, 16553, 16561, 16567, 16573, 16603, 249 | 16607, 16619, 16631, 16633, 16649, 16651, 16657, 16661, 250 | 16673, 16691, 16693, 16699, 16703, 16729, 16741, 16747, 251 | 16759, 16763, 16787, 16811, 16823, 16829, 16831, 16843, 252 | 16871, 16879, 16883, 16889, 16901, 16903, 16921, 16927, 253 | 16931, 16937, 16943, 16963, 16979, 16981, 16987, 16993, 254 | 17011, 17021, 17027, 17029, 17033, 17041, 17047, 17053, 255 | 17077, 17093, 17099, 17107, 17117, 17123, 17137, 17159, 256 | 17167, 17183, 17189, 17191, 17203, 17207, 17209, 17231, 257 | 17239, 17257, 17291, 17293, 17299, 17317, 17321, 17327, 258 | 17333, 17341, 17351, 17359, 17377, 17383, 17387, 17389, 259 | 17393, 17401, 17417, 17419, 17431, 17443, 17449, 17467, 260 | 17471, 17477, 17483, 17489, 17491, 17497, 17509, 17519, 261 | 17539, 17551, 17569, 17573, 17579, 17581, 17597, 17599, 262 | 17609, 17623, 17627, 17657, 17659, 17669, 17681, 17683, 263 | 17707, 17713, 17729, 17737, 17747, 17749, 17761, 17783, 264 | 17789, 17791, 17807, 17827, 17837, 17839, 17851, 17863, 265 | }; 266 | -------------------------------------------------------------------------------- /src/result.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Eugen Hartmann. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | // Class to store function execution results. 13 | // Usually, you do not need to transfer a system error code to an upper layer. 14 | // So, the class does not have a property for an error code. 15 | // Instead, it has an enum, something like std::error_condition 16 | // to allow upper layers handling lower layer errors. 17 | // Also, it has an error description string to show it to a user or to debug. 18 | class Result final { 19 | public: 20 | // The condition is just an abstract thing 21 | // to allow upper layers to handle errors without 22 | // knowing actual lower layer error codes. 23 | enum class Condition { 24 | Success = 0, 25 | Undefined, 26 | Fail, 27 | //FileOpenFailed 28 | //InvalidPassword, 29 | //NeedPassword, 30 | //... 31 | }; 32 | 33 | using enum Condition; 34 | 35 | Result() 36 | : condition_(Undefined) {} 37 | 38 | Result(Condition condition) 39 | : condition_(condition) {} 40 | 41 | Result(Condition condition, std::string_view description) 42 | : condition_(condition) 43 | , description_(description) { 44 | } 45 | 46 | template 47 | Result(Condition condition, std::string_view tpl, Args&&... args) 48 | : condition_(condition) 49 | , description_(std::vformat(tpl, std::make_format_args(args...))) { 50 | } 51 | 52 | // Copy constructor. 53 | Result& operator=(const Result& other) { 54 | condition_ = other.condition_; 55 | description_ = other.description_; 56 | return *this; 57 | } 58 | 59 | // Success means true! 60 | // I really do not like std::error_code::operator bool() 61 | // because it check if the value is non-zero 62 | // which means it contains an error. 63 | inline operator bool() const { 64 | return condition_ == Condition::Success; 65 | } 66 | 67 | // Comparison methods. 68 | bool operator==(Condition condition) const { 69 | return condition_ == condition; 70 | } 71 | bool operator!=(Condition condition) const { 72 | return condition_ != condition; 73 | } 74 | 75 | // Gets the description. 76 | inline const std::string& GetDescription() const { 77 | return description_; 78 | } 79 | 80 | private: 81 | Condition condition_; 82 | std::string description_; 83 | }; 84 | 85 | 86 | --------------------------------------------------------------------------------