Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
This commit is contained in:
161
tools/EVerest-main/lib/everest/tls/tests/CMakeLists.txt
Normal file
161
tools/EVerest-main/lib/everest/tls/tests/CMakeLists.txt
Normal file
@@ -0,0 +1,161 @@
|
||||
find_package(OpenSSL 3)
|
||||
|
||||
set(TLS_TEST_FILES
|
||||
alt_openssl-pki.conf
|
||||
iso_pkey.asn1
|
||||
openssl-pki.conf
|
||||
ocsp_response.der
|
||||
pki.sh
|
||||
pki-tpm.sh
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${TLS_TEST_FILES}
|
||||
COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/pki
|
||||
COMMAND cd pki && cp ${TLS_TEST_FILES} ${CMAKE_CURRENT_BINARY_DIR}/
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
add_custom_target(tls_test_files_target
|
||||
DEPENDS ${TLS_TEST_FILES}
|
||||
)
|
||||
|
||||
set(TLS_GTEST_NAME tls_test)
|
||||
add_executable(${TLS_GTEST_NAME})
|
||||
add_dependencies(${TLS_GTEST_NAME} tls_test_files_target)
|
||||
|
||||
target_include_directories(${TLS_GTEST_NAME} PRIVATE
|
||||
..
|
||||
../include
|
||||
)
|
||||
|
||||
target_compile_definitions(${TLS_GTEST_NAME} PRIVATE
|
||||
-DUNIT_TEST
|
||||
-DLIBEVSE_CRYPTO_SUPPLIER_OPENSSL
|
||||
)
|
||||
|
||||
target_sources(${TLS_GTEST_NAME} PRIVATE
|
||||
gtest_main.cpp
|
||||
crypto_test.cpp
|
||||
openssl_util_test.cpp
|
||||
tls_test.cpp
|
||||
tls_connection_test.cpp
|
||||
../extensions/helpers.cpp
|
||||
../extensions/status_request.cpp
|
||||
../extensions/trusted_ca_keys.cpp
|
||||
../src/openssl_conv.cpp
|
||||
../src/openssl_util.cpp
|
||||
../src/tls.cpp
|
||||
)
|
||||
|
||||
if(USING_TPM2)
|
||||
target_sources(${TLS_GTEST_NAME} PRIVATE
|
||||
tls_connection_test_tpm.cpp
|
||||
)
|
||||
target_compile_definitions(${TLS_GTEST_NAME} PRIVATE
|
||||
USING_TPM2
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${TLS_GTEST_NAME}
|
||||
PRIVATE
|
||||
GTest::gtest
|
||||
OpenSSL::SSL
|
||||
OpenSSL::Crypto
|
||||
everest::evse_security
|
||||
everest::util
|
||||
)
|
||||
|
||||
set(TLS_MAIN_NAME tls_server)
|
||||
add_executable(${TLS_MAIN_NAME})
|
||||
add_dependencies(${TLS_MAIN_NAME} tls_test_files_target)
|
||||
|
||||
target_include_directories(${TLS_MAIN_NAME} PRIVATE
|
||||
..
|
||||
../include
|
||||
)
|
||||
|
||||
target_compile_definitions(${TLS_MAIN_NAME} PRIVATE
|
||||
-DUNIT_TEST
|
||||
)
|
||||
|
||||
target_sources(${TLS_MAIN_NAME} PRIVATE
|
||||
tls_main.cpp
|
||||
../extensions/helpers.cpp
|
||||
../extensions/status_request.cpp
|
||||
../extensions/trusted_ca_keys.cpp
|
||||
../src/openssl_util.cpp
|
||||
../src/tls.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(${TLS_MAIN_NAME}
|
||||
PRIVATE
|
||||
OpenSSL::SSL
|
||||
OpenSSL::Crypto
|
||||
everest::evse_security
|
||||
everest::util
|
||||
)
|
||||
|
||||
set(TLS_CLIENT_NAME tls_client)
|
||||
add_executable(${TLS_CLIENT_NAME})
|
||||
add_dependencies(${TLS_CLIENT_NAME} tls_test_files_target)
|
||||
|
||||
target_include_directories(${TLS_CLIENT_NAME} PRIVATE
|
||||
..
|
||||
../include
|
||||
)
|
||||
|
||||
target_compile_definitions(${TLS_CLIENT_NAME} PRIVATE
|
||||
-DUNIT_TEST
|
||||
)
|
||||
|
||||
target_sources(${TLS_CLIENT_NAME} PRIVATE
|
||||
tls_client_main.cpp
|
||||
../extensions/helpers.cpp
|
||||
../extensions/status_request.cpp
|
||||
../extensions/trusted_ca_keys.cpp
|
||||
../src/openssl_util.cpp
|
||||
../src/tls.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(${TLS_CLIENT_NAME}
|
||||
PRIVATE
|
||||
OpenSSL::SSL
|
||||
OpenSSL::Crypto
|
||||
everest::evse_security
|
||||
everest::util
|
||||
)
|
||||
|
||||
set(TLS_PATCH_NAME patched_test)
|
||||
add_executable(${TLS_PATCH_NAME})
|
||||
add_dependencies(${TLS_PATCH_NAME} tls_test_files_target)
|
||||
|
||||
target_include_directories(${TLS_PATCH_NAME} PRIVATE
|
||||
..
|
||||
../include
|
||||
)
|
||||
|
||||
target_compile_definitions(${TLS_PATCH_NAME} PRIVATE
|
||||
-DUNIT_TEST
|
||||
)
|
||||
|
||||
target_sources(${TLS_PATCH_NAME} PRIVATE
|
||||
patched_test.cpp
|
||||
../extensions/helpers.cpp
|
||||
../extensions/status_request.cpp
|
||||
../extensions/trusted_ca_keys.cpp
|
||||
../src/openssl_util.cpp
|
||||
../src/tls.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(${TLS_PATCH_NAME}
|
||||
PRIVATE
|
||||
GTest::gtest_main
|
||||
OpenSSL::SSL
|
||||
OpenSSL::Crypto
|
||||
everest::evse_security
|
||||
everest::util
|
||||
)
|
||||
|
||||
add_test(${TLS_GTEST_NAME} ${TLS_GTEST_NAME})
|
||||
ev_register_test_target(${TLS_GTEST_NAME})
|
||||
42
tools/EVerest-main/lib/everest/tls/tests/README.md
Normal file
42
tools/EVerest-main/lib/everest/tls/tests/README.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Tests
|
||||
|
||||
Building tests:
|
||||
|
||||
```sh
|
||||
$ cd EVerest
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake -GNinja -DEVEREST_CORE_BUILD_TESTING=ON ..
|
||||
$ ninja install
|
||||
```
|
||||
|
||||
`touch release.json` may be needed if it hasn't been created
|
||||
(then re-run `ninja install`).
|
||||
|
||||
## Unit tests
|
||||
|
||||
- `./tls_test` and `./patched_test`
|
||||
- automatically runs `pki.sh`
|
||||
- run from the directory containing the executable
|
||||
|
||||
## Standalone server
|
||||
|
||||
- Run `pki.sh` to build the test certificates and keys
|
||||
- use openssl_s_client to make test connections
|
||||
- run from the directory containing the executable
|
||||
|
||||
### Standalone TLS server
|
||||
|
||||
Tests the Server class in isolation.
|
||||
|
||||
- `./tls_server`
|
||||
- connects to IPv4 and IPv6
|
||||
- only one connection at a time
|
||||
- gracefully terminates after 30 seconds
|
||||
- `valgrind` can be used to check memory allocations (should be none)
|
||||
- requires client certificate and supports `status_request` extension
|
||||
- s_client echos back what is typed
|
||||
|
||||
```sh
|
||||
openssl s_client -connect localhost:8444 -verify 2 -CAfile server_root_cert.pem -cert client_cert.pem -cert_chain client_chain.pem -key client_priv.pem -verify_return_error -verify_hostname evse.pionix.de -status
|
||||
```
|
||||
41
tools/EVerest-main/lib/everest/tls/tests/crypto_test.cpp
Normal file
41
tools/EVerest-main/lib/everest/tls/tests/crypto_test.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2024 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <everest/tls/openssl_conv.hpp>
|
||||
#include <everest/tls/openssl_util.hpp>
|
||||
|
||||
#include <evse_security/certificate/x509_hierarchy.hpp>
|
||||
#include <evse_security/certificate/x509_wrapper.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
using evse_security::HashAlgorithm;
|
||||
using evse_security::X509Wrapper;
|
||||
using openssl::load_certificates;
|
||||
using openssl::conversions::to_X509Wrapper;
|
||||
|
||||
TEST(evseSecurity, certificateHash) {
|
||||
auto chain = load_certificates("client_chain.pem");
|
||||
ASSERT_GT(chain.size(), 0);
|
||||
|
||||
std::vector<X509Wrapper> certs;
|
||||
|
||||
for (const auto& cert : chain) {
|
||||
certs.push_back(to_X509Wrapper(cert.get()));
|
||||
}
|
||||
|
||||
for (std::uint8_t i = 0; i < certs.size() - 1; i++) {
|
||||
SCOPED_TRACE("i=" + std::to_string(i));
|
||||
const auto& cert = certs[i];
|
||||
const auto& issuer = certs[i + 1];
|
||||
const auto resA = cert.get_certificate_hash_data(issuer);
|
||||
EXPECT_EQ(resA.hash_algorithm, HashAlgorithm::SHA256);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
60
tools/EVerest-main/lib/everest/tls/tests/gtest_main.cpp
Normal file
60
tools/EVerest-main/lib/everest/tls/tests/gtest_main.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2024 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <linux/limits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <everest/tls/openssl_util.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
void log_handler(openssl::log_level_t level, const std::string& str) {
|
||||
switch (level) {
|
||||
case openssl::log_level_t::debug:
|
||||
// std::cout << "DEBUG: " << str << std::endl;
|
||||
break;
|
||||
case openssl::log_level_t::info:
|
||||
std::cout << "INFO: " << str << std::endl;
|
||||
break;
|
||||
case openssl::log_level_t::warning:
|
||||
std::cout << "WARN: " << str << std::endl;
|
||||
break;
|
||||
case openssl::log_level_t::error:
|
||||
std::cerr << "ERROR: " << str << std::endl;
|
||||
break;
|
||||
default:
|
||||
std::cerr << "Unknown: " << str << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// create test certificates and keys
|
||||
openssl::set_log_handler(log_handler);
|
||||
if (std::system("./pki.sh") != 0) {
|
||||
std::cerr << "Problem creating test certificates and keys" << std::endl;
|
||||
char buf[PATH_MAX];
|
||||
if (getcwd(&buf[0], sizeof(buf)) != nullptr) {
|
||||
std::cerr << "./pki.sh not found in " << buf << std::endl;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#ifdef USING_TPM2
|
||||
if (std::system("./pki-tpm.sh") != 0) {
|
||||
std::cerr << "Problem creating TPM test certificates and keys" << std::endl;
|
||||
char buf[PATH_MAX];
|
||||
if (getcwd(&buf[0], sizeof(buf)) != nullptr) {
|
||||
std::cerr << "./pki-tpm.sh not found in " << buf << std::endl;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
817
tools/EVerest-main/lib/everest/tls/tests/openssl_util_test.cpp
Normal file
817
tools/EVerest-main/lib/everest/tls/tests/openssl_util_test.cpp
Normal file
@@ -0,0 +1,817 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2024 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <everest/tls/openssl_util.hpp>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <vector>
|
||||
|
||||
#include <evse_security/crypto/openssl/openssl_provider.hpp>
|
||||
|
||||
bool operator==(const ::openssl::certificate_ptr& lhs, const ::openssl::certificate_ptr& rhs) {
|
||||
using ::openssl::certificate_to_pem;
|
||||
if (lhs && rhs) {
|
||||
const auto res_lhs = certificate_to_pem(lhs.get());
|
||||
const auto res_rhs = certificate_to_pem(rhs.get());
|
||||
return res_lhs == res_rhs;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T> constexpr void setCharacters(T& dest, const std::string& s) {
|
||||
dest.charactersLen = s.size();
|
||||
std::memcpy(&dest.characters[0], s.c_str(), s.size());
|
||||
}
|
||||
|
||||
template <typename T> constexpr void setBytes(T& dest, const std::uint8_t* b, std::size_t len) {
|
||||
dest.bytesLen = len;
|
||||
std::memcpy(&dest.bytes[0], b, len);
|
||||
}
|
||||
|
||||
struct test_vectors_t {
|
||||
const char* input;
|
||||
const std::uint8_t digest[32];
|
||||
};
|
||||
|
||||
constexpr std::uint8_t sign_test[] = {0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
|
||||
0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55};
|
||||
|
||||
constexpr test_vectors_t sha_256_test[] = {
|
||||
{"", {0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
|
||||
0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}},
|
||||
{"abc", {0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
|
||||
0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}}};
|
||||
|
||||
// EXI AuthorizationReq: checked okay (hash computes correctly)
|
||||
constexpr std::uint8_t iso_exi_a[] = {0x80, 0x04, 0x01, 0x52, 0x51, 0x0c, 0x40, 0x82, 0x9b, 0x7b, 0x6b, 0x29, 0x02,
|
||||
0x93, 0x0b, 0x73, 0x23, 0x7b, 0x69, 0x02, 0x23, 0x0b, 0xa3, 0x09, 0xe8};
|
||||
|
||||
// checked okay
|
||||
constexpr std::uint8_t iso_exi_a_hash[] = {0xd1, 0xb5, 0xe0, 0x3d, 0x00, 0x65, 0xbe, 0xe5, 0x6b, 0x31, 0x79,
|
||||
0x84, 0x45, 0x30, 0x51, 0xeb, 0x54, 0xca, 0x18, 0xfc, 0x0e, 0x09,
|
||||
0x16, 0x17, 0x4f, 0x8b, 0x3c, 0x77, 0xa9, 0x8f, 0x4a, 0xa9};
|
||||
|
||||
// EXI AuthorizationReq signature block: checked okay (hash computes correctly)
|
||||
constexpr std::uint8_t iso_exi_b[] = {
|
||||
0x80, 0x81, 0x12, 0xb4, 0x3a, 0x3a, 0x38, 0x1d, 0x17, 0x97, 0xbb, 0xbb, 0xbb, 0x97, 0x3b, 0x99, 0x97, 0x37, 0xb9,
|
||||
0x33, 0x97, 0xaa, 0x29, 0x17, 0xb1, 0xb0, 0xb7, 0x37, 0xb7, 0x34, 0xb1, 0xb0, 0xb6, 0x16, 0xb2, 0xbc, 0x34, 0x97,
|
||||
0xa1, 0xab, 0x43, 0xa3, 0xa3, 0x81, 0xd1, 0x79, 0x7b, 0xbb, 0xbb, 0xb9, 0x73, 0xb9, 0x99, 0x73, 0x7b, 0x93, 0x39,
|
||||
0x79, 0x91, 0x81, 0x81, 0x89, 0x79, 0x81, 0xa1, 0x7b, 0xc3, 0x6b, 0x63, 0x23, 0x9b, 0x4b, 0x39, 0x6b, 0x6b, 0x7b,
|
||||
0x93, 0x29, 0x1b, 0x2b, 0x1b, 0x23, 0x9b, 0x09, 0x6b, 0x9b, 0x43, 0x09, 0x91, 0xa9, 0xb2, 0x20, 0x62, 0x34, 0x94,
|
||||
0x43, 0x10, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72,
|
||||
0x67, 0x2f, 0x54, 0x52, 0x2f, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x2d, 0x65, 0x78, 0x69, 0x2f,
|
||||
0x48, 0x52, 0xd0, 0xe8, 0xe8, 0xe0, 0x74, 0x5e, 0x5e, 0xee, 0xee, 0xee, 0x5c, 0xee, 0x66, 0x5c, 0xde, 0xe4, 0xce,
|
||||
0x5e, 0x64, 0x60, 0x60, 0x62, 0x5e, 0x60, 0x68, 0x5e, 0xf0, 0xda, 0xd8, 0xca, 0xdc, 0xc6, 0x46, 0xe6, 0xd0, 0xc2,
|
||||
0x64, 0x6a, 0x6c, 0x84, 0x1a, 0x36, 0xbc, 0x07, 0xa0, 0x0c, 0xb7, 0xdc, 0xad, 0x66, 0x2f, 0x30, 0x88, 0xa6, 0x0a,
|
||||
0x3d, 0x6a, 0x99, 0x43, 0x1f, 0x81, 0xc1, 0x22, 0xc2, 0xe9, 0xf1, 0x67, 0x8e, 0xf5, 0x31, 0xe9, 0x55, 0x23, 0x70};
|
||||
|
||||
// checked okay
|
||||
constexpr std::uint8_t iso_exi_b_hash[] = {0xa4, 0xe9, 0x03, 0xe1, 0x82, 0x43, 0x04, 0x1b, 0x55, 0x4e, 0x11,
|
||||
0x64, 0x7e, 0x10, 0x1e, 0xd2, 0x5f, 0xc9, 0xf2, 0x15, 0x2a, 0xf4,
|
||||
0x67, 0x40, 0x14, 0xfe, 0x2a, 0xde, 0xac, 0x1e, 0x1c, 0xf7};
|
||||
|
||||
// checked okay (verifies iso_exi_b_hash with iso_priv.pem)
|
||||
constexpr std::uint8_t iso_exi_sig[] = {0x4c, 0x8f, 0x20, 0xc1, 0x40, 0x0b, 0xa6, 0x76, 0x06, 0xaa, 0x48, 0x11, 0x57,
|
||||
0x2a, 0x2f, 0x1a, 0xd3, 0xc1, 0x50, 0x89, 0xd9, 0x54, 0x20, 0x36, 0x34, 0x30,
|
||||
0xbb, 0x26, 0xb4, 0x9d, 0xb1, 0x04, 0xf0, 0x8d, 0xfa, 0x8b, 0xf8, 0x05, 0x5e,
|
||||
0x63, 0xa4, 0xb7, 0x5a, 0x8d, 0x31, 0x69, 0x20, 0x6f, 0xa8, 0xd5, 0x43, 0x08,
|
||||
0xba, 0x58, 0xf0, 0x56, 0x6b, 0x96, 0xba, 0xf6, 0x92, 0xce, 0x59, 0x50};
|
||||
|
||||
const char iso_exi_a_hash_b64[] = "0bXgPQBlvuVrMXmERTBR61TKGPwOCRYXT4s8d6mPSqk=";
|
||||
const char iso_exi_a_hash_b64_nl[] = "0bXgPQBlvuVrMXmERTBR61TKGPwOCRYXT4s8d6mPSqk=\n";
|
||||
|
||||
const char iso_exi_a_hash_b64_crlf[] = "0bXgPQBlvuVrMXmERTBR61TKGPwOCRYXT4s8d6mPSqk=\r\n";
|
||||
const char iso_exi_a_hash_b64_cr[] = "0bXgPQBlvuVrMXmERTBR61TKGPwOCRYXT4s8d6mPSqk=\r";
|
||||
const char iso_exi_a_hash_b64_spaces[] = "0bXgPQBlvuVr MXmERTBR61TK GPwOCRYXT4s8d6mPSqk=";
|
||||
const char iso_exi_a_hash_b64_tabs[] = "0bXgPQBlvuVr\tMXmERTBR61TKGPwOCRYXT4s8d6mPSqk=";
|
||||
|
||||
const char iso_exi_sig_b64[] =
|
||||
"TI8gwUALpnYGqkgRVyovGtPBUInZVCA2NDC7JrSdsQTwjfqL+AVeY6S3Wo0xaSBvqNVDCLpY8FZrlrr2ks5ZUA==";
|
||||
const char iso_exi_sig_b64_nl[] =
|
||||
"TI8gwUALpnYGqkgRVyovGtPBUInZVCA2NDC7JrSdsQTwjfqL+AVeY6S3Wo0xaSBv\nqNVDCLpY8FZrlrr2ks5ZUA==\n";
|
||||
|
||||
const char test_cert_pem[] = "-----BEGIN CERTIFICATE-----\n"
|
||||
"MIICBDCCAaqgAwIBAgIUQnMkyWtvc/a5OG8dZr9ziA5uQqYwCgYIKoZIzj0EAwIw\n"
|
||||
"TjELMAkGA1UEBhMCR0IxDzANBgNVBAcMBkxvbmRvbjEPMA0GA1UECgwGUGlvbml4\n"
|
||||
"MR0wGwYDVQQDDBRDUyBSb290IFRydXN0IEFuY2hvcjAeFw0yNDA5MTkxMzQwMDBa\n"
|
||||
"Fw0yNDEwMjExMzQwMDBaME4xCzAJBgNVBAYTAkdCMQ8wDQYDVQQHDAZMb25kb24x\n"
|
||||
"DzANBgNVBAoMBlBpb25peDEdMBsGA1UEAwwUQ1MgUm9vdCBUcnVzdCBBbmNob3Iw\n"
|
||||
"WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARLkawitst5NtPoYGpDCp8/GBTDrNRJ\n"
|
||||
"pCzS3KHT2lZJDOwzegRn+Zhs0csqXIQgbkCqdSozg+d83QNKcpmJk4FYo2YwZDAO\n"
|
||||
"BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFB6Ytfi9uSF7NSYGXmyZEcKsWHwJMB8G\n"
|
||||
"A1UdIwQYMBaAFB6Ytfi9uSF7NSYGXmyZEcKsWHwJMBIGA1UdEwEB/wQIMAYBAf8C\n"
|
||||
"AQIwCgYIKoZIzj0EAwIDSAAwRQIge4+uxc2EFYD7AkHR+9d/NbULUnKFIBRLqYE+\n"
|
||||
"Ib4h2CMCIQCtFWyvxwOUNidUTZGqyZXFmDyutJiNM0mi1iuFk8/8Mw==\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
const char test_cert_hash[] = "082f891b26de97c8bdedb159f8d59113cfb55dc0";
|
||||
const char test_cert_key_hash[] = "3b094e5f2594a3ae4511a9ff4285acd91fcd11c0";
|
||||
|
||||
inline const auto to_hex_string(const openssl::sha_1_digest_t& b) {
|
||||
std::stringstream string_stream;
|
||||
string_stream << std::hex;
|
||||
|
||||
for (int idx = 0; idx < sizeof(b); ++idx)
|
||||
string_stream << std::setw(2) << std::setfill('0') << (int)b[idx];
|
||||
|
||||
return string_stream.str();
|
||||
}
|
||||
|
||||
TEST(util, removeHyphen) {
|
||||
const std::string expected{"UKSWI123456791A"};
|
||||
std::string cert_emaid{"UKSWI123456791A"};
|
||||
|
||||
EXPECT_EQ(cert_emaid, expected);
|
||||
cert_emaid.erase(std::remove(cert_emaid.begin(), cert_emaid.end(), '-'), cert_emaid.end());
|
||||
EXPECT_EQ(cert_emaid, expected);
|
||||
|
||||
cert_emaid = std::string{"-UKSWI-123456791-A-"};
|
||||
cert_emaid.erase(std::remove(cert_emaid.begin(), cert_emaid.end(), '-'), cert_emaid.end());
|
||||
EXPECT_EQ(cert_emaid, expected);
|
||||
}
|
||||
|
||||
TEST(certificate_sha_1, hash) {
|
||||
auto cert = openssl::pem_to_certificate(test_cert_pem);
|
||||
EXPECT_TRUE(cert);
|
||||
openssl::sha_1_digest_t digest;
|
||||
auto res = openssl::certificate_sha_1(digest, cert.get());
|
||||
EXPECT_TRUE(res);
|
||||
EXPECT_EQ(to_hex_string(digest), test_cert_hash);
|
||||
}
|
||||
|
||||
TEST(certificate_subject_public_key_sha_1, hash) {
|
||||
auto cert = openssl::pem_to_certificate(test_cert_pem);
|
||||
EXPECT_TRUE(cert);
|
||||
openssl::sha_1_digest_t digest;
|
||||
auto res = openssl::certificate_subject_public_key_sha_1(digest, cert.get());
|
||||
EXPECT_TRUE(res);
|
||||
EXPECT_EQ(to_hex_string(digest), test_cert_key_hash);
|
||||
}
|
||||
|
||||
TEST(DER, equal) {
|
||||
const std::uint8_t data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
openssl::DER a;
|
||||
openssl::DER b(sizeof(data));
|
||||
openssl::DER c1(&data[0], sizeof(data));
|
||||
openssl::DER c2(&data[0], sizeof(data));
|
||||
openssl::DER d(&data[0], sizeof(data) - 1);
|
||||
|
||||
EXPECT_FALSE(a);
|
||||
EXPECT_TRUE(b);
|
||||
EXPECT_TRUE(c1);
|
||||
EXPECT_TRUE(c2);
|
||||
EXPECT_TRUE(d);
|
||||
|
||||
EXPECT_EQ(a, nullptr);
|
||||
EXPECT_NE(b, nullptr);
|
||||
EXPECT_NE(c1, nullptr);
|
||||
EXPECT_NE(c2, nullptr);
|
||||
EXPECT_NE(d, nullptr);
|
||||
|
||||
EXPECT_NE(c1.get(), c2.get());
|
||||
EXPECT_EQ(c1.size(), c2.size());
|
||||
EXPECT_EQ(c1, c2);
|
||||
EXPECT_EQ(c1, c1);
|
||||
|
||||
EXPECT_NE(c1, a);
|
||||
EXPECT_NE(c1, b);
|
||||
EXPECT_NE(c1, d);
|
||||
EXPECT_NE(a, c1);
|
||||
EXPECT_NE(b, c1);
|
||||
EXPECT_NE(d, c1);
|
||||
}
|
||||
|
||||
TEST(DER, construct) {
|
||||
const std::uint8_t data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
const openssl::DER a(&data[0], sizeof(data));
|
||||
|
||||
auto b = a;
|
||||
EXPECT_NE(a.get(), b.get());
|
||||
EXPECT_EQ(a, b);
|
||||
|
||||
auto c{a};
|
||||
EXPECT_NE(a.get(), c.get());
|
||||
EXPECT_EQ(a, c);
|
||||
EXPECT_EQ(b, c);
|
||||
|
||||
auto d = std::move(b);
|
||||
EXPECT_EQ(a, d);
|
||||
EXPECT_EQ(b.size(), 0);
|
||||
EXPECT_EQ(b, nullptr);
|
||||
EXPECT_FALSE(b);
|
||||
|
||||
auto e(std::move(c));
|
||||
EXPECT_EQ(a, e);
|
||||
EXPECT_EQ(c.size(), 0);
|
||||
EXPECT_EQ(c, nullptr);
|
||||
EXPECT_FALSE(c);
|
||||
|
||||
const std::uint8_t alt[] = {9, 8, 7, 6, 5, 4};
|
||||
openssl::DER x(&alt[0], sizeof(alt));
|
||||
x = e;
|
||||
EXPECT_EQ(x, a);
|
||||
EXPECT_NE(x, a.get());
|
||||
EXPECT_NE(x, e.get());
|
||||
}
|
||||
|
||||
TEST(DER, release) {
|
||||
const std::uint8_t data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
openssl::DER a(&data[0], sizeof(data));
|
||||
EXPECT_EQ(a.size(), sizeof(data));
|
||||
EXPECT_NE(a.get(), &data[0]);
|
||||
const auto* tmp_p = a.get();
|
||||
|
||||
auto* ptr = a.release();
|
||||
EXPECT_EQ(a.size(), 0);
|
||||
EXPECT_EQ(a.get(), nullptr);
|
||||
EXPECT_EQ(ptr, tmp_p);
|
||||
openssl::DER::free(ptr);
|
||||
}
|
||||
|
||||
TEST(openssl, sizes) {
|
||||
EXPECT_EQ(sizeof(openssl::sha_1_digest_t), openssl::sha_1_digest_size);
|
||||
EXPECT_EQ(sizeof(openssl::sha_256_digest_t), openssl::sha_256_digest_size);
|
||||
EXPECT_EQ(sizeof(openssl::sha_384_digest_t), openssl::sha_384_digest_size);
|
||||
EXPECT_EQ(sizeof(openssl::sha_512_digest_t), openssl::sha_512_digest_size);
|
||||
}
|
||||
|
||||
TEST(openssl, base64Encode) {
|
||||
auto res = openssl::base64_encode(&iso_exi_a_hash[0], sizeof(iso_exi_a_hash));
|
||||
EXPECT_EQ(res, iso_exi_a_hash_b64);
|
||||
res = openssl::base64_encode(&iso_exi_sig[0], sizeof(iso_exi_sig));
|
||||
EXPECT_EQ(res, iso_exi_sig_b64);
|
||||
}
|
||||
|
||||
TEST(openssl, base64EncodeNl) {
|
||||
auto res = openssl::base64_encode(&iso_exi_a_hash[0], sizeof(iso_exi_a_hash), true);
|
||||
EXPECT_EQ(res, iso_exi_a_hash_b64_nl);
|
||||
res = openssl::base64_encode(&iso_exi_sig[0], sizeof(iso_exi_sig), true);
|
||||
EXPECT_EQ(res, iso_exi_sig_b64_nl);
|
||||
}
|
||||
|
||||
TEST(openssl, base64Decode) {
|
||||
auto res = openssl::base64_decode(&iso_exi_a_hash_b64[0], sizeof(iso_exi_a_hash_b64) - 1);
|
||||
ASSERT_EQ(res.size(), sizeof(iso_exi_a_hash));
|
||||
EXPECT_EQ(std::memcmp(res.data(), &iso_exi_a_hash[0], res.size()), 0);
|
||||
res = openssl::base64_decode(&iso_exi_sig_b64[0], sizeof(iso_exi_sig_b64) - 1);
|
||||
ASSERT_EQ(res.size(), sizeof(iso_exi_sig));
|
||||
EXPECT_EQ(std::memcmp(res.data(), &iso_exi_sig[0], res.size()), 0);
|
||||
|
||||
std::array<std::uint8_t, 512> buffer{};
|
||||
std::size_t buffer_len = buffer.size();
|
||||
|
||||
EXPECT_TRUE(
|
||||
openssl::base64_decode(&iso_exi_a_hash_b64[0], sizeof(iso_exi_a_hash_b64) - 1, buffer.data(), buffer_len));
|
||||
ASSERT_EQ(buffer_len, sizeof(iso_exi_a_hash));
|
||||
EXPECT_EQ(std::memcmp(buffer.data(), &iso_exi_a_hash[0], buffer_len), 0);
|
||||
}
|
||||
|
||||
TEST(openssl, base64DecodeNl) {
|
||||
auto res = openssl::base64_decode(&iso_exi_a_hash_b64_nl[0], sizeof(iso_exi_a_hash_b64_nl) - 1);
|
||||
ASSERT_EQ(res.size(), sizeof(iso_exi_a_hash));
|
||||
EXPECT_EQ(std::memcmp(res.data(), &iso_exi_a_hash[0], res.size()), 0);
|
||||
res = openssl::base64_decode(&iso_exi_sig_b64_nl[0], sizeof(iso_exi_sig_b64_nl) - 1);
|
||||
ASSERT_EQ(res.size(), sizeof(iso_exi_sig));
|
||||
EXPECT_EQ(std::memcmp(res.data(), &iso_exi_sig[0], res.size()), 0);
|
||||
|
||||
std::array<std::uint8_t, 512> buffer{};
|
||||
std::size_t buffer_len = buffer.size();
|
||||
|
||||
EXPECT_TRUE(openssl::base64_decode(&iso_exi_a_hash_b64_nl[0], sizeof(iso_exi_a_hash_b64_nl) - 1, buffer.data(),
|
||||
buffer_len));
|
||||
ASSERT_EQ(buffer_len, sizeof(iso_exi_a_hash));
|
||||
EXPECT_EQ(std::memcmp(buffer.data(), &iso_exi_a_hash[0], buffer_len), 0);
|
||||
}
|
||||
|
||||
TEST(openssl, base64DecodeCrlf) {
|
||||
auto res = openssl::base64_decode(&iso_exi_a_hash_b64_crlf[0], sizeof(iso_exi_a_hash_b64_crlf) - 1);
|
||||
ASSERT_EQ(res.size(), sizeof(iso_exi_a_hash));
|
||||
EXPECT_EQ(std::memcmp(res.data(), &iso_exi_a_hash[0], res.size()), 0);
|
||||
}
|
||||
|
||||
TEST(openssl, base64DecodeCrOnly) {
|
||||
auto res = openssl::base64_decode(&iso_exi_a_hash_b64_cr[0], sizeof(iso_exi_a_hash_b64_cr) - 1);
|
||||
ASSERT_EQ(res.size(), sizeof(iso_exi_a_hash));
|
||||
EXPECT_EQ(std::memcmp(res.data(), &iso_exi_a_hash[0], res.size()), 0);
|
||||
}
|
||||
|
||||
TEST(openssl, base64DecodeInternalSpaces) {
|
||||
auto res = openssl::base64_decode(&iso_exi_a_hash_b64_spaces[0], sizeof(iso_exi_a_hash_b64_spaces) - 1);
|
||||
ASSERT_EQ(res.size(), sizeof(iso_exi_a_hash));
|
||||
EXPECT_EQ(std::memcmp(res.data(), &iso_exi_a_hash[0], res.size()), 0);
|
||||
}
|
||||
|
||||
TEST(openssl, base64DecodeTabs) {
|
||||
auto res = openssl::base64_decode(&iso_exi_a_hash_b64_tabs[0], sizeof(iso_exi_a_hash_b64_tabs) - 1);
|
||||
ASSERT_EQ(res.size(), sizeof(iso_exi_a_hash));
|
||||
EXPECT_EQ(std::memcmp(res.data(), &iso_exi_a_hash[0], res.size()), 0);
|
||||
}
|
||||
|
||||
TEST(openssl, base64DecodeTrailingNulIsTolerated) {
|
||||
// sizeof(literal) includes the trailing NUL; impl must skip it.
|
||||
auto res = openssl::base64_decode(&iso_exi_a_hash_b64[0], sizeof(iso_exi_a_hash_b64));
|
||||
ASSERT_EQ(res.size(), sizeof(iso_exi_a_hash));
|
||||
EXPECT_EQ(std::memcmp(res.data(), &iso_exi_a_hash[0], res.size()), 0);
|
||||
}
|
||||
|
||||
TEST(openssl, base64DecodeVerticalTabAndFormFeed) {
|
||||
const char vt_ff[] = "0bXgPQBlvuVr\vMXmERTBR61TKGPwOC\fRYXT4s8d6mPSqk=";
|
||||
auto res = openssl::base64_decode(&vt_ff[0], sizeof(vt_ff) - 1);
|
||||
ASSERT_EQ(res.size(), sizeof(iso_exi_a_hash));
|
||||
EXPECT_EQ(std::memcmp(res.data(), &iso_exi_a_hash[0], res.size()), 0);
|
||||
}
|
||||
|
||||
TEST(openssl, base64DecodeInvalidByteRejected) {
|
||||
// Non-whitespace non-alphabet bytes still produce empty output.
|
||||
auto res = openssl::base64_decode("@@@@", 4);
|
||||
EXPECT_TRUE(res.empty());
|
||||
}
|
||||
|
||||
TEST(openssl, base64DecodeEmptyInputReturnsEmpty) {
|
||||
auto res = openssl::base64_decode("", 0);
|
||||
EXPECT_TRUE(res.empty());
|
||||
|
||||
std::array<std::uint8_t, 16> buffer{};
|
||||
std::size_t buffer_len = buffer.size();
|
||||
EXPECT_FALSE(openssl::base64_decode("", 0, buffer.data(), buffer_len));
|
||||
}
|
||||
|
||||
TEST(openssl, base64EncodeEmptyInputReturnsEmpty) {
|
||||
const std::uint8_t empty_buf[1] = {0};
|
||||
auto res = openssl::base64_encode(empty_buf, 0);
|
||||
EXPECT_TRUE(res.empty());
|
||||
}
|
||||
|
||||
TEST(openssl, sha256) {
|
||||
openssl::sha_256_digest_t digest;
|
||||
EXPECT_TRUE(openssl::sha_256(sha_256_test[0].input, 0, digest));
|
||||
EXPECT_EQ(std::memcmp(digest.data(), &sha_256_test[0].digest[0], 32), 0);
|
||||
EXPECT_TRUE(openssl::sha_256(sha_256_test[1].input, 3, digest));
|
||||
EXPECT_EQ(std::memcmp(digest.data(), &sha_256_test[1].digest[0], 32), 0);
|
||||
}
|
||||
|
||||
TEST(openssl, sha256Exi) {
|
||||
openssl::sha_256_digest_t digest;
|
||||
EXPECT_TRUE(openssl::sha_256(&iso_exi_a[0], sizeof(iso_exi_a), digest));
|
||||
EXPECT_EQ(std::memcmp(digest.data(), &iso_exi_a_hash[0], 32), 0);
|
||||
|
||||
EXPECT_TRUE(openssl::sha_256(&iso_exi_b[0], sizeof(iso_exi_b), digest));
|
||||
EXPECT_EQ(std::memcmp(digest.data(), &iso_exi_b_hash[0], 32), 0);
|
||||
}
|
||||
|
||||
TEST(openssl, signVerify) {
|
||||
auto pkey = openssl::load_private_key("server_priv.pem", nullptr);
|
||||
ASSERT_TRUE(pkey);
|
||||
|
||||
std::array<std::uint8_t, 256> sig_der{};
|
||||
std::size_t sig_der_len{sig_der.size()};
|
||||
openssl::sha_256_digest_t digest;
|
||||
EXPECT_TRUE(openssl::sha_256(&sign_test[0], openssl::sha_256_digest_size, digest));
|
||||
|
||||
EXPECT_TRUE(openssl::sign(pkey.get(), sig_der.data(), sig_der_len, digest.data(), digest.size()));
|
||||
// std::cout << "signature size: " << sig_der_len << std::endl;
|
||||
EXPECT_TRUE(openssl::verify(pkey.get(), sig_der.data(), sig_der_len, digest.data(), digest.size()));
|
||||
}
|
||||
|
||||
#ifdef USING_TPM2
|
||||
TEST(opensslTpm, signVerify) {
|
||||
auto pkey = openssl::load_private_key("tpm_pki/server_priv.pem", nullptr);
|
||||
ASSERT_TRUE(pkey);
|
||||
|
||||
// looks like sign/verify may not be supported
|
||||
// TODO(james-ctc) investigation needed
|
||||
// perhaps the public key needs to be extracted
|
||||
|
||||
#if 0
|
||||
if (EVP_PKEY_can_sign(pkey.get()) == 1) {
|
||||
std::array<std::uint8_t, 256> sig_der{};
|
||||
std::size_t sig_der_len{sig_der.size()};
|
||||
openssl::sha_256_digest_t digest;
|
||||
evse_security::OpenSSLProvider provider;
|
||||
provider.set_global_mode(evse_security::OpenSSLProvider::mode_t::custom_provider);
|
||||
EXPECT_TRUE(openssl::sha_256(&sign_test[0], openssl::sha_256_digest_size, digest));
|
||||
|
||||
EXPECT_TRUE(openssl::sign(pkey.get(), sig_der.data(), sig_der_len, digest.data(), digest.size()));
|
||||
// std::cout << "signature size: " << sig_der_len << std::endl;
|
||||
|
||||
EXPECT_TRUE(openssl::verify(pkey.get(), sig_der.data(), sig_der_len, digest.data(), digest.size()));
|
||||
} else {
|
||||
std::cout << "sign/verify not supported" << std::endl;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(openssl, signVerifyBn) {
|
||||
auto pkey = openssl::load_private_key("server_priv.pem", nullptr);
|
||||
ASSERT_TRUE(pkey);
|
||||
|
||||
openssl::bn_t r;
|
||||
openssl::bn_t s;
|
||||
|
||||
openssl::sha_256_digest_t digest;
|
||||
EXPECT_TRUE(openssl::sha_256(&sign_test[0], openssl::sha_256_digest_size, digest));
|
||||
|
||||
EXPECT_TRUE(openssl::sign(pkey.get(), r, s, digest));
|
||||
// std::cout << "signature size: " << sig_der_len << std::endl;
|
||||
EXPECT_TRUE(openssl::verify(pkey.get(), r, s, digest));
|
||||
}
|
||||
|
||||
TEST(openssl, signVerifyMix) {
|
||||
auto pkey = openssl::load_private_key("server_priv.pem", nullptr);
|
||||
ASSERT_TRUE(pkey);
|
||||
|
||||
std::array<std::uint8_t, 80> sig_der;
|
||||
std::size_t sig_der_len{sig_der.size()};
|
||||
openssl::sha_256_digest_t digest;
|
||||
EXPECT_TRUE(openssl::sha_256(&sign_test[0], openssl::sha_256_digest_size, digest));
|
||||
|
||||
EXPECT_TRUE(openssl::sign(pkey.get(), sig_der.data(), sig_der_len, digest.data(), digest.size()));
|
||||
|
||||
openssl::bn_t r;
|
||||
openssl::bn_t s;
|
||||
EXPECT_TRUE(openssl::signature_to_bn(r, s, sig_der.data(), sig_der_len));
|
||||
EXPECT_TRUE(openssl::verify(pkey.get(), r, s, digest));
|
||||
}
|
||||
|
||||
TEST(openssl, signVerifyFail) {
|
||||
auto pkey = openssl::load_private_key("server_priv.pem", nullptr);
|
||||
ASSERT_TRUE(pkey);
|
||||
auto pkey_inv = openssl::load_private_key("client_priv.pem", nullptr);
|
||||
ASSERT_TRUE(pkey);
|
||||
|
||||
std::array<std::uint8_t, 256> sig_der;
|
||||
std::size_t sig_der_len{sig_der.size()};
|
||||
openssl::sha_256_digest_t digest;
|
||||
EXPECT_TRUE(openssl::sha_256(&sign_test[0], openssl::sha_256_digest_size, digest));
|
||||
|
||||
EXPECT_TRUE(openssl::sign(pkey.get(), sig_der.data(), sig_der_len, digest.data(), digest.size()));
|
||||
// std::cout << "signature size: " << sig_der_len << std::endl;
|
||||
EXPECT_FALSE(openssl::verify(pkey_inv.get(), sig_der.data(), sig_der_len, digest.data(), digest.size()));
|
||||
}
|
||||
|
||||
TEST(openssl, verifyIso) {
|
||||
auto pkey = openssl::load_private_key("iso_priv.pem", nullptr);
|
||||
ASSERT_TRUE(pkey);
|
||||
|
||||
auto sig = openssl::bn_to_signature(&iso_exi_sig[0], &iso_exi_sig[32]);
|
||||
EXPECT_TRUE(openssl::verify(pkey.get(), sig.get(), sig.size(), &iso_exi_b_hash[0], sizeof(iso_exi_b_hash)));
|
||||
}
|
||||
|
||||
TEST(certificateLoad, single) {
|
||||
auto certs = ::openssl::load_certificates("server_cert.pem");
|
||||
EXPECT_EQ(certs.size(), 1);
|
||||
}
|
||||
|
||||
TEST(certificateLoad, chain) {
|
||||
auto certs = ::openssl::load_certificates("server_chain.pem");
|
||||
EXPECT_EQ(certs.size(), 2);
|
||||
}
|
||||
|
||||
TEST(certificateLoad, key) {
|
||||
auto certs = ::openssl::load_certificates("server_priv.pem");
|
||||
EXPECT_EQ(certs.size(), 0);
|
||||
}
|
||||
|
||||
TEST(certificateLoad, missing) {
|
||||
auto certs = ::openssl::load_certificates("server_priv.pem-not-found");
|
||||
EXPECT_EQ(certs.size(), 0);
|
||||
}
|
||||
|
||||
TEST(certificateLoad, nullptr) {
|
||||
auto certs = ::openssl::load_certificates(nullptr);
|
||||
EXPECT_EQ(certs.size(), 0);
|
||||
}
|
||||
|
||||
TEST(certificateLoad, empty) {
|
||||
auto certs = ::openssl::load_certificates("");
|
||||
EXPECT_EQ(certs.size(), 0);
|
||||
}
|
||||
|
||||
TEST(certificateLoad, multiFile) {
|
||||
std::vector<const char*> files{"server_chain.pem", "alt_server_chain.pem"};
|
||||
|
||||
auto certs = ::openssl::load_certificates(files);
|
||||
ASSERT_EQ(certs.size(), 4);
|
||||
|
||||
auto server = ::openssl::load_certificates("server_cert.pem");
|
||||
auto ca = ::openssl::load_certificates("server_ca_cert.pem");
|
||||
auto alt_server = ::openssl::load_certificates("alt_server_cert.pem");
|
||||
auto alt_ca = ::openssl::load_certificates("alt_server_ca_cert.pem");
|
||||
|
||||
EXPECT_EQ(certs[0], server[0]);
|
||||
EXPECT_EQ(certs[1], ca[0]);
|
||||
EXPECT_EQ(certs[2], alt_server[0]);
|
||||
EXPECT_EQ(certs[3], alt_ca[0]);
|
||||
}
|
||||
|
||||
TEST(certificateLoad, multiFileEmpty) {
|
||||
std::vector<const char*> files{};
|
||||
|
||||
auto certs = ::openssl::load_certificates(files);
|
||||
ASSERT_EQ(certs.size(), 0);
|
||||
}
|
||||
|
||||
TEST(certificateLoad, multiFileMissing) {
|
||||
std::vector<const char*> files{"server_chain.pem", "alt_server_chain.pem-not-found"};
|
||||
|
||||
auto certs = ::openssl::load_certificates(files);
|
||||
ASSERT_EQ(certs.size(), 2);
|
||||
|
||||
auto server = ::openssl::load_certificates("server_cert.pem");
|
||||
auto ca = ::openssl::load_certificates("server_ca_cert.pem");
|
||||
|
||||
EXPECT_EQ(certs[0], server[0]);
|
||||
EXPECT_EQ(certs[1], ca[0]);
|
||||
}
|
||||
|
||||
TEST(certificateLoad, multiFileNullptr) {
|
||||
std::vector<const char*> files{nullptr, "alt_server_chain.pem"};
|
||||
|
||||
auto certs = ::openssl::load_certificates(files);
|
||||
ASSERT_EQ(certs.size(), 2);
|
||||
|
||||
auto alt_server = ::openssl::load_certificates("alt_server_cert.pem");
|
||||
auto alt_ca = ::openssl::load_certificates("alt_server_ca_cert.pem");
|
||||
|
||||
EXPECT_EQ(certs[0], alt_server[0]);
|
||||
EXPECT_EQ(certs[1], alt_ca[0]);
|
||||
}
|
||||
|
||||
TEST(certificateLoadPki, none) {
|
||||
auto certs = ::openssl::load_certificates(nullptr, nullptr, nullptr);
|
||||
EXPECT_EQ(certs.leaf, nullptr);
|
||||
EXPECT_EQ(certs.chain.size(), 0);
|
||||
EXPECT_EQ(certs.trust_anchors.size(), 0);
|
||||
|
||||
certs = ::openssl::load_certificates("server_cert.pem", nullptr, nullptr);
|
||||
EXPECT_EQ(certs.leaf, nullptr);
|
||||
EXPECT_EQ(certs.chain.size(), 0);
|
||||
EXPECT_EQ(certs.trust_anchors.size(), 0);
|
||||
|
||||
certs = ::openssl::load_certificates(nullptr, "server_chain.pem", nullptr);
|
||||
EXPECT_EQ(certs.leaf, nullptr);
|
||||
EXPECT_EQ(certs.chain.size(), 0);
|
||||
EXPECT_EQ(certs.trust_anchors.size(), 0);
|
||||
|
||||
certs = ::openssl::load_certificates(nullptr, nullptr, "server_root_cert.pem");
|
||||
EXPECT_EQ(certs.leaf, nullptr);
|
||||
EXPECT_EQ(certs.chain.size(), 0);
|
||||
EXPECT_EQ(certs.trust_anchors.size(), 0);
|
||||
}
|
||||
|
||||
TEST(certificateLoadPki, full) {
|
||||
auto certs = ::openssl::load_certificates("server_cert.pem", "server_ca_cert.pem", "server_root_cert.pem");
|
||||
|
||||
auto server = ::openssl::load_certificates("server_cert.pem");
|
||||
ASSERT_EQ(server.size(), 1);
|
||||
auto chain = ::openssl::load_certificates("server_ca_cert.pem");
|
||||
ASSERT_EQ(chain.size(), 1);
|
||||
auto trust_anchors = ::openssl::load_certificates("server_root_cert.pem");
|
||||
ASSERT_EQ(trust_anchors.size(), 1);
|
||||
|
||||
ASSERT_NE(certs.leaf, nullptr);
|
||||
EXPECT_EQ(certs.leaf, server[0]);
|
||||
ASSERT_EQ(certs.chain.size(), 1);
|
||||
ASSERT_EQ(certs.trust_anchors.size(), 1);
|
||||
|
||||
EXPECT_EQ(certs.chain, chain);
|
||||
EXPECT_EQ(certs.trust_anchors, trust_anchors);
|
||||
}
|
||||
|
||||
TEST(certificateLoadPki, noLeaf) {
|
||||
// should work since leaf is 1st certificate in server_chain.pem
|
||||
auto certs = ::openssl::load_certificates(nullptr, "server_chain.pem", "server_root_cert.pem");
|
||||
|
||||
auto server = ::openssl::load_certificates("server_cert.pem");
|
||||
ASSERT_EQ(server.size(), 1);
|
||||
auto chain = ::openssl::load_certificates("server_ca_cert.pem");
|
||||
ASSERT_EQ(chain.size(), 1);
|
||||
auto trust_anchors = ::openssl::load_certificates("server_root_cert.pem");
|
||||
ASSERT_EQ(trust_anchors.size(), 1);
|
||||
|
||||
EXPECT_EQ(certs.leaf, server[0]);
|
||||
ASSERT_EQ(certs.chain.size(), 1);
|
||||
ASSERT_EQ(certs.trust_anchors.size(), 1);
|
||||
|
||||
EXPECT_EQ(certs.chain, chain);
|
||||
EXPECT_EQ(certs.trust_anchors, trust_anchors);
|
||||
}
|
||||
|
||||
TEST(certificateLoadPki, invalid) {
|
||||
auto certs = ::openssl::load_certificates("client_cert.pem", "server_ca_cert.pem", "server_root_cert.pem");
|
||||
EXPECT_EQ(certs.leaf, nullptr);
|
||||
EXPECT_EQ(certs.chain.size(), 0);
|
||||
EXPECT_EQ(certs.trust_anchors.size(), 0);
|
||||
|
||||
certs = ::openssl::load_certificates("server_cert.pem", "client_ca_cert.pem", "server_root_cert.pem");
|
||||
EXPECT_EQ(certs.leaf, nullptr);
|
||||
EXPECT_EQ(certs.chain.size(), 0);
|
||||
EXPECT_EQ(certs.trust_anchors.size(), 0);
|
||||
|
||||
certs = ::openssl::load_certificates("server_cert.pem", "server_ca_cert.pem", "client_root_cert.pem");
|
||||
EXPECT_EQ(certs.leaf, nullptr);
|
||||
EXPECT_EQ(certs.chain.size(), 0);
|
||||
EXPECT_EQ(certs.trust_anchors.size(), 0);
|
||||
|
||||
certs = ::openssl::load_certificates(nullptr, "server_chain.pem", "client_root_cert.pem");
|
||||
EXPECT_EQ(certs.leaf, nullptr);
|
||||
EXPECT_EQ(certs.chain.size(), 0);
|
||||
EXPECT_EQ(certs.trust_anchors.size(), 0);
|
||||
}
|
||||
|
||||
TEST(certificate, toPem) {
|
||||
auto certs = ::openssl::load_certificates("client_ca_cert.pem");
|
||||
ASSERT_EQ(certs.size(), 1);
|
||||
auto pem = ::openssl::certificate_to_pem(certs[0].get());
|
||||
EXPECT_FALSE(pem.empty());
|
||||
// std::cout << pem << std::endl;
|
||||
}
|
||||
|
||||
TEST(certificate, loadPemSingle) {
|
||||
auto certs = ::openssl::load_certificates("client_ca_cert.pem");
|
||||
ASSERT_EQ(certs.size(), 1);
|
||||
auto pem = ::openssl::certificate_to_pem(certs[0].get());
|
||||
EXPECT_FALSE(pem.empty());
|
||||
|
||||
auto pem_certs = ::openssl::load_certificates_pem(pem.c_str());
|
||||
ASSERT_EQ(pem_certs.size(), 1);
|
||||
EXPECT_EQ(certs[0], pem_certs[0]);
|
||||
}
|
||||
|
||||
TEST(certificate, loadPemMulti) {
|
||||
auto certs = ::openssl::load_certificates("client_chain.pem");
|
||||
ASSERT_GT(certs.size(), 1);
|
||||
std::string pem;
|
||||
for (const auto& cert : certs) {
|
||||
pem += ::openssl::certificate_to_pem(cert.get());
|
||||
}
|
||||
EXPECT_FALSE(pem.empty());
|
||||
// std::cout << pem << std::endl << "Output" << std::endl;
|
||||
|
||||
auto pem_certs = ::openssl::load_certificates_pem(pem.c_str());
|
||||
ASSERT_EQ(pem_certs.size(), certs.size());
|
||||
for (auto i = 0; i < certs.size(); i++) {
|
||||
SCOPED_TRACE(std::to_string(i));
|
||||
// std::cout << ::openssl::certificate_to_pem(pem_certs[i].get()) << std::endl;
|
||||
EXPECT_EQ(certs[i], pem_certs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(certificate, verify) {
|
||||
auto client = ::openssl::load_certificates("client_cert.pem");
|
||||
auto chain = ::openssl::load_certificates("client_chain.pem");
|
||||
auto root = ::openssl::load_certificates("client_root_cert.pem");
|
||||
|
||||
ASSERT_EQ(client.size(), 1);
|
||||
EXPECT_GT(chain.size(), 0);
|
||||
EXPECT_EQ(root.size(), 1);
|
||||
|
||||
EXPECT_EQ(::openssl::verify_certificate(client[0].get(), root, chain), openssl::verify_result_t::Verified);
|
||||
}
|
||||
|
||||
TEST(certificate, verifyCross) {
|
||||
auto client = ::openssl::load_certificates("server_cert.pem");
|
||||
auto chain = ::openssl::load_certificates("cross_ca_cert.pem");
|
||||
auto root = ::openssl::load_certificates("client_root_cert.pem");
|
||||
|
||||
ASSERT_EQ(client.size(), 1);
|
||||
EXPECT_GT(chain.size(), 0);
|
||||
EXPECT_EQ(root.size(), 1);
|
||||
|
||||
EXPECT_EQ(::openssl::verify_certificate(client[0].get(), root, chain), openssl::verify_result_t::Verified);
|
||||
}
|
||||
|
||||
TEST(certificate, verifyRemoveClientFromChain) {
|
||||
auto client = ::openssl::load_certificates("client_cert.pem");
|
||||
auto chain = ::openssl::load_certificates("client_chain.pem");
|
||||
auto root = ::openssl::load_certificates("client_root_cert.pem");
|
||||
|
||||
ASSERT_EQ(client.size(), 1);
|
||||
EXPECT_GT(chain.size(), 0);
|
||||
EXPECT_EQ(root.size(), 1);
|
||||
|
||||
// client certificate is 1st in the list
|
||||
openssl::certificate_list new_chain;
|
||||
for (auto itt = std::next(chain.begin()); itt != chain.end(); itt++) {
|
||||
new_chain.push_back(std::move(*itt));
|
||||
}
|
||||
|
||||
EXPECT_EQ(::openssl::verify_certificate(client[0].get(), root, new_chain), openssl::verify_result_t::Verified);
|
||||
}
|
||||
|
||||
TEST(certificate, verifyNoClient) {
|
||||
// client certificate is in the chain
|
||||
auto chain = ::openssl::load_certificates("client_chain.pem");
|
||||
auto root = ::openssl::load_certificates("client_root_cert.pem");
|
||||
|
||||
EXPECT_GT(chain.size(), 0);
|
||||
EXPECT_EQ(root.size(), 1);
|
||||
|
||||
EXPECT_EQ(::openssl::verify_certificate(nullptr, root, chain), openssl::verify_result_t::Verified);
|
||||
}
|
||||
|
||||
TEST(certificate, verifyFailWrongClient) {
|
||||
auto client = ::openssl::load_certificates("server_cert.pem");
|
||||
auto chain = ::openssl::load_certificates("client_chain.pem");
|
||||
auto root = ::openssl::load_certificates("client_root_cert.pem");
|
||||
|
||||
ASSERT_EQ(client.size(), 1);
|
||||
EXPECT_GT(chain.size(), 0);
|
||||
EXPECT_EQ(root.size(), 1);
|
||||
|
||||
EXPECT_NE(::openssl::verify_certificate(client[0].get(), root, chain), openssl::verify_result_t::Verified);
|
||||
}
|
||||
|
||||
TEST(certificate, verifyFailWrongRoot) {
|
||||
auto client = ::openssl::load_certificates("client_cert.pem");
|
||||
auto chain = ::openssl::load_certificates("client_chain.pem");
|
||||
auto root = ::openssl::load_certificates("server_root_cert.pem");
|
||||
|
||||
ASSERT_EQ(client.size(), 1);
|
||||
EXPECT_GT(chain.size(), 0);
|
||||
EXPECT_EQ(root.size(), 1);
|
||||
|
||||
EXPECT_NE(::openssl::verify_certificate(client[0].get(), root, chain), openssl::verify_result_t::Verified);
|
||||
}
|
||||
|
||||
TEST(certificate, verifyFailWrongChain) {
|
||||
auto client = ::openssl::load_certificates("client_cert.pem");
|
||||
auto chain = ::openssl::load_certificates("server_chain.pem");
|
||||
auto root = ::openssl::load_certificates("client_root_cert.pem");
|
||||
|
||||
ASSERT_EQ(client.size(), 1);
|
||||
EXPECT_GT(chain.size(), 0);
|
||||
EXPECT_EQ(root.size(), 1);
|
||||
|
||||
EXPECT_NE(::openssl::verify_certificate(client[0].get(), root, chain), openssl::verify_result_t::Verified);
|
||||
}
|
||||
|
||||
TEST(certificate, subjectName) {
|
||||
auto chain = ::openssl::load_certificates("client_chain.pem");
|
||||
EXPECT_GT(chain.size(), 0);
|
||||
|
||||
for (const auto& cert : chain) {
|
||||
auto subject = ::openssl::certificate_subject(cert.get());
|
||||
EXPECT_GT(subject.size(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(certificateChainInfo, valid) {
|
||||
auto chain = openssl::load_certificates("client_cert.pem", "client_ca_cert.pem", "client_root_cert.pem");
|
||||
EXPECT_TRUE(openssl::verify_chain(chain));
|
||||
}
|
||||
|
||||
TEST(certificateChainInfo, invalid) {
|
||||
auto chain = openssl::load_certificates("server_cert.pem", "client_ca_cert.pem", "client_root_cert.pem");
|
||||
EXPECT_FALSE(openssl::verify_chain(chain));
|
||||
chain = openssl::load_certificates("client_cert.pem", "server_ca_cert.pem", "client_root_cert.pem");
|
||||
EXPECT_FALSE(openssl::verify_chain(chain));
|
||||
chain = openssl::load_certificates("client_cert.pem", "client_ca_cert.pem", "server_root_cert.pem");
|
||||
EXPECT_FALSE(openssl::verify_chain(chain));
|
||||
}
|
||||
|
||||
TEST(certificateChain, valid) {
|
||||
auto pkey = openssl::load_private_key("client_priv.pem", nullptr);
|
||||
auto chain_info = openssl::load_certificates("client_cert.pem", "client_ca_cert.pem", "client_root_cert.pem");
|
||||
openssl::chain_t chain = {std::move(chain_info), std::move(pkey)};
|
||||
EXPECT_TRUE(openssl::verify_chain(chain));
|
||||
}
|
||||
|
||||
TEST(certificateChain, invalid) {
|
||||
auto pkey = openssl::load_private_key("server_priv.pem", nullptr);
|
||||
auto chain_info = openssl::load_certificates("client_cert.pem", "client_ca_cert.pem", "client_root_cert.pem");
|
||||
openssl::chain_t chain = {std::move(chain_info), std::move(pkey)};
|
||||
EXPECT_FALSE(openssl::verify_chain(chain));
|
||||
chain_info = openssl::load_certificates("server_cert.pem", "client_ca_cert.pem", "client_root_cert.pem");
|
||||
chain.chain = std::move(chain_info);
|
||||
EXPECT_FALSE(openssl::verify_chain(chain));
|
||||
chain_info = openssl::load_certificates("client_cert.pem", "server_ca_cert.pem", "client_root_cert.pem");
|
||||
chain.chain = std::move(chain_info);
|
||||
EXPECT_FALSE(openssl::verify_chain(chain));
|
||||
chain_info = openssl::load_certificates("client_cert.pem", "client_ca_cert.pem", "server_root_cert.pem");
|
||||
chain.chain = std::move(chain_info);
|
||||
EXPECT_FALSE(openssl::verify_chain(chain));
|
||||
}
|
||||
|
||||
TEST(certificate, apply) {
|
||||
auto pkey = openssl::load_private_key("client_priv.pem", nullptr);
|
||||
auto chain_info = openssl::load_certificates("client_cert.pem", "client_ca_cert.pem", "client_root_cert.pem");
|
||||
openssl::chain_t chain = {std::move(chain_info), std::move(pkey)};
|
||||
auto* ctx = SSL_CTX_new(TLS_server_method());
|
||||
auto* ssl = SSL_new(ctx);
|
||||
EXPECT_TRUE(openssl::use_certificate_and_key(ssl, chain));
|
||||
SSL_free(ssl);
|
||||
SSL_CTX_free(ctx);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
62
tools/EVerest-main/lib/everest/tls/tests/patched_test.cpp
Normal file
62
tools/EVerest-main/lib/everest/tls/tests/patched_test.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2024 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
/**
|
||||
* \file testing patched version of OpenSSL
|
||||
*
|
||||
* These tests will only pass on a patched version of OpenSSL.
|
||||
* (they should compile and run fine with some test failures)
|
||||
*
|
||||
* It is recommended to also run tests alongside Wireshark
|
||||
* e.g. `./patched_test --gtest_filter=TlsTest.TLS12`
|
||||
* to check that the Server Hello record is correctly formed:
|
||||
* - no status_request or status_request_v2 then no Certificate Status record
|
||||
* - status_request or status_request_v2 then there is a Certificate Status record
|
||||
* - never both status_request and status_request_v2
|
||||
*/
|
||||
|
||||
#include "tls_connection_test.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// The tests - only pass on a patched OpenSSL
|
||||
|
||||
TEST_F(TlsTest, TLS12) {
|
||||
// test using TLS 1.2
|
||||
start();
|
||||
connect();
|
||||
// no status requested
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_cb));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_v2));
|
||||
|
||||
client_config.status_request = true;
|
||||
connect();
|
||||
// status_request only
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(is_set(flags_t::status_request_cb));
|
||||
EXPECT_TRUE(is_set(flags_t::status_request));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_v2));
|
||||
|
||||
client_config.status_request = false;
|
||||
client_config.status_request_v2 = true;
|
||||
connect();
|
||||
// status_request_v2 only
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(is_set(flags_t::status_request_cb));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request));
|
||||
EXPECT_TRUE(is_set(flags_t::status_request_v2));
|
||||
|
||||
client_config.status_request = true;
|
||||
connect();
|
||||
// status_request and status_request_v2
|
||||
// status_request_v2 is preferred over status_request
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(is_set(flags_t::status_request_cb));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request));
|
||||
EXPECT_TRUE(is_set(flags_t::status_request_v2));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
1
tools/EVerest-main/lib/everest/tls/tests/pki/.gitignore
vendored
Normal file
1
tools/EVerest-main/lib/everest/tls/tests/pki/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.pem
|
||||
@@ -0,0 +1,143 @@
|
||||
openssl_conf = openssl_init
|
||||
|
||||
[openssl_init]
|
||||
providers = provider_section
|
||||
|
||||
[provider_section]
|
||||
default = default_section
|
||||
tpm2 = tpm2_section
|
||||
base = base_section
|
||||
|
||||
[default_section]
|
||||
activate = 1
|
||||
|
||||
[tpm2_section]
|
||||
activate = 1
|
||||
|
||||
[base_section]
|
||||
activate = 1
|
||||
|
||||
# server section
|
||||
# ==============
|
||||
[req_server_root]
|
||||
distinguished_name = req_dn_server_root
|
||||
utf8 = yes
|
||||
prompt = no
|
||||
req_extensions = v3_server_root
|
||||
|
||||
[req_server_ca]
|
||||
distinguished_name = req_dn_server_ca
|
||||
utf8 = yes
|
||||
prompt = no
|
||||
req_extensions = v3_server_ca
|
||||
|
||||
[req_server]
|
||||
distinguished_name = req_dn_server
|
||||
utf8 = yes
|
||||
prompt = no
|
||||
req_extensions = v3_server
|
||||
|
||||
[req_dn_server_root]
|
||||
C = GB
|
||||
O = Pionix
|
||||
L = London
|
||||
CN = Alternate Root Trust Anchor
|
||||
|
||||
[req_dn_server_ca]
|
||||
C = GB
|
||||
O = Pionix
|
||||
L = London
|
||||
CN = Alternate Intermediate CA
|
||||
|
||||
[req_dn_server]
|
||||
C = GB
|
||||
O = Pionix
|
||||
L = London
|
||||
CN = 11111111
|
||||
|
||||
[req_dn_client]
|
||||
C = GB
|
||||
O = Pionix
|
||||
L = London
|
||||
CN = 98765432
|
||||
|
||||
[v3_server_root]
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid:always,issuer:always
|
||||
basicConstraints = critical, CA:true, pathlen:2
|
||||
keyUsage = keyCertSign, cRLSign
|
||||
|
||||
[v3_server_ca]
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid:always,issuer:always
|
||||
basicConstraints = critical, CA:true
|
||||
keyUsage = keyCertSign, cRLSign
|
||||
|
||||
[v3_server]
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid:always
|
||||
keyUsage = digitalSignature, keyEncipherment, keyAgreement
|
||||
extendedKeyUsage = serverAuth, clientAuth
|
||||
subjectAltName = IP:192.168.245.1, DNS:evse.pionix.de
|
||||
|
||||
# client section
|
||||
# ==============
|
||||
[req_client]
|
||||
distinguished_name = req_dn_client
|
||||
utf8 = yes
|
||||
prompt = no
|
||||
req_extensions = v3_client
|
||||
|
||||
[req_client_root]
|
||||
distinguished_name = req_dn_client_root
|
||||
utf8 = yes
|
||||
prompt = no
|
||||
req_extensions = v3_client_root
|
||||
|
||||
[req_client_ca]
|
||||
distinguished_name = req_dn_client_ca
|
||||
utf8 = yes
|
||||
prompt = no
|
||||
req_extensions = v3_client_ca
|
||||
|
||||
[req_server]
|
||||
distinguished_name = req_dn_server
|
||||
utf8 = yes
|
||||
prompt = no
|
||||
req_extensions = v3_server
|
||||
|
||||
[req_dn_client_root]
|
||||
C = DE
|
||||
O = Pionix
|
||||
L = Frankfurt
|
||||
CN = Alternate Root Trust Anchor
|
||||
|
||||
[req_dn_client_ca]
|
||||
C = DE
|
||||
O = Pionix
|
||||
L = Frankfurt
|
||||
CN = Alternate Intermediate CA
|
||||
|
||||
[req_dn_client]
|
||||
C = DE
|
||||
O = Pionix
|
||||
L = Frankfurt
|
||||
CN = 66666666
|
||||
|
||||
[v3_client_root]
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid:always,issuer:always
|
||||
basicConstraints = critical, CA:true, pathlen:2
|
||||
keyUsage = keyCertSign, cRLSign
|
||||
|
||||
[v3_client_ca]
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid:always,issuer:always
|
||||
basicConstraints = critical, CA:true
|
||||
keyUsage = keyCertSign, cRLSign
|
||||
|
||||
[v3_client]
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid:always
|
||||
keyUsage = digitalSignature, keyEncipherment, keyAgreement
|
||||
extendedKeyUsage = clientAuth
|
||||
11
tools/EVerest-main/lib/everest/tls/tests/pki/iso_pkey.asn1
Normal file
11
tools/EVerest-main/lib/everest/tls/tests/pki/iso_pkey.asn1
Normal file
@@ -0,0 +1,11 @@
|
||||
asn1=SEQ:pkcs8c
|
||||
[pkcs8c]
|
||||
ver=INT:0
|
||||
algid=SEQ:algid
|
||||
data=OCTWRAP,SEQ:sec1
|
||||
[algid]
|
||||
alg=OID:id-ecPublicKey
|
||||
parm=OID:prime256v1
|
||||
[sec1]
|
||||
ver=INT:1
|
||||
privkey=FORMAT:HEX,OCT:b9134963f51c4414738435057f97bbf1010cabcb8dbde9c5d48138396aa94b9d
|
||||
BIN
tools/EVerest-main/lib/everest/tls/tests/pki/ocsp_response.der
Normal file
BIN
tools/EVerest-main/lib/everest/tls/tests/pki/ocsp_response.der
Normal file
Binary file not shown.
143
tools/EVerest-main/lib/everest/tls/tests/pki/openssl-pki.conf
Normal file
143
tools/EVerest-main/lib/everest/tls/tests/pki/openssl-pki.conf
Normal file
@@ -0,0 +1,143 @@
|
||||
openssl_conf = openssl_init
|
||||
|
||||
[openssl_init]
|
||||
providers = provider_section
|
||||
|
||||
[provider_section]
|
||||
default = default_section
|
||||
tpm2 = tpm2_section
|
||||
base = base_section
|
||||
|
||||
[default_section]
|
||||
activate = 1
|
||||
|
||||
[tpm2_section]
|
||||
activate = 1
|
||||
|
||||
[base_section]
|
||||
activate = 1
|
||||
|
||||
# server section
|
||||
# ==============
|
||||
[req_server_root]
|
||||
distinguished_name = req_dn_server_root
|
||||
utf8 = yes
|
||||
prompt = no
|
||||
req_extensions = v3_server_root
|
||||
|
||||
[req_server_ca]
|
||||
distinguished_name = req_dn_server_ca
|
||||
utf8 = yes
|
||||
prompt = no
|
||||
req_extensions = v3_server_ca
|
||||
|
||||
[req_server]
|
||||
distinguished_name = req_dn_server
|
||||
utf8 = yes
|
||||
prompt = no
|
||||
req_extensions = v3_server
|
||||
|
||||
[req_dn_server_root]
|
||||
C = GB
|
||||
O = Pionix
|
||||
L = London
|
||||
CN = Root Trust Anchor
|
||||
|
||||
[req_dn_server_ca]
|
||||
C = GB
|
||||
O = Pionix
|
||||
L = London
|
||||
CN = Intermediate CA
|
||||
|
||||
[req_dn_server]
|
||||
C = GB
|
||||
O = Pionix
|
||||
L = London
|
||||
CN = 00000000
|
||||
|
||||
[req_dn_client]
|
||||
C = GB
|
||||
O = Pionix
|
||||
L = London
|
||||
CN = 12345678
|
||||
|
||||
[v3_server_root]
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid:always,issuer:always
|
||||
basicConstraints = critical, CA:true, pathlen:2
|
||||
keyUsage = keyCertSign, cRLSign
|
||||
|
||||
[v3_server_ca]
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid:always,issuer:always
|
||||
basicConstraints = critical, CA:true
|
||||
keyUsage = keyCertSign, cRLSign
|
||||
|
||||
[v3_server]
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid:always
|
||||
keyUsage = digitalSignature, keyEncipherment, keyAgreement
|
||||
extendedKeyUsage = serverAuth, clientAuth
|
||||
subjectAltName = IP:192.168.245.1, DNS:evse.pionix.de
|
||||
|
||||
# client section
|
||||
# ==============
|
||||
[req_client]
|
||||
distinguished_name = req_dn_client
|
||||
utf8 = yes
|
||||
prompt = no
|
||||
req_extensions = v3_client
|
||||
|
||||
[req_client_root]
|
||||
distinguished_name = req_dn_client_root
|
||||
utf8 = yes
|
||||
prompt = no
|
||||
req_extensions = v3_client_root
|
||||
|
||||
[req_client_ca]
|
||||
distinguished_name = req_dn_client_ca
|
||||
utf8 = yes
|
||||
prompt = no
|
||||
req_extensions = v3_client_ca
|
||||
|
||||
[req_server]
|
||||
distinguished_name = req_dn_server
|
||||
utf8 = yes
|
||||
prompt = no
|
||||
req_extensions = v3_server
|
||||
|
||||
[req_dn_client_root]
|
||||
C = DE
|
||||
O = Pionix
|
||||
L = Frankfurt
|
||||
CN = Root Trust Anchor
|
||||
|
||||
[req_dn_client_ca]
|
||||
C = DE
|
||||
O = Pionix
|
||||
L = Frankfurt
|
||||
CN = Intermediate CA
|
||||
|
||||
[req_dn_client]
|
||||
C = DE
|
||||
O = Pionix
|
||||
L = Frankfurt
|
||||
CN = 12345678
|
||||
|
||||
[v3_client_root]
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid:always,issuer:always
|
||||
basicConstraints = critical, CA:true, pathlen:2
|
||||
keyUsage = keyCertSign, cRLSign
|
||||
|
||||
[v3_client_ca]
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid:always,issuer:always
|
||||
basicConstraints = critical, CA:true
|
||||
keyUsage = keyCertSign, cRLSign
|
||||
|
||||
[v3_client]
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid:always
|
||||
keyUsage = digitalSignature, keyEncipherment, keyAgreement
|
||||
extendedKeyUsage = clientAuth
|
||||
57
tools/EVerest-main/lib/everest/tls/tests/pki/pki-tpm.sh
Executable file
57
tools/EVerest-main/lib/everest/tls/tests/pki/pki-tpm.sh
Executable file
@@ -0,0 +1,57 @@
|
||||
#!/bin/sh
|
||||
|
||||
base=.
|
||||
cfg=./openssl-pki.conf
|
||||
dir=tpm_pki
|
||||
|
||||
[ ! -f "$cfg" ] && echo "missing openssl-pki.conf" && exit 1
|
||||
|
||||
generate() {
|
||||
local base=$1
|
||||
local dir=$2
|
||||
mkdir -p ${base}/${dir}
|
||||
|
||||
local root_priv=${base}/${dir}/server_root_priv.pem
|
||||
local ca_priv=${base}/${dir}/server_ca_priv.pem
|
||||
local server_priv=${base}/${dir}/server_priv.pem
|
||||
|
||||
local root_cert=${base}/${dir}/server_root_cert.pem
|
||||
local ca_cert=${base}/${dir}/server_ca_cert.pem
|
||||
local server_cert=${base}/${dir}/server_cert.pem
|
||||
local cert_path=${base}/${dir}/server_chain.pem
|
||||
|
||||
local tpmA="-provider"
|
||||
local tpmB="tpm2"
|
||||
local propA="-propquery"
|
||||
local propB="?provider=tpm2"
|
||||
|
||||
# generate keys
|
||||
for i in ${root_priv} ${ca_priv} ${server_priv}
|
||||
do
|
||||
openssl genpkey -config ${cfg} ${tpmA} ${tpmB} ${propA} ${propB} -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out $i
|
||||
done
|
||||
|
||||
export OPENSSL_CONF=${cfg}
|
||||
# generate root cert
|
||||
echo "Generate root"
|
||||
openssl req ${tpmA} ${tpmB} -provider default ${propA} ${propB} \
|
||||
-config ${cfg} -x509 -section req_server_root -extensions v3_server_root \
|
||||
-key ${root_priv} -out ${root_cert}
|
||||
# generate ca cert
|
||||
echo "Generate ca"
|
||||
openssl req ${tpmA} ${tpmB} -provider default ${propA} ${propB} \
|
||||
-config ${cfg} -x509 -section req_server_ca -extensions v3_server_ca \
|
||||
-key ${ca_priv} -CA ${root_cert} \
|
||||
-CAkey ${root_priv} -out ${ca_cert}
|
||||
# generate server cert
|
||||
echo "Generate server"
|
||||
openssl req ${tpmA} ${tpmB} -provider default ${propA} ${propB} \
|
||||
-config ${cfg} -x509 -section req_server -extensions v3_server \
|
||||
-key ${server_priv} -CA ${ca_cert} \
|
||||
-CAkey ${ca_priv} -out ${server_cert}
|
||||
|
||||
# create bundle
|
||||
cat ${server_cert} ${ca_cert} > ${cert_path}
|
||||
}
|
||||
|
||||
generate $base $dir
|
||||
80
tools/EVerest-main/lib/everest/tls/tests/pki/pki.sh
Executable file
80
tools/EVerest-main/lib/everest/tls/tests/pki/pki.sh
Executable file
@@ -0,0 +1,80 @@
|
||||
#!/bin/sh
|
||||
|
||||
generate() {
|
||||
local base="$1"
|
||||
# generate keys
|
||||
for i in "${base}${server_root_priv}" "${base}${server_ca_priv}" "${base}${server_priv}" \
|
||||
"${base}${client_root_priv}" "${base}${client_ca_priv}" "${base}${client_priv}"
|
||||
do
|
||||
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out "$i"
|
||||
chmod 644 "$i"
|
||||
done
|
||||
|
||||
export OPENSSL_CONF="${base}${cfg}"
|
||||
|
||||
echo "Generate ${base}server_root"
|
||||
openssl req \
|
||||
-config "${base}${cfg}" -x509 -section req_server_root -extensions v3_server_root \
|
||||
-key "${base}${server_root_priv}" -out "${base}${server_root_cert}"
|
||||
echo "Generate ${base}server_ca"
|
||||
openssl req \
|
||||
-config "${base}${cfg}" -x509 -section req_server_ca -extensions v3_server_ca \
|
||||
-key "${base}${server_ca_priv}" -CA "${base}${server_root_cert}" \
|
||||
-CAkey "${base}${server_root_priv}" -out "${base}${server_ca_cert}"
|
||||
echo "Generate ${base}server"
|
||||
openssl req \
|
||||
-config "${base}${cfg}" -x509 -section req_server -extensions v3_server \
|
||||
-key "${base}${server_priv}" -CA "${base}${server_ca_cert}" \
|
||||
-CAkey "${base}${server_ca_priv}" -out "${base}${server_cert}"
|
||||
cat "${base}${server_cert}" "${base}${server_ca_cert}" > "${base}${server_chain}"
|
||||
|
||||
echo "Generate ${base}client_root"
|
||||
openssl req \
|
||||
-config "${base}${cfg}" -x509 -section req_client_root -extensions v3_client_root \
|
||||
-key "${base}${client_root_priv}" -out "${base}${client_root_cert}"
|
||||
echo "Generate ${base}client_ca"
|
||||
openssl req \
|
||||
-config "${base}${cfg}" -x509 -section req_client_ca -extensions v3_client_ca \
|
||||
-key "${base}${client_ca_priv}" -CA "${base}${client_root_cert}" \
|
||||
-CAkey "${base}${client_root_priv}" -out "${base}${client_ca_cert}"
|
||||
echo "Generate ${base}client"
|
||||
openssl req \
|
||||
-config "${base}${cfg}" -x509 -section req_client -extensions v3_client \
|
||||
-key "${base}${client_priv}" -CA "${base}${client_ca_cert}" \
|
||||
-CAkey "${base}${client_ca_priv}" -out "${base}${client_cert}"
|
||||
|
||||
cat "${base}${client_cert}" "${base}${client_ca_cert}" > "${base}${client_chain}"
|
||||
}
|
||||
|
||||
cfg=openssl-pki.conf
|
||||
|
||||
server_root_priv=server_root_priv.pem
|
||||
server_ca_priv=server_ca_priv.pem
|
||||
server_priv=server_priv.pem
|
||||
|
||||
server_root_cert=server_root_cert.pem
|
||||
server_ca_cert=server_ca_cert.pem
|
||||
server_cert=server_cert.pem
|
||||
server_chain=server_chain.pem
|
||||
|
||||
client_root_priv=client_root_priv.pem
|
||||
client_ca_priv=client_ca_priv.pem
|
||||
client_priv=client_priv.pem
|
||||
|
||||
client_root_cert=client_root_cert.pem
|
||||
client_ca_cert=client_ca_cert.pem
|
||||
client_cert=client_cert.pem
|
||||
client_chain=client_chain.pem
|
||||
|
||||
generate
|
||||
generate alt_
|
||||
|
||||
# cross signed intermediate certificate
|
||||
echo "Generate cross_ca"
|
||||
openssl req \
|
||||
-config "${cfg}" -x509 -section req_server_ca -extensions v3_server_ca \
|
||||
-key "${base}${server_ca_priv}" -CA "${base}${client_root_cert}" \
|
||||
-CAkey "${base}${client_root_priv}" -out cross_ca_cert.pem
|
||||
|
||||
# convert iso key to PEM
|
||||
openssl asn1parse -genconf iso_pkey.asn1 -noout -out -| openssl pkey -inform der -out iso_priv.pem
|
||||
135
tools/EVerest-main/lib/everest/tls/tests/tls_client_main.cpp
Normal file
135
tools/EVerest-main/lib/everest/tls/tests/tls_client_main.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2024 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include <everest/tls/openssl_util.hpp>
|
||||
#include <everest/tls/tls.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace {
|
||||
const char* short_opts = "h123r:";
|
||||
bool use_tls1_3{false};
|
||||
bool use_status_request{false};
|
||||
bool use_status_request_v2{false};
|
||||
const char* trust_anchor{nullptr};
|
||||
|
||||
void parse_options(int argc, char** argv) {
|
||||
int c;
|
||||
|
||||
while ((c = getopt(argc, argv, short_opts)) != -1) {
|
||||
switch (c) {
|
||||
break;
|
||||
case '1':
|
||||
use_status_request = true;
|
||||
break;
|
||||
case '2':
|
||||
use_status_request_v2 = true;
|
||||
break;
|
||||
case '3':
|
||||
use_tls1_3 = true;
|
||||
break;
|
||||
case 'r':
|
||||
trust_anchor = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
std::cout << "Usage: " << argv[0] << " [-1|-2|-3] [-r server_root_cert.pem]" << std::endl;
|
||||
std::cout << " -1 request status_request" << std::endl;
|
||||
std::cout << " -2 request status_request_v2" << std::endl;
|
||||
std::cout << " -3 use TLS 1.3 (TLS 1.2 otherwise)" << std::endl;
|
||||
std::cout << " -r root certificate / trust anchor" << std::endl;
|
||||
exit(1);
|
||||
break;
|
||||
default:
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
parse_options(argc, argv);
|
||||
|
||||
// used test client for extra output
|
||||
tls::Client client;
|
||||
tls::Client::config_t config;
|
||||
|
||||
if (use_tls1_3) {
|
||||
config.cipher_list = "ECDHE-ECDSA-AES128-SHA256";
|
||||
config.ciphersuites = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384";
|
||||
std::cout << "use_tls1_3 true" << std::endl;
|
||||
} else {
|
||||
config.cipher_list = "ECDHE-ECDSA-AES128-SHA256";
|
||||
config.ciphersuites = ""; // No TLS1.3
|
||||
std::cout << "use_tls1_3 false" << std::endl;
|
||||
}
|
||||
|
||||
config.certificate_chain_file = "client_chain.pem";
|
||||
config.private_key_file = "client_priv.pem";
|
||||
config.verify_locations_file = "server_root_cert.pem";
|
||||
config.io_timeout_ms = 500;
|
||||
config.verify_server = false;
|
||||
|
||||
if (trust_anchor != nullptr) {
|
||||
openssl::sha_1_digest_t digest;
|
||||
auto certs = openssl::load_certificates(trust_anchor);
|
||||
for (const auto& ta : certs) {
|
||||
if (openssl::certificate_sha_1(digest, ta.get())) {
|
||||
config.trusted_ca_keys_data.cert_sha1_hash.push_back(digest);
|
||||
}
|
||||
}
|
||||
config.verify_locations_file = trust_anchor;
|
||||
config.trusted_ca_keys = true;
|
||||
config.trusted_ca_keys_data.pre_agreed = true;
|
||||
}
|
||||
|
||||
if (use_status_request) {
|
||||
config.status_request = true;
|
||||
std::cout << "use_status_request true" << std::endl;
|
||||
} else {
|
||||
config.status_request = false;
|
||||
std::cout << "use_status_request false" << std::endl;
|
||||
}
|
||||
|
||||
if (use_status_request_v2) {
|
||||
config.status_request_v2 = true;
|
||||
std::cout << "use_status_request_v2 true" << std::endl;
|
||||
} else {
|
||||
config.status_request_v2 = false;
|
||||
std::cout << "use_status_request_v2 false" << std::endl;
|
||||
}
|
||||
|
||||
client.init(config);
|
||||
|
||||
// localhost works in some cases but not in the CI pipeline ip6-localhost is an option
|
||||
auto connection = client.connect("localhost", "8444", false, 1000);
|
||||
if (connection) {
|
||||
if (connection->connect() == tls::Connection::result_t::success) {
|
||||
const auto* cert = connection->peer_certificate();
|
||||
if (cert != nullptr) {
|
||||
const auto subject = openssl::certificate_subject(cert);
|
||||
if (!subject.empty()) {
|
||||
std::cout << "subject:";
|
||||
for (const auto& itt : subject) {
|
||||
std::cout << " " << itt.first << ":" << itt.second;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
std::array<std::byte, 1024> buffer{};
|
||||
std::size_t readbytes = 0;
|
||||
std::cout << "about to read" << std::endl;
|
||||
const auto res = connection->read(buffer.data(), buffer.size(), readbytes);
|
||||
std::cout << (int)res << std::endl;
|
||||
std::this_thread::sleep_for(1s);
|
||||
connection->shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
908
tools/EVerest-main/lib/everest/tls/tests/tls_connection_test.cpp
Normal file
908
tools/EVerest-main/lib/everest/tls/tests/tls_connection_test.cpp
Normal file
@@ -0,0 +1,908 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2024 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "tls_connection_test.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <poll.h>
|
||||
#include <thread>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace {
|
||||
using result_t = tls::Connection::result_t;
|
||||
using tls::status_request::ClientStatusRequestV2;
|
||||
|
||||
constexpr auto server_root_CN = "00000000";
|
||||
constexpr auto alt_server_root_CN = "11111111";
|
||||
constexpr auto WAIT_FOR_SERVER_START_TIMEOUT = 50ms;
|
||||
|
||||
void do_poll(std::array<pollfd, 2>& fds, int server_soc, int client_soc) {
|
||||
const std::int16_t events = POLLOUT | POLLIN;
|
||||
fds[0].fd = server_soc;
|
||||
fds[0].events = events;
|
||||
fds[0].revents = 0;
|
||||
fds[1].fd = client_soc;
|
||||
fds[1].events = events;
|
||||
fds[1].revents = 0;
|
||||
auto poll_res = poll(fds.data(), fds.size(), -1);
|
||||
ASSERT_NE(poll_res, -1);
|
||||
}
|
||||
|
||||
tls::Server::OptionalConfig ssl_init() {
|
||||
std::cout << "ssl_init" << std::endl;
|
||||
auto server_config = std::make_unique<tls::Server::config_t>();
|
||||
server_config->cipher_list = "ECDHE-ECDSA-AES128-SHA256";
|
||||
server_config->ciphersuites = "";
|
||||
auto& ref = server_config->chains.emplace_back();
|
||||
ref.certificate_chain_file = "server_chain.pem";
|
||||
ref.private_key_file = "server_priv.pem";
|
||||
ref.trust_anchor_file = "server_root_cert.pem";
|
||||
ref.ocsp_response_files = {"ocsp_response.der", "ocsp_response.der"};
|
||||
server_config->host = "127.0.0.1";
|
||||
server_config->service = "8444";
|
||||
server_config->ipv6_only = false;
|
||||
server_config->verify_client = false;
|
||||
server_config->io_timeout_ms = 500;
|
||||
return {{std::move(server_config)}};
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// The tests
|
||||
|
||||
TEST_F(TlsTest, StartStop) {
|
||||
// test shouldn't hang
|
||||
start();
|
||||
|
||||
// check TearDown on stopped server is okay
|
||||
server.stop();
|
||||
server.wait_stopped();
|
||||
if (server_thread.joinable()) {
|
||||
server_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, StartConnectDisconnect) {
|
||||
// test shouldn't hang
|
||||
start();
|
||||
connect();
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_cb));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_v2));
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, NonBlocking) {
|
||||
client_config.io_timeout_ms = 0;
|
||||
server_config.io_timeout_ms = 0;
|
||||
std::timed_mutex mux;
|
||||
mux.lock();
|
||||
|
||||
tls::Server::ConnectionPtr server_connection;
|
||||
tls::Client::ConnectionPtr client_connection;
|
||||
|
||||
auto server_handler_fn = [&server_connection, &mux](tls::Server::ConnectionPtr&& connection) {
|
||||
server_connection = std::move(connection);
|
||||
mux.unlock();
|
||||
};
|
||||
start(server_handler_fn);
|
||||
|
||||
auto client_handler_fn = [&client_connection](tls::Client::ConnectionPtr& connection) {
|
||||
if (connection) {
|
||||
client_connection = std::move(connection);
|
||||
}
|
||||
};
|
||||
connect(client_handler_fn);
|
||||
|
||||
// FIXME (aw): this is not a proper solution. It would be necessary
|
||||
// to get an exception or result on whether `start()` function has
|
||||
// been successful
|
||||
if (not mux.try_lock_for(WAIT_FOR_SERVER_START_TIMEOUT)) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
// check there is a TCP connection
|
||||
ASSERT_TRUE(server_connection);
|
||||
ASSERT_TRUE(client_connection);
|
||||
|
||||
int server_soc = server_connection->socket();
|
||||
int client_soc = client_connection->socket();
|
||||
std::array<pollfd, 2> fds;
|
||||
|
||||
EXPECT_EQ(server_connection->accept(0), result_t::want_read);
|
||||
EXPECT_EQ(client_connection->connect(0), result_t::want_read);
|
||||
|
||||
bool s_complete{false};
|
||||
bool c_complete{false};
|
||||
std::uint32_t s_count{0};
|
||||
std::uint32_t c_count{0};
|
||||
|
||||
while (!s_complete && !c_complete) {
|
||||
do_poll(fds, server_soc, client_soc);
|
||||
if (((fds[0].revents & POLLIN) != 0) || ((fds[0].revents & POLLOUT) != 0)) {
|
||||
s_complete = server_connection->accept(0) == result_t::success;
|
||||
s_count++;
|
||||
}
|
||||
if (((fds[1].revents & POLLIN) != 0) || ((fds[1].revents & POLLOUT) != 0)) {
|
||||
c_complete = client_connection->connect(0) == result_t::success;
|
||||
c_count++;
|
||||
}
|
||||
|
||||
ASSERT_EQ(fds[0].revents & POLLHUP, 0);
|
||||
ASSERT_EQ(fds[1].revents & POLLHUP, 0);
|
||||
ASSERT_EQ(fds[0].revents & POLLERR, 0);
|
||||
ASSERT_EQ(fds[1].revents & POLLERR, 0);
|
||||
}
|
||||
|
||||
// std::cout << "counts: " << s_count << " " << c_count << std::endl;
|
||||
EXPECT_GT(s_count, 0);
|
||||
EXPECT_GT(c_count, 0);
|
||||
|
||||
const std::byte data{0xf3};
|
||||
|
||||
std::byte s_buf{0};
|
||||
std::size_t s_readbytes{0};
|
||||
std::size_t s_writebytes{0};
|
||||
std::byte c_buf{0};
|
||||
std::size_t c_readbytes{0};
|
||||
std::size_t c_writebytes{0};
|
||||
|
||||
EXPECT_EQ(server_connection->read(&s_buf, sizeof(s_buf), s_readbytes, 0), result_t::want_read);
|
||||
EXPECT_EQ(client_connection->read(&c_buf, sizeof(c_buf), c_readbytes, 0), result_t::want_read);
|
||||
|
||||
EXPECT_EQ(server_connection->write(&data, sizeof(data), s_writebytes, 0), result_t::success);
|
||||
EXPECT_EQ(client_connection->write(&data, sizeof(data), c_writebytes, 0), result_t::success);
|
||||
|
||||
s_complete = false;
|
||||
c_complete = false;
|
||||
s_count = 0;
|
||||
c_count = 0;
|
||||
|
||||
while (!s_complete && !c_complete) {
|
||||
do_poll(fds, server_soc, client_soc);
|
||||
if ((fds[0].revents & POLLIN) != 0) {
|
||||
s_complete = server_connection->read(&s_buf, sizeof(s_buf), s_readbytes, 0) == result_t::success;
|
||||
s_count++;
|
||||
}
|
||||
if ((fds[1].revents & POLLIN) != 0) {
|
||||
c_complete = client_connection->read(&c_buf, sizeof(c_buf), c_readbytes, 0) == result_t::success;
|
||||
c_count++;
|
||||
}
|
||||
|
||||
ASSERT_EQ(fds[0].revents & POLLHUP, 0);
|
||||
ASSERT_EQ(fds[1].revents & POLLHUP, 0);
|
||||
ASSERT_EQ(fds[0].revents & POLLERR, 0);
|
||||
ASSERT_EQ(fds[1].revents & POLLERR, 0);
|
||||
}
|
||||
|
||||
EXPECT_EQ(s_readbytes, 1);
|
||||
EXPECT_EQ(s_buf, data);
|
||||
EXPECT_EQ(c_readbytes, 1);
|
||||
EXPECT_EQ(c_buf, data);
|
||||
|
||||
// std::cout << "counts: " << s_count << " " << c_count << std::endl;
|
||||
EXPECT_GT(s_count, 0);
|
||||
EXPECT_GT(c_count, 0);
|
||||
|
||||
s_complete = false;
|
||||
c_complete = false;
|
||||
s_count = 0;
|
||||
c_count = 0;
|
||||
|
||||
EXPECT_EQ(server_connection->read(&s_buf, sizeof(s_buf), s_readbytes, 0), result_t::want_read);
|
||||
EXPECT_EQ(client_connection->shutdown(0), result_t::closed); // closed
|
||||
while (!s_complete && !c_complete) {
|
||||
do_poll(fds, server_soc, client_soc);
|
||||
if (((fds[0].revents & POLLIN) != 0) || ((fds[0].revents & POLLOUT) != 0)) {
|
||||
s_complete = server_connection->read(&s_buf, sizeof(s_buf), s_readbytes, 0) == result_t::closed;
|
||||
s_count++;
|
||||
}
|
||||
if (((fds[1].revents & POLLIN) != 0) || ((fds[1].revents & POLLOUT) != 0)) {
|
||||
c_complete = client_connection->shutdown(0) == result_t::success;
|
||||
c_count++;
|
||||
}
|
||||
|
||||
ASSERT_EQ(fds[0].revents & POLLERR, 0);
|
||||
ASSERT_EQ(fds[1].revents & POLLERR, 0);
|
||||
}
|
||||
|
||||
// std::cout << "counts: " << s_count << " " << c_count << std::endl;
|
||||
EXPECT_GT(s_count, 0);
|
||||
EXPECT_GT(c_count, 0);
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, NonBlockingClientClose) {
|
||||
std::timed_mutex mux;
|
||||
mux.lock();
|
||||
|
||||
tls::Server::ConnectionPtr server_connection;
|
||||
tls::Client::ConnectionPtr client_connection;
|
||||
|
||||
auto server_handler_fn = [&server_connection, &mux](tls::Server::ConnectionPtr&& connection) {
|
||||
if (connection->accept() == result_t::success) {
|
||||
server_connection = std::move(connection);
|
||||
mux.unlock();
|
||||
}
|
||||
};
|
||||
start(server_handler_fn);
|
||||
|
||||
auto client_handler_fn = [&client_connection](tls::Client::ConnectionPtr& connection) {
|
||||
if (connection) {
|
||||
if (connection->connect() == result_t::success) {
|
||||
client_connection = std::move(connection);
|
||||
}
|
||||
}
|
||||
};
|
||||
connect(client_handler_fn);
|
||||
|
||||
if (not mux.try_lock_for(WAIT_FOR_SERVER_START_TIMEOUT)) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
// check there is a TCP connection
|
||||
ASSERT_TRUE(server_connection);
|
||||
ASSERT_TRUE(client_connection);
|
||||
|
||||
int server_soc = server_connection->socket();
|
||||
int client_soc = client_connection->socket();
|
||||
std::array<pollfd, 2> fds;
|
||||
|
||||
bool s_complete{false};
|
||||
bool c_complete{false};
|
||||
std::uint32_t s_count{0};
|
||||
std::uint32_t c_count{0};
|
||||
|
||||
std::byte buf{0};
|
||||
std::size_t readbytes{0};
|
||||
|
||||
EXPECT_EQ(server_connection->read(&buf, sizeof(buf), readbytes, 0), result_t::want_read);
|
||||
EXPECT_EQ(client_connection->shutdown(0), result_t::closed); // closed
|
||||
while (!s_complete && !c_complete) {
|
||||
do_poll(fds, server_soc, client_soc);
|
||||
if (((fds[0].revents & POLLIN) != 0) || ((fds[0].revents & POLLOUT) != 0)) {
|
||||
s_complete = server_connection->read(&buf, sizeof(buf), readbytes, 0) == result_t::closed;
|
||||
s_count++;
|
||||
}
|
||||
if (((fds[1].revents & POLLIN) != 0) || ((fds[1].revents & POLLOUT) != 0)) {
|
||||
c_complete = client_connection->shutdown(0) == result_t::closed;
|
||||
c_count++;
|
||||
}
|
||||
|
||||
ASSERT_EQ(fds[0].revents & POLLERR, 0);
|
||||
ASSERT_EQ(fds[1].revents & POLLERR, 0);
|
||||
}
|
||||
|
||||
// std::cout << "counts: " << s_count << " " << c_count << std::endl;
|
||||
EXPECT_GT(s_count, 0);
|
||||
EXPECT_GT(c_count, 0);
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, NonBlockingServerClose) {
|
||||
std::timed_mutex mux;
|
||||
mux.lock();
|
||||
|
||||
tls::Server::ConnectionPtr server_connection;
|
||||
tls::Client::ConnectionPtr client_connection;
|
||||
|
||||
auto server_handler_fn = [&server_connection, &mux](tls::Server::ConnectionPtr&& connection) {
|
||||
if (connection->accept() == result_t::success) {
|
||||
server_connection = std::move(connection);
|
||||
mux.unlock();
|
||||
}
|
||||
};
|
||||
start(server_handler_fn);
|
||||
|
||||
auto client_handler_fn = [&client_connection](tls::Client::ConnectionPtr& connection) {
|
||||
if (connection) {
|
||||
if (connection->connect() == result_t::success) {
|
||||
client_connection = std::move(connection);
|
||||
}
|
||||
}
|
||||
};
|
||||
connect(client_handler_fn);
|
||||
|
||||
if (not mux.try_lock_for(WAIT_FOR_SERVER_START_TIMEOUT)) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
// check there is a TCP connection
|
||||
ASSERT_TRUE(server_connection);
|
||||
ASSERT_TRUE(client_connection);
|
||||
|
||||
int server_soc = server_connection->socket();
|
||||
int client_soc = client_connection->socket();
|
||||
std::array<pollfd, 2> fds;
|
||||
|
||||
bool s_complete{false};
|
||||
bool c_complete{false};
|
||||
std::uint32_t s_count{0};
|
||||
std::uint32_t c_count{0};
|
||||
|
||||
std::byte buf{0};
|
||||
std::size_t readbytes{0};
|
||||
|
||||
EXPECT_EQ(server_connection->shutdown(0), result_t::closed); // closed
|
||||
EXPECT_EQ(client_connection->read(&buf, sizeof(buf), readbytes, 0), result_t::want_read);
|
||||
while (!s_complete && !c_complete) {
|
||||
do_poll(fds, server_soc, client_soc);
|
||||
if (((fds[0].revents & POLLIN) != 0) || ((fds[0].revents & POLLOUT) != 0)) {
|
||||
s_complete = server_connection->shutdown(0) == result_t::closed;
|
||||
s_count++;
|
||||
}
|
||||
if (((fds[1].revents & POLLIN) != 0) || ((fds[1].revents & POLLOUT) != 0)) {
|
||||
c_complete = client_connection->read(&buf, sizeof(buf), readbytes, 0) == result_t::closed;
|
||||
c_count++;
|
||||
}
|
||||
|
||||
ASSERT_EQ(fds[0].revents & POLLERR, 0);
|
||||
ASSERT_EQ(fds[1].revents & POLLERR, 0);
|
||||
}
|
||||
|
||||
// std::cout << "counts: " << s_count << " " << c_count << std::endl;
|
||||
EXPECT_GT(s_count, 0);
|
||||
EXPECT_GT(c_count, 0);
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, ClientReadTimeout) {
|
||||
// test shouldn't hang
|
||||
client_config.io_timeout_ms = 50;
|
||||
|
||||
auto client_handler_fn = [this](tls::Client::ConnectionPtr& connection) {
|
||||
if (connection) {
|
||||
if (connection->connect() == result_t::success) {
|
||||
this->set(ClientTest::flags_t::connected);
|
||||
std::byte buffer{};
|
||||
std::size_t readbytes{0};
|
||||
auto res = connection->read(&buffer, sizeof(buffer), readbytes);
|
||||
EXPECT_EQ(readbytes, 0);
|
||||
EXPECT_EQ(res, result_t::timeout);
|
||||
if (res != result_t::closed) {
|
||||
connection->shutdown();
|
||||
}
|
||||
connection->shutdown();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
start();
|
||||
connect(client_handler_fn);
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_cb));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_v2));
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, ClientWriteTimeout) {
|
||||
// test shouldn't hang
|
||||
client_config.io_timeout_ms = 50;
|
||||
|
||||
bool did_timeout{false};
|
||||
std::size_t count{0};
|
||||
std::mutex mux;
|
||||
mux.lock();
|
||||
|
||||
constexpr std::size_t max_bytes = 1024 * 1024 * 1024;
|
||||
|
||||
auto server_handler_fn = [&mux](tls::Server::ConnectionPtr&& con) {
|
||||
if (con->accept() == result_t::success) {
|
||||
mux.lock();
|
||||
con->shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
auto client_handler_fn = [this, &mux, &did_timeout, &count](tls::Client::ConnectionPtr& connection) {
|
||||
if (connection) {
|
||||
if (connection->connect() == result_t::success) {
|
||||
this->set(ClientTest::flags_t::connected);
|
||||
std::array<std::byte, 1024> buffer{};
|
||||
std::size_t writebytes{0};
|
||||
|
||||
bool exit{false};
|
||||
|
||||
while (!exit) {
|
||||
switch (connection->write(buffer.data(), buffer.size(), writebytes)) {
|
||||
case result_t::success:
|
||||
count += writebytes;
|
||||
exit = count > max_bytes;
|
||||
break;
|
||||
case result_t::timeout:
|
||||
// std::cout << "timeout: " << count << " bytes" << std::endl;
|
||||
did_timeout = true;
|
||||
exit = true;
|
||||
break;
|
||||
case result_t::closed:
|
||||
default:
|
||||
exit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mux.unlock();
|
||||
std::size_t readbytes = 0;
|
||||
auto res = connection->read(buffer.data(), buffer.size(), readbytes);
|
||||
if (res != result_t::closed) {
|
||||
connection->shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
start(server_handler_fn);
|
||||
connect(client_handler_fn);
|
||||
EXPECT_TRUE(did_timeout);
|
||||
EXPECT_LE(count, max_bytes);
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_cb));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_v2));
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, ServerReadTimeout) {
|
||||
// test shouldn't hang
|
||||
bool did_timeout{false};
|
||||
std::mutex mux;
|
||||
mux.lock();
|
||||
|
||||
auto server_handler_fn = [&mux, &did_timeout](tls::Server::ConnectionPtr&& con) {
|
||||
if (con->accept() == result_t::success) {
|
||||
std::array<std::byte, 1024> buffer{};
|
||||
std::size_t readbytes = 0;
|
||||
auto res = con->read(buffer.data(), buffer.size(), readbytes);
|
||||
did_timeout = res == result_t::timeout;
|
||||
mux.unlock();
|
||||
con->shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
auto client_handler_fn = [this, &mux](tls::Client::ConnectionPtr& connection) {
|
||||
if (connection) {
|
||||
if (connection->connect() == result_t::success) {
|
||||
this->set(ClientTest::flags_t::connected);
|
||||
mux.lock();
|
||||
connection->shutdown();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
start(server_handler_fn);
|
||||
connect(client_handler_fn);
|
||||
EXPECT_TRUE(did_timeout);
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_cb));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_v2));
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, ServerWriteTimeout) {
|
||||
// test shouldn't hang
|
||||
bool did_timeout{false};
|
||||
std::size_t count{0};
|
||||
std::mutex mux;
|
||||
mux.lock();
|
||||
|
||||
constexpr std::size_t max_bytes = 1024 * 1024 * 1024;
|
||||
|
||||
auto server_handler_fn = [&mux, &did_timeout, &count](tls::Server::ConnectionPtr&& con) {
|
||||
if (con->accept() == result_t::success) {
|
||||
std::array<std::byte, 1024> buffer{};
|
||||
std::size_t writebytes{0};
|
||||
|
||||
bool exit{false};
|
||||
|
||||
while (!exit) {
|
||||
switch (con->write(buffer.data(), buffer.size(), writebytes)) {
|
||||
case result_t::success:
|
||||
count += writebytes;
|
||||
exit = count > max_bytes;
|
||||
break;
|
||||
case result_t::timeout:
|
||||
// std::cout << "timeout: " << count << " bytes" << std::endl;
|
||||
did_timeout = true;
|
||||
exit = true;
|
||||
break;
|
||||
case result_t::closed:
|
||||
default:
|
||||
exit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mux.unlock();
|
||||
std::size_t readbytes = 0;
|
||||
auto res = con->read(buffer.data(), buffer.size(), readbytes);
|
||||
if (res != result_t::closed) {
|
||||
con->shutdown();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto client_handler_fn = [this, &mux](tls::Client::ConnectionPtr& connection) {
|
||||
if (connection) {
|
||||
if (connection->connect() == result_t::success) {
|
||||
this->set(ClientTest::flags_t::connected);
|
||||
}
|
||||
mux.lock();
|
||||
connection->shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
start(server_handler_fn);
|
||||
connect(client_handler_fn);
|
||||
EXPECT_TRUE(did_timeout);
|
||||
EXPECT_LE(count, max_bytes);
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_cb));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_v2));
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, delayedConfig) {
|
||||
// partial config
|
||||
server_config.chains.clear();
|
||||
|
||||
start(ssl_init);
|
||||
connect();
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_cb));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_v2));
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, partialConfig) {
|
||||
// partial config - no support for trusted_ca_keys
|
||||
for (auto& i : server_config.chains) {
|
||||
i.trust_anchor_file = nullptr;
|
||||
}
|
||||
|
||||
start();
|
||||
connect();
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_cb));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_v2));
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, TLS13) {
|
||||
// test using TLS 1.3
|
||||
// there shouldn't be status_request_v2 responses
|
||||
// TLS 1.3 still supports status_request however it is handled differently
|
||||
// (which is handled within the OpenSSL API)
|
||||
server_config.ciphersuites = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384";
|
||||
start();
|
||||
connect();
|
||||
// no status requested
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_cb));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_v2));
|
||||
|
||||
client_config.status_request = true;
|
||||
connect();
|
||||
// status_request only
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(is_set(flags_t::status_request_cb));
|
||||
EXPECT_TRUE(is_set(flags_t::status_request));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_v2));
|
||||
|
||||
client_config.status_request = false;
|
||||
client_config.status_request_v2 = true;
|
||||
connect();
|
||||
// status_request_v2 only - ignored by server
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(is_set(flags_t::status_request_cb));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_v2));
|
||||
|
||||
client_config.status_request = true;
|
||||
connect();
|
||||
// status_request and status_request_v2
|
||||
// status_request_v2 is ignored by server and status_request used
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(is_set(flags_t::status_request_cb));
|
||||
EXPECT_TRUE(is_set(flags_t::status_request));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_v2));
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, NoOcspFiles) {
|
||||
// test using TLS 1.2
|
||||
for (auto& chain : server_config.chains) {
|
||||
chain.ocsp_response_files.clear();
|
||||
}
|
||||
|
||||
start();
|
||||
connect();
|
||||
// no status requested
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_cb));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_v2));
|
||||
|
||||
client_config.status_request = true;
|
||||
connect();
|
||||
// status_request only
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(is_set(flags_t::status_request_cb));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_v2));
|
||||
|
||||
client_config.status_request = false;
|
||||
client_config.status_request_v2 = true;
|
||||
connect();
|
||||
// status_request_v2 only
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(is_set(flags_t::status_request_cb));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_v2));
|
||||
|
||||
client_config.status_request = true;
|
||||
connect();
|
||||
// status_request and status_request_v2
|
||||
// status_request_v2 is preferred over status_request
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(is_set(flags_t::status_request_cb));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_v2));
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, CertVerify) {
|
||||
client_config.verify_locations_file = "alt_server_root_cert.pem";
|
||||
start();
|
||||
connect();
|
||||
EXPECT_FALSE(is_set(flags_t::connected));
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, TCKeysNone) {
|
||||
// trusted_ca_keys - none match - default certificate should be used
|
||||
std::map<std::string, std::string> subject;
|
||||
|
||||
client_config.trusted_ca_keys = true;
|
||||
client_config.trusted_ca_keys_data.pre_agreed = true;
|
||||
add_ta_cert_hash("client_root_cert.pem");
|
||||
add_ta_key_hash("client_root_cert.pem");
|
||||
add_ta_name("client_root_cert.pem");
|
||||
|
||||
auto client_handler_fn = [this, &subject](tls::Client::ConnectionPtr& connection) {
|
||||
if (connection) {
|
||||
if (connection->connect() == result_t::success) {
|
||||
this->set(ClientTest::flags_t::connected);
|
||||
subject = openssl::certificate_subject(connection->peer_certificate());
|
||||
connection->shutdown();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
start();
|
||||
connect(client_handler_fn);
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_EQ(subject["CN"], server_root_CN);
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, TCKeysCert) {
|
||||
// trusted_ca_keys - cert hash matches
|
||||
std::map<std::string, std::string> subject;
|
||||
|
||||
client_config.trusted_ca_keys = true;
|
||||
client_config.verify_locations_file = "alt_server_root_cert.pem";
|
||||
add_ta_cert_hash("alt_server_root_cert.pem");
|
||||
|
||||
auto client_handler_fn = [this, &subject](tls::Client::ConnectionPtr& connection) {
|
||||
if (connection) {
|
||||
if (connection->connect() == result_t::success) {
|
||||
this->set(ClientTest::flags_t::connected);
|
||||
subject = openssl::certificate_subject(connection->peer_certificate());
|
||||
connection->shutdown();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
start();
|
||||
connect(client_handler_fn);
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_EQ(subject["CN"], alt_server_root_CN);
|
||||
|
||||
client_config.trusted_ca_keys_data.x509_name.clear();
|
||||
add_ta_cert_hash("client_root_cert.pem");
|
||||
add_ta_cert_hash("alt_server_root_cert.pem");
|
||||
|
||||
connect(client_handler_fn);
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_EQ(subject["CN"], alt_server_root_CN);
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, TCKeysKey) {
|
||||
// trusted_ca_keys - key hash matches
|
||||
std::map<std::string, std::string> subject;
|
||||
|
||||
client_config.trusted_ca_keys = true;
|
||||
client_config.verify_locations_file = "alt_server_root_cert.pem";
|
||||
add_ta_key_hash("alt_server_root_cert.pem");
|
||||
|
||||
auto client_handler_fn = [this, &subject](tls::Client::ConnectionPtr& connection) {
|
||||
if (connection) {
|
||||
if (connection->connect() == result_t::success) {
|
||||
this->set(ClientTest::flags_t::connected);
|
||||
subject = openssl::certificate_subject(connection->peer_certificate());
|
||||
connection->shutdown();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
start();
|
||||
connect(client_handler_fn);
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_EQ(subject["CN"], alt_server_root_CN);
|
||||
|
||||
client_config.trusted_ca_keys_data.x509_name.clear();
|
||||
add_ta_key_hash("client_root_cert.pem");
|
||||
add_ta_key_hash("alt_server_root_cert.pem");
|
||||
|
||||
connect(client_handler_fn);
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_EQ(subject["CN"], alt_server_root_CN);
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, TCKeysKeyPem) {
|
||||
// same as TCKeysKey but using a PEM string trust anchor rather than file
|
||||
std::map<std::string, std::string> subject;
|
||||
|
||||
client_config.trusted_ca_keys = true;
|
||||
client_config.verify_locations_file = "alt_server_root_cert.pem";
|
||||
add_ta_key_hash("alt_server_root_cert.pem");
|
||||
|
||||
auto client_handler_fn = [this, &subject](tls::Client::ConnectionPtr& connection) {
|
||||
if (connection) {
|
||||
if (connection->connect() == result_t::success) {
|
||||
this->set(ClientTest::flags_t::connected);
|
||||
subject = openssl::certificate_subject(connection->peer_certificate());
|
||||
connection->shutdown();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// convert file to PEM in config
|
||||
for (auto& cfg : server_config.chains) {
|
||||
const auto certs = ::openssl::load_certificates(cfg.trust_anchor_file);
|
||||
std::string pem;
|
||||
for (const auto& cert : certs) {
|
||||
pem += ::openssl::certificate_to_pem(cert.get());
|
||||
}
|
||||
// std::cout << cfg.trust_anchor_file << ": " << certs.size() << std::endl;
|
||||
ASSERT_FALSE(pem.empty());
|
||||
cfg.trust_anchor_file = nullptr;
|
||||
cfg.trust_anchor_pem = pem.c_str();
|
||||
}
|
||||
|
||||
start();
|
||||
connect(client_handler_fn);
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_EQ(subject["CN"], alt_server_root_CN);
|
||||
|
||||
client_config.trusted_ca_keys_data.x509_name.clear();
|
||||
add_ta_key_hash("client_root_cert.pem");
|
||||
add_ta_key_hash("alt_server_root_cert.pem");
|
||||
|
||||
connect(client_handler_fn);
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_EQ(subject["CN"], alt_server_root_CN);
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, TCKeysName) {
|
||||
// trusted_ca_keys - subject name matches
|
||||
std::map<std::string, std::string> subject;
|
||||
|
||||
client_config.trusted_ca_keys = true;
|
||||
client_config.verify_locations_file = "alt_server_root_cert.pem";
|
||||
add_ta_name("alt_server_root_cert.pem");
|
||||
|
||||
auto client_handler_fn = [this, &subject](tls::Client::ConnectionPtr& connection) {
|
||||
if (connection) {
|
||||
if (connection->connect() == result_t::success) {
|
||||
this->set(ClientTest::flags_t::connected);
|
||||
subject = openssl::certificate_subject(connection->peer_certificate());
|
||||
connection->shutdown();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
start();
|
||||
connect(client_handler_fn);
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_EQ(subject["CN"], alt_server_root_CN);
|
||||
|
||||
client_config.trusted_ca_keys_data.x509_name.clear();
|
||||
add_ta_name("client_root_cert.pem");
|
||||
add_ta_name("alt_server_root_cert.pem");
|
||||
|
||||
connect(client_handler_fn);
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_EQ(subject["CN"], alt_server_root_CN);
|
||||
}
|
||||
|
||||
// based on an example seen in a WireShark log
|
||||
// (invalid missing the size of trusted_authorities_list)
|
||||
// 01 identifier_type key_sha1_hash 4cd7290bf592d2c1ba90f56e08946d4c8e99dc38 SHA1Hash
|
||||
// 01 identifier_type key_sha1_hash 00fae3900795c888a4d4d7bd9fdffa60418ac19f SHA1Hash
|
||||
int trusted_ca_keys_add_bad(SSL* ctx, unsigned int ext_type, unsigned int context, const unsigned char** out,
|
||||
std::size_t* outlen, X509* cert, std::size_t chainidx, int* alert, void* object) {
|
||||
// std::cout << "trusted_ca_keys_add_bad" << std::endl;
|
||||
int result{0};
|
||||
if ((context == SSL_EXT_CLIENT_HELLO) && (object != nullptr)) {
|
||||
constexpr std::uint8_t value[] = {
|
||||
0x01, 0x4c, 0xd7, 0x29, 0x0b, 0xf5, 0x92, 0xd2, 0xc1, 0xba, 0x90, 0xf5, 0x6e, 0x08,
|
||||
0x94, 0x6d, 0x4c, 0x8e, 0x99, 0xdc, 0x38, 0x01, 0x00, 0xfa, 0xe3, 0x90, 0x07, 0x95,
|
||||
0xc8, 0x88, 0xa4, 0xd4, 0xd7, 0xbd, 0x9f, 0xdf, 0xfa, 0x60, 0x41, 0x8a, 0xc1, 0x9f,
|
||||
};
|
||||
openssl::DER der(&value[0], sizeof(value));
|
||||
const auto len = der.size();
|
||||
auto* ptr = openssl::DER::dup(der);
|
||||
if (ptr != nullptr) {
|
||||
*out = ptr;
|
||||
*outlen = len;
|
||||
result = 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, TCKeysInvalid) {
|
||||
// trusted_ca_keys - incorrectly formatted extension, connect using defaults
|
||||
std::map<std::string, std::string> subject;
|
||||
|
||||
client_config.trusted_ca_keys = true;
|
||||
client_config.verify_locations_file = "server_root_cert.pem";
|
||||
|
||||
auto override = tls::Client::default_overrides();
|
||||
override.trusted_ca_keys_add = &trusted_ca_keys_add_bad;
|
||||
|
||||
start();
|
||||
client.init(client_config, override);
|
||||
client.reset();
|
||||
// localhost works in some cases but not in the CI pipeline for IPv6
|
||||
// use ip6-localhost
|
||||
auto connection = client.connect("127.0.0.1", "8444", false, 1000);
|
||||
if (connection) {
|
||||
if (connection->connect() == result_t::success) {
|
||||
set(ClientTest::flags_t::connected);
|
||||
subject = openssl::certificate_subject(connection->peer_certificate());
|
||||
connection->shutdown();
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_EQ(subject["CN"], server_root_CN);
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, Suspend) {
|
||||
using state_t = tls::Server::state_t;
|
||||
start();
|
||||
EXPECT_EQ(server.state(), state_t::running);
|
||||
connect();
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(server.suspend());
|
||||
EXPECT_EQ(server.state(), state_t::init_socket);
|
||||
connect();
|
||||
EXPECT_FALSE(is_set(flags_t::connected));
|
||||
EXPECT_EQ(server.state(), state_t::init_socket);
|
||||
EXPECT_TRUE(server.update(server_config));
|
||||
EXPECT_EQ(server.state(), state_t::init_complete);
|
||||
connect();
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_EQ(server.state(), state_t::running);
|
||||
}
|
||||
|
||||
TEST_F(TlsTest, SuspendToRunning) {
|
||||
using state_t = tls::Server::state_t;
|
||||
start();
|
||||
EXPECT_EQ(server.state(), state_t::running);
|
||||
connect();
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(server.suspend());
|
||||
EXPECT_EQ(server.state(), state_t::init_socket);
|
||||
connect();
|
||||
EXPECT_FALSE(is_set(flags_t::connected));
|
||||
EXPECT_EQ(server.state(), state_t::init_socket);
|
||||
EXPECT_TRUE(server.update(server_config));
|
||||
EXPECT_EQ(server.state(), state_t::init_complete);
|
||||
// should switch to running after a timeout
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(tls::c_serve_timeout_ms + 100));
|
||||
EXPECT_EQ(server.state(), state_t::running);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
323
tools/EVerest-main/lib/everest/tls/tests/tls_connection_test.hpp
Normal file
323
tools/EVerest-main/lib/everest/tls/tests/tls_connection_test.hpp
Normal file
@@ -0,0 +1,323 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2024 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef TLS_CONNECTION_TEST_HPP_
|
||||
#define TLS_CONNECTION_TEST_HPP_
|
||||
|
||||
#include "extensions/helpers.hpp"
|
||||
#include <everest/tls/openssl_util.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <csignal>
|
||||
#include <everest/tls/tls.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
#include <memory>
|
||||
#include <openssl/ssl.h>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <everest/util/enum/EnumFlags.hpp>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace {
|
||||
using tls::status_request::ClientStatusRequestV2;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// set up code
|
||||
|
||||
struct ClientStatusRequestV2Test : public ClientStatusRequestV2 {
|
||||
enum class flags_t : std::uint8_t {
|
||||
status_request_cb,
|
||||
status_request,
|
||||
status_request_v2,
|
||||
connected,
|
||||
last = connected,
|
||||
};
|
||||
|
||||
everest::lib::util::AtomicEnumFlags<flags_t>& flags;
|
||||
|
||||
ClientStatusRequestV2Test() = delete;
|
||||
explicit ClientStatusRequestV2Test(everest::lib::util::AtomicEnumFlags<flags_t>& flag_ref) : flags(flag_ref) {
|
||||
}
|
||||
|
||||
int status_request_cb(tls::Ssl* ctx) override {
|
||||
/*
|
||||
* This callback is called when status_request or status_request_v2 extensions
|
||||
* were present in the Client Hello. It doesn't mean that the extension is in
|
||||
* the Server Hello SSL_get_tlsext_status_ocsp_resp() returns -1 in that case
|
||||
*/
|
||||
|
||||
int result{1};
|
||||
const unsigned char* response{nullptr};
|
||||
const auto total_length = SSL_get_tlsext_status_ocsp_resp(ctx, &response);
|
||||
flags.set(flags_t::status_request_cb);
|
||||
if ((response != nullptr) && (total_length > 0) && (total_length <= std::numeric_limits<std::int32_t>::max())) {
|
||||
switch (response[0]) {
|
||||
case 0x30: // a status_request response
|
||||
flags.set(flags_t::status_request);
|
||||
if (!print_ocsp_response(stdout, response, total_length)) {
|
||||
result = 0;
|
||||
}
|
||||
break;
|
||||
case 0x00: // a status_request_v2 response
|
||||
{
|
||||
flags.set(flags_t::status_request_v2);
|
||||
|
||||
// multiple responses
|
||||
auto remaining = static_cast<std::int32_t>(total_length);
|
||||
const unsigned char* ptr{response};
|
||||
|
||||
while (remaining >= 3) {
|
||||
const auto len = tls::uint24(ptr);
|
||||
tls::update_position(ptr, remaining, 3);
|
||||
// print_ocsp_response updates tmp_p
|
||||
auto* tmp_p = ptr;
|
||||
const auto res = print_ocsp_response(stdout, tmp_p, len);
|
||||
tls::update_position(ptr, remaining, len);
|
||||
if (!res || (ptr != tmp_p)) {
|
||||
result = 0;
|
||||
remaining = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (remaining != 0) {
|
||||
result = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
struct ClientTest : public tls::Client {
|
||||
using flags_t = ClientStatusRequestV2Test::flags_t;
|
||||
everest::lib::util::AtomicEnumFlags<flags_t> flags;
|
||||
|
||||
ClientTest() : tls::Client(std::unique_ptr<ClientStatusRequestV2>(new ClientStatusRequestV2Test(flags))) {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
flags.reset();
|
||||
}
|
||||
};
|
||||
|
||||
void handler(tls::Server::ConnectionPtr&& con) {
|
||||
if (con->accept() == tls::Connection::result_t::success) {
|
||||
std::uint32_t count{0};
|
||||
std::array<std::byte, 1024> buffer{};
|
||||
bool bExit = false;
|
||||
while (!bExit) {
|
||||
std::size_t readbytes = 0;
|
||||
std::size_t writebytes = 0;
|
||||
|
||||
switch (con->read(buffer.data(), buffer.size(), readbytes)) {
|
||||
case tls::Connection::result_t::success:
|
||||
switch (con->write(buffer.data(), readbytes, writebytes)) {
|
||||
case tls::Connection::result_t::success:
|
||||
break;
|
||||
case tls::Connection::result_t::timeout:
|
||||
case tls::Connection::result_t::closed:
|
||||
default:
|
||||
bExit = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case tls::Connection::result_t::timeout:
|
||||
count++;
|
||||
if (count > 10) {
|
||||
bExit = true;
|
||||
}
|
||||
break;
|
||||
case tls::Connection::result_t::closed:
|
||||
default:
|
||||
bExit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
con->shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void run_server(tls::Server& server) {
|
||||
server.serve(&handler);
|
||||
}
|
||||
|
||||
class TlsTest : public testing::Test {
|
||||
protected:
|
||||
using flags_t = ClientTest::flags_t;
|
||||
|
||||
tls::Server server;
|
||||
tls::Server::config_t server_config;
|
||||
std::thread server_thread;
|
||||
ClientTest client;
|
||||
tls::Client::config_t client_config;
|
||||
|
||||
static void SetUpTestSuite() {
|
||||
struct sigaction action;
|
||||
std::memset(&action, 0, sizeof(action));
|
||||
action.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &action, nullptr);
|
||||
tls::Server::configure_signal_handler(SIGUSR1);
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
server_config.cipher_list = "ECDHE-ECDSA-AES128-SHA256";
|
||||
// server_config.ciphersuites = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384";
|
||||
server_config.ciphersuites = "";
|
||||
auto& ref0 = server_config.chains.emplace_back();
|
||||
ref0.certificate_chain_file = "server_chain.pem";
|
||||
ref0.private_key_file = "server_priv.pem";
|
||||
ref0.trust_anchor_file = "server_root_cert.pem";
|
||||
ref0.ocsp_response_files = {"ocsp_response.der", "ocsp_response.der"};
|
||||
auto& ref1 = server_config.chains.emplace_back();
|
||||
ref1.certificate_chain_file = "alt_server_chain.pem";
|
||||
ref1.private_key_file = "alt_server_priv.pem";
|
||||
ref1.trust_anchor_file = "alt_server_root_cert.pem";
|
||||
ref1.ocsp_response_files = {"ocsp_response.der", "ocsp_response.der"};
|
||||
// server_config.verify_locations_file = "client_root_cert.pem";
|
||||
server_config.host = "127.0.0.1";
|
||||
server_config.service = "8444";
|
||||
server_config.ipv6_only = false;
|
||||
server_config.verify_client = false;
|
||||
server_config.io_timeout_ms = 1000; // no lower than 200ms, valgrind need much higher
|
||||
|
||||
client_config.cipher_list = "ECDHE-ECDSA-AES128-SHA256";
|
||||
// client_config.ciphersuites = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384";
|
||||
// client_config.certificate_chain_file = "client_chain.pem";
|
||||
// client_config.private_key_file = "client_priv.pem";
|
||||
client_config.verify_locations_file = "server_root_cert.pem";
|
||||
client_config.io_timeout_ms = 1000;
|
||||
client_config.verify_server = true;
|
||||
client_config.status_request = false;
|
||||
client_config.status_request_v2 = false;
|
||||
client.reset();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
server.stop();
|
||||
server.wait_stopped();
|
||||
if (server_thread.joinable()) {
|
||||
server_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void start(const tls::Server::ConfigurationCallback& init_ssl = nullptr) {
|
||||
using state_t = tls::Server::state_t;
|
||||
const auto res = server.init(server_config, init_ssl);
|
||||
if ((res == state_t::init_complete) || (res == state_t::init_socket)) {
|
||||
server_thread = std::thread(&run_server, std::ref(server));
|
||||
server.wait_running();
|
||||
}
|
||||
}
|
||||
|
||||
void start(const std::function<void(tls::Server::ConnectionPtr&& con)>& handler) {
|
||||
using state_t = tls::Server::state_t;
|
||||
const auto res = server.init(server_config, nullptr);
|
||||
if ((res == state_t::init_complete) || (res == state_t::init_socket)) {
|
||||
server_thread = std::thread([this, handler]() { this->server.serve(handler); });
|
||||
server.wait_running();
|
||||
}
|
||||
}
|
||||
|
||||
void connect(const std::function<void(tls::Client::ConnectionPtr& con)>& handler = nullptr) {
|
||||
client.init(client_config);
|
||||
client.reset();
|
||||
// localhost works in some cases but not in the CI pipeline for IPv6
|
||||
// use ip6-localhost
|
||||
auto connection = client.connect("127.0.0.1", "8444", false, 1000);
|
||||
if (handler == nullptr) {
|
||||
if (connection) {
|
||||
if (connection->connect() == tls::Connection::result_t::success) {
|
||||
set(ClientTest::flags_t::connected);
|
||||
connection->shutdown();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
handler(connection);
|
||||
}
|
||||
}
|
||||
|
||||
void set(flags_t flag) {
|
||||
client.flags.set(flag);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool is_set(flags_t flag) const {
|
||||
return client.flags.is_set(flag);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool is_reset(flags_t flag) const {
|
||||
return client.flags.is_reset(flag);
|
||||
}
|
||||
|
||||
void add_ta_cert_hash(const char* filename) {
|
||||
openssl::sha_1_digest_t digest;
|
||||
auto certs = openssl::load_certificates(filename);
|
||||
for (const auto& ta : certs) {
|
||||
if (openssl::certificate_sha_1(digest, ta.get())) {
|
||||
client_config.trusted_ca_keys_data.cert_sha1_hash.push_back(digest);
|
||||
}
|
||||
}
|
||||
}
|
||||
void add_ta_key_hash(const char* filename) {
|
||||
openssl::sha_1_digest_t digest;
|
||||
auto certs = openssl::load_certificates(filename);
|
||||
for (const auto& ta : certs) {
|
||||
if (openssl::certificate_subject_public_key_sha_1(digest, ta.get())) {
|
||||
client_config.trusted_ca_keys_data.key_sha1_hash.push_back(digest);
|
||||
}
|
||||
}
|
||||
}
|
||||
void add_ta_name(const char* filename) {
|
||||
auto certs = openssl::load_certificates(filename);
|
||||
for (const auto& ta : certs) {
|
||||
auto res = openssl::certificate_subject_der(ta.get());
|
||||
if (res) {
|
||||
client_config.trusted_ca_keys_data.x509_name.emplace_back(std::move(res));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class TlsTestTpm : public TlsTest {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
server_config.cipher_list = "ECDHE-ECDSA-AES128-SHA256";
|
||||
// server_config.ciphersuites = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384";
|
||||
server_config.ciphersuites = "";
|
||||
auto& ref0 = server_config.chains.emplace_back();
|
||||
ref0.certificate_chain_file = "tpm_pki/server_chain.pem";
|
||||
ref0.private_key_file = "tpm_pki/server_priv.pem";
|
||||
ref0.trust_anchor_file = "tpm_pki/server_root_cert.pem";
|
||||
ref0.ocsp_response_files = {"ocsp_response.der", "ocsp_response.der"};
|
||||
// auto& ref1 = server_config.chains.emplace_back();
|
||||
// ref1.certificate_chain_file = "alt_server_chain.pem";
|
||||
// ref1.private_key_file = "alt_server_priv.pem";
|
||||
// ref1.trust_anchor_file = "alt_server_root_cert.pem";
|
||||
// ref1.ocsp_response_files = {"ocsp_response.der", "ocsp_response.der"};
|
||||
server_config.host = "127.0.0.1";
|
||||
server_config.service = "8444";
|
||||
server_config.ipv6_only = false;
|
||||
server_config.verify_client = false;
|
||||
server_config.io_timeout_ms = 1000; // no lower than 200ms, valgrind need much higher
|
||||
|
||||
client_config.cipher_list = "ECDHE-ECDSA-AES128-SHA256";
|
||||
// client_config.ciphersuites = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384";
|
||||
// client_config.certificate_chain_file = "client_chain.pem";
|
||||
// client_config.private_key_file = "client_priv.pem";
|
||||
client_config.verify_locations_file = "tpm_pki/server_root_cert.pem";
|
||||
client_config.io_timeout_ms = 1000;
|
||||
client_config.verify_server = true;
|
||||
client_config.status_request = false;
|
||||
client_config.status_request_v2 = false;
|
||||
client.reset();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
#endif // TLS_CONNECTION_TEST_HPP_
|
||||
@@ -0,0 +1,34 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2024 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "tls_connection_test.hpp"
|
||||
#include <everest/tls/openssl_util.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <poll.h>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace {
|
||||
using result_t = tls::Connection::result_t;
|
||||
using tls::status_request::ClientStatusRequestV2;
|
||||
|
||||
constexpr auto server_root_CN = "00000000";
|
||||
constexpr auto alt_server_root_CN = "11111111";
|
||||
constexpr auto WAIT_FOR_SERVER_START_TIMEOUT = 50ms;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// The tests
|
||||
|
||||
TEST_F(TlsTestTpm, StartConnectDisconnect) {
|
||||
start();
|
||||
connect();
|
||||
// no status requested
|
||||
EXPECT_TRUE(is_set(flags_t::connected));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_cb));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request));
|
||||
EXPECT_TRUE(is_reset(flags_t::status_request_v2));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
159
tools/EVerest-main/lib/everest/tls/tests/tls_main.cpp
Normal file
159
tools/EVerest-main/lib/everest/tls/tests/tls_main.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2024 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
/*
|
||||
* testing options
|
||||
* openssl s_client -connect localhost:8444 -verify 2 -CAfile server_root_cert.pem -cert client_cert.pem -cert_chain
|
||||
* client_chain.pem -key client_priv.pem -verify_return_error -verify_hostname evse.pionix.de -status
|
||||
*/
|
||||
|
||||
#include <everest/tls/tls.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <csignal>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace {
|
||||
|
||||
const char* short_opts = "ch36t";
|
||||
bool disable_tls1_3{false};
|
||||
bool enable_tpm{false};
|
||||
bool ipv6_only{false};
|
||||
bool verify_client{false};
|
||||
|
||||
void parse_options(int argc, char** argv) {
|
||||
int c;
|
||||
|
||||
while ((c = getopt(argc, argv, short_opts)) != -1) {
|
||||
switch (c) {
|
||||
break;
|
||||
case 'c':
|
||||
verify_client = true;
|
||||
break;
|
||||
case '3':
|
||||
disable_tls1_3 = true;
|
||||
break;
|
||||
case '6':
|
||||
ipv6_only = true;
|
||||
break;
|
||||
case 't':
|
||||
enable_tpm = true;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
std::cout << "Usage: " << argv[0] << " [-c|-3|-6]" << std::endl;
|
||||
std::cout << " -c verify client" << std::endl;
|
||||
std::cout << " -3 disable TLS 1.3" << std::endl;
|
||||
std::cout << " -6 IPv6 only" << std::endl;
|
||||
std::cout << " -t enable TPM" << std::endl;
|
||||
exit(1);
|
||||
break;
|
||||
default:
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handle_connection(tls::Server::ConnectionPtr&& con) {
|
||||
std::cout << "Connection" << std::endl;
|
||||
if (con->accept() == tls::Connection::result_t::success) {
|
||||
std::uint32_t count{0};
|
||||
std::array<std::byte, 1024> buffer{};
|
||||
bool bExit = false;
|
||||
while (!bExit) {
|
||||
std::size_t readbytes = 0;
|
||||
std::size_t writebytes = 0;
|
||||
|
||||
switch (con->read(buffer.data(), buffer.size(), readbytes)) {
|
||||
case tls::Connection::result_t::success:
|
||||
switch (con->write(buffer.data(), readbytes, writebytes)) {
|
||||
case tls::Connection::result_t::success:
|
||||
break;
|
||||
case tls::Connection::result_t::timeout:
|
||||
case tls::Connection::result_t::closed:
|
||||
default:
|
||||
bExit = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case tls::Connection::result_t::timeout:
|
||||
count++;
|
||||
if (count > 10) {
|
||||
bExit = true;
|
||||
}
|
||||
break;
|
||||
case tls::Connection::result_t::closed:
|
||||
default:
|
||||
bExit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
con->shutdown();
|
||||
}
|
||||
std::cout << "Connection closed" << std::endl;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
parse_options(argc, argv);
|
||||
|
||||
tls::Server::configure_signal_handler(SIGUSR1);
|
||||
tls::Server server;
|
||||
tls::Server::config_t config;
|
||||
|
||||
// 15118 required suites, ECDH-ECDSA-AES128-SHA256 not supported by OpenSSL
|
||||
// config.cipher_list = "ECDHE-ECDSA-AES128-SHA256:ECDH-ECDSA-AES128-SHA256";
|
||||
|
||||
config.cipher_list = "ECDHE-ECDSA-AES128-SHA256";
|
||||
if (disable_tls1_3) {
|
||||
config.ciphersuites = "";
|
||||
} else {
|
||||
config.ciphersuites = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384";
|
||||
}
|
||||
|
||||
auto& ref0 = config.chains.emplace_back();
|
||||
if (enable_tpm) {
|
||||
ref0.certificate_chain_file = "tpm_pki/server_chain.pem";
|
||||
ref0.private_key_file = "tpm_pki/server_priv.pem";
|
||||
ref0.trust_anchor_file = "tpm_pki/server_root_cert.pem";
|
||||
} else {
|
||||
ref0.certificate_chain_file = "server_chain.pem";
|
||||
ref0.private_key_file = "server_priv.pem";
|
||||
ref0.trust_anchor_file = "server_root_cert.pem";
|
||||
}
|
||||
ref0.ocsp_response_files = {"ocsp_response.der", "ocsp_response.der"};
|
||||
auto& ref1 = config.chains.emplace_back();
|
||||
ref1.certificate_chain_file = "alt_server_chain.pem";
|
||||
ref1.private_key_file = "alt_server_priv.pem";
|
||||
ref1.trust_anchor_file = "alt_server_root_cert.pem";
|
||||
config.verify_locations_file = "client_root_cert.pem";
|
||||
|
||||
config.service = "8444";
|
||||
config.ipv6_only = ipv6_only;
|
||||
config.verify_client = verify_client;
|
||||
config.io_timeout_ms = 10000;
|
||||
|
||||
std::thread stop([&server]() {
|
||||
std::this_thread::sleep_for(30s);
|
||||
std::cout << "stopping ..." << std::endl;
|
||||
server.stop();
|
||||
});
|
||||
|
||||
server.init(config, nullptr);
|
||||
server.wait_stopped();
|
||||
|
||||
server.serve(&handle_connection);
|
||||
server.wait_stopped();
|
||||
|
||||
stop.join();
|
||||
|
||||
return 0;
|
||||
}
|
||||
452
tools/EVerest-main/lib/everest/tls/tests/tls_test.cpp
Normal file
452
tools/EVerest-main/lib/everest/tls/tests/tls_test.cpp
Normal file
@@ -0,0 +1,452 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2024 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "extensions/trusted_ca_keys.hpp"
|
||||
#include <everest/tls/openssl_util.hpp>
|
||||
#include <everest/tls/tls.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
#include <iterator>
|
||||
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
std::string to_string(const std::uint8_t* const ptr, const std::size_t len) {
|
||||
std::stringstream string_stream;
|
||||
string_stream << std::hex;
|
||||
|
||||
for (int idx = 0; idx < len; ++idx)
|
||||
string_stream << std::setw(2) << std::setfill('0') << (int)ptr[idx];
|
||||
|
||||
return string_stream.str();
|
||||
}
|
||||
|
||||
std::string to_string(const tls::OcspCache::digest_t& digest) {
|
||||
return to_string(reinterpret_cast<const std::uint8_t*>(&digest), sizeof(digest));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(strdup, usage) {
|
||||
// auto* r1 = strdup(nullptr); need to ensure non-nullptr
|
||||
auto* r2 = strdup("");
|
||||
auto* r3 = strdup("hello");
|
||||
// free(r1);
|
||||
free(r2);
|
||||
free(r3);
|
||||
free(nullptr);
|
||||
}
|
||||
|
||||
TEST(string, use) {
|
||||
// was hoping to use std::string for config, but ...
|
||||
std::string empty;
|
||||
std::string space{""};
|
||||
std::string value{"something"};
|
||||
|
||||
EXPECT_TRUE(empty.empty());
|
||||
// EXPECT_FALSE(space.empty()); was hoping it would be true
|
||||
EXPECT_FALSE(value.empty());
|
||||
|
||||
// EXPECT_EQ(empty.c_str(), nullptr); was hoping it would be nullptr
|
||||
EXPECT_NE(space.c_str(), nullptr);
|
||||
EXPECT_NE(value.c_str(), nullptr);
|
||||
}
|
||||
|
||||
TEST(ConfigItem, test) {
|
||||
// tests reduced with new ConfigStore implementation
|
||||
tls::ConfigItem i1;
|
||||
tls::ConfigItem i2{nullptr};
|
||||
tls::ConfigItem i3{"Hello"};
|
||||
tls::ConfigItem i4 = nullptr;
|
||||
tls::ConfigItem i5(nullptr);
|
||||
tls::ConfigItem i6("Hello");
|
||||
|
||||
EXPECT_EQ(i1, nullptr);
|
||||
EXPECT_EQ(i4, nullptr);
|
||||
EXPECT_EQ(i5, nullptr);
|
||||
|
||||
EXPECT_EQ(i2, i5);
|
||||
EXPECT_STREQ(i3, i6);
|
||||
|
||||
EXPECT_EQ(i1, i2);
|
||||
EXPECT_NE(i1, i3);
|
||||
EXPECT_EQ(i1, i5);
|
||||
EXPECT_NE(i1, i6);
|
||||
|
||||
tls::ConfigItem j2{""};
|
||||
|
||||
auto j1(std::move(i3));
|
||||
j2 = std::move(i6);
|
||||
EXPECT_STREQ(i6, i3);
|
||||
EXPECT_STREQ(j1, j2);
|
||||
EXPECT_STREQ(j1, "Hello");
|
||||
EXPECT_NE(j1, i6);
|
||||
|
||||
EXPECT_NE(j1, nullptr);
|
||||
EXPECT_NE(j2, nullptr);
|
||||
|
||||
// EXPECT_EQ(i3, nullptr);
|
||||
// EXPECT_EQ(i6, nullptr);
|
||||
// EXPECT_EQ(i6, i3);
|
||||
|
||||
std::vector<tls::ConfigItem> j3 = {"one", "two", nullptr};
|
||||
EXPECT_STREQ(j3[0], "one");
|
||||
EXPECT_STREQ(j3[1], "two");
|
||||
EXPECT_EQ(j3[2], nullptr);
|
||||
|
||||
const char* p = j1;
|
||||
EXPECT_STREQ(p, "Hello");
|
||||
j1 = "Goodbye";
|
||||
EXPECT_STRNE(j1, "Hello");
|
||||
j1 = j2;
|
||||
EXPECT_STREQ(j1, j2);
|
||||
}
|
||||
|
||||
TEST(ConfigItem, testB) {
|
||||
tls::ConfigItem i1;
|
||||
tls::ConfigItem i2{nullptr};
|
||||
tls::ConfigItem i3{""};
|
||||
tls::ConfigItem i4{"Hello"};
|
||||
|
||||
EXPECT_EQ(i1, nullptr);
|
||||
EXPECT_EQ(i2, nullptr);
|
||||
EXPECT_STREQ(i3, "");
|
||||
EXPECT_STREQ(i4, "Hello");
|
||||
EXPECT_STREQ("Hello", i4);
|
||||
}
|
||||
|
||||
using namespace tls::trusted_ca_keys;
|
||||
using namespace openssl;
|
||||
|
||||
TEST(OcspCache, initEmpty) {
|
||||
tls::OcspCache cache;
|
||||
tls::OcspCache::digest_t digest{};
|
||||
auto res = cache.lookup(digest);
|
||||
EXPECT_EQ(res.get(), nullptr);
|
||||
}
|
||||
|
||||
TEST(OcspCache, init) {
|
||||
tls::OcspCache cache;
|
||||
|
||||
auto chain = openssl::load_certificates("client_chain.pem");
|
||||
std::vector<tls::OcspCache::ocsp_entry_t> entries;
|
||||
|
||||
tls::OcspCache::digest_t digest{};
|
||||
for (const auto& cert : chain) {
|
||||
ASSERT_TRUE(tls::OcspCache::digest(digest, cert.get()));
|
||||
// std::cout << "digest: " << to_string(digest) << std::endl;
|
||||
entries.emplace_back(digest, "ocsp_response.der");
|
||||
}
|
||||
|
||||
EXPECT_TRUE(cache.load(entries));
|
||||
// std::cout << "digest: " << to_string(digest) << std::endl;
|
||||
auto res = cache.lookup(digest);
|
||||
EXPECT_NE(res.get(), nullptr);
|
||||
}
|
||||
|
||||
TEST(TrustedCaKeys, parseAudi) {
|
||||
|
||||
/*
|
||||
* Audi
|
||||
* 0069 trusted_authorities_list length
|
||||
* 03 identifier_type cert_sha1_hash d8367e861f5807f8141fea572d676dbf58bb5f7c SHA1Hash
|
||||
* 03 identifier_type cert_sha1_hash b491ddd08fafe72d9f6f9bafc68eb04da84cc09a SHA1Hash
|
||||
* 03 identifier_type cert_sha1_hash 30aaaab25b1cc8a09a7b32652c33cc5a973c13f3 SHA1Hash
|
||||
* 03 identifier_type cert_sha1_hash 700bf78ad58e0819dac6fcaead5ed20f7bb0554f SHA1Hash
|
||||
* 03 identifier_type cert_sha1_hash 8c821f41604ed4c3431cf6d19f2ae107cf1f50e1 SHA1Hash
|
||||
*/
|
||||
|
||||
using trusted_authority = tls::trusted_ca_keys::trusted_authority;
|
||||
|
||||
std::uint8_t extension[] = {
|
||||
0x00, 0x69, 0x03, 0xd8, 0x36, 0x7e, 0x86, 0x1f, 0x58, 0x07, 0xf8, 0x14, 0x1f, 0xea, 0x57, 0x2d, 0x67, 0x6d,
|
||||
0xbf, 0x58, 0xbb, 0x5f, 0x7c, 0x03, 0xb4, 0x91, 0xdd, 0xd0, 0x8f, 0xaf, 0xe7, 0x2d, 0x9f, 0x6f, 0x9b, 0xaf,
|
||||
0xc6, 0x8e, 0xb0, 0x4d, 0xa8, 0x4c, 0xc0, 0x9a, 0x03, 0x30, 0xaa, 0xaa, 0xb2, 0x5b, 0x1c, 0xc8, 0xa0, 0x9a,
|
||||
0x7b, 0x32, 0x65, 0x2c, 0x33, 0xcc, 0x5a, 0x97, 0x3c, 0x13, 0xf3, 0x03, 0x70, 0x0b, 0xf7, 0x8a, 0xd5, 0x8e,
|
||||
0x08, 0x19, 0xda, 0xc6, 0xfc, 0xae, 0xad, 0x5e, 0xd2, 0x0f, 0x7b, 0xb0, 0x55, 0x4f, 0x03, 0x8c, 0x82, 0x1f,
|
||||
0x41, 0x60, 0x4e, 0xd4, 0xc3, 0x43, 0x1c, 0xf6, 0xd1, 0x9f, 0x2a, 0xe1, 0x07, 0xcf, 0x1f, 0x50, 0xe1,
|
||||
};
|
||||
|
||||
trusted_authority ext{&extension[0], sizeof(extension)};
|
||||
auto res = tls::trusted_ca_keys::convert(ext);
|
||||
EXPECT_EQ(res.cert_sha1_hash.size(), 5);
|
||||
EXPECT_EQ(res.key_sha1_hash.size(), 0);
|
||||
EXPECT_EQ(res.x509_name.size(), 0);
|
||||
EXPECT_FALSE(res.pre_agreed);
|
||||
}
|
||||
|
||||
TEST(TrustedCaKeys, parseBuzz) {
|
||||
|
||||
/*
|
||||
* Buzz
|
||||
* 002a trusted_authorities_list length
|
||||
* 03 identifier_type cert_sha1_hash d8367e861f5807f8141fea572d676dbf58bb5f7c SHA1Hash
|
||||
* 03 identifier_type cert_sha1_hash 8c821f41604ed4c3431cf6d19f2ae107cf1f50e1 SHA1Hash
|
||||
*/
|
||||
|
||||
using trusted_authority = tls::trusted_ca_keys::trusted_authority;
|
||||
|
||||
std::uint8_t extension[] = {
|
||||
0x00, 0x2a, 0x03, 0xd8, 0x36, 0x7e, 0x86, 0x1f, 0x58, 0x07, 0xf8, 0x14, 0x1f, 0xea, 0x57,
|
||||
0x2d, 0x67, 0x6d, 0xbf, 0x58, 0xbb, 0x5f, 0x7c, 0x03, 0x8c, 0x82, 0x1f, 0x41, 0x60, 0x4e,
|
||||
0xd4, 0xc3, 0x43, 0x1c, 0xf6, 0xd1, 0x9f, 0x2a, 0xe1, 0x07, 0xcf, 0x1f, 0x50, 0xe1,
|
||||
};
|
||||
|
||||
trusted_authority ext{&extension[0], sizeof(extension)};
|
||||
auto res = tls::trusted_ca_keys::convert(ext);
|
||||
EXPECT_EQ(res.cert_sha1_hash.size(), 2);
|
||||
EXPECT_EQ(res.key_sha1_hash.size(), 0);
|
||||
EXPECT_EQ(res.x509_name.size(), 0);
|
||||
EXPECT_FALSE(res.pre_agreed);
|
||||
}
|
||||
|
||||
TEST(TrustedCaKeys, parseIoniq6) {
|
||||
|
||||
/*
|
||||
* Ioniq 6 (invalid missing the size of trusted_authorities_list)
|
||||
* 01 identifier_type key_sha1_hash 4cd7290bf592d2c1ba90f56e08946d4c8e99dc38 SHA1Hash
|
||||
* 01 identifier_type key_sha1_hash 00fae3900795c888a4d4d7bd9fdffa60418ac19f SHA1Hash
|
||||
*/
|
||||
|
||||
using trusted_authority = tls::trusted_ca_keys::trusted_authority;
|
||||
|
||||
std::uint8_t extension[] = {
|
||||
0x00, 0x2a, 0x01, 0x4c, 0xd7, 0x29, 0x0b, 0xf5, 0x92, 0xd2, 0xc1, 0xba, 0x90, 0xf5, 0x6e,
|
||||
0x08, 0x94, 0x6d, 0x4c, 0x8e, 0x99, 0xdc, 0x38, 0x01, 0x00, 0xfa, 0xe3, 0x90, 0x07, 0x95,
|
||||
0xc8, 0x88, 0xa4, 0xd4, 0xd7, 0xbd, 0x9f, 0xdf, 0xfa, 0x60, 0x41, 0x8a, 0xc1, 0x9f,
|
||||
};
|
||||
|
||||
trusted_authority ext{&extension[2], sizeof(extension) - 2};
|
||||
auto res = tls::trusted_ca_keys::convert(ext);
|
||||
EXPECT_EQ(res.cert_sha1_hash.size(), 0);
|
||||
EXPECT_EQ(res.key_sha1_hash.size(), 0);
|
||||
EXPECT_EQ(res.x509_name.size(), 0);
|
||||
EXPECT_FALSE(res.pre_agreed);
|
||||
|
||||
ext = trusted_authority{&extension[0], sizeof(extension)};
|
||||
res = tls::trusted_ca_keys::convert(ext);
|
||||
EXPECT_EQ(res.cert_sha1_hash.size(), 0);
|
||||
EXPECT_EQ(res.key_sha1_hash.size(), 2);
|
||||
EXPECT_EQ(res.x509_name.size(), 0);
|
||||
EXPECT_FALSE(res.pre_agreed);
|
||||
}
|
||||
|
||||
TEST(TrustedCaKeys, generateBuzz) {
|
||||
|
||||
/*
|
||||
* Buzz
|
||||
* 002a trusted_authorities_list length
|
||||
* 03 identifier_type cert_sha1_hash d8367e861f5807f8141fea572d676dbf58bb5f7c SHA1Hash
|
||||
* 03 identifier_type cert_sha1_hash 8c821f41604ed4c3431cf6d19f2ae107cf1f50e1 SHA1Hash
|
||||
*/
|
||||
|
||||
using trusted_ca_keys_t = tls::trusted_ca_keys::trusted_ca_keys_t;
|
||||
|
||||
openssl::sha_1_digest_t hash1 = {
|
||||
0xd8, 0x36, 0x7e, 0x86, 0x1f, 0x58, 0x07, 0xf8, 0x14, 0x1f,
|
||||
0xea, 0x57, 0x2d, 0x67, 0x6d, 0xbf, 0x58, 0xbb, 0x5f, 0x7c,
|
||||
};
|
||||
openssl::sha_1_digest_t hash2 = {
|
||||
0x8c, 0x82, 0x1f, 0x41, 0x60, 0x4e, 0xd4, 0xc3, 0x43, 0x1c,
|
||||
0xf6, 0xd1, 0x9f, 0x2a, 0xe1, 0x07, 0xcf, 0x1f, 0x50, 0xe1,
|
||||
};
|
||||
|
||||
std::uint8_t extension[] = {
|
||||
0x00, 0x2a, 0x03, 0xd8, 0x36, 0x7e, 0x86, 0x1f, 0x58, 0x07, 0xf8, 0x14, 0x1f, 0xea, 0x57,
|
||||
0x2d, 0x67, 0x6d, 0xbf, 0x58, 0xbb, 0x5f, 0x7c, 0x03, 0x8c, 0x82, 0x1f, 0x41, 0x60, 0x4e,
|
||||
0xd4, 0xc3, 0x43, 0x1c, 0xf6, 0xd1, 0x9f, 0x2a, 0xe1, 0x07, 0xcf, 0x1f, 0x50, 0xe1,
|
||||
};
|
||||
|
||||
trusted_ca_keys_t tck;
|
||||
tck.cert_sha1_hash.push_back(hash1);
|
||||
tck.cert_sha1_hash.push_back(hash2);
|
||||
|
||||
auto res = tls::trusted_ca_keys::convert(tck);
|
||||
|
||||
EXPECT_EQ(res.size(), sizeof(extension));
|
||||
EXPECT_EQ(std::memcmp(res.get(), &extension, sizeof(extension)), 0);
|
||||
// std::cout << "A: " << to_string(std::get<TrustedAuthority_ptr>(res).get(), sizeof(extension)) << std::endl;
|
||||
// std::cout << "B: " << to_string(&extension[0], sizeof(extension)) << std::endl;
|
||||
}
|
||||
|
||||
TEST(TrustedCaKeys, CertChain) {
|
||||
// match server certificate to trust anchors
|
||||
auto server_root = openssl::load_certificates("server_root_cert.pem");
|
||||
auto server_ca = openssl::load_certificates("server_ca_cert.pem");
|
||||
auto server = openssl::load_certificates("server_cert.pem");
|
||||
auto alt_server_root = openssl::load_certificates("alt_server_root_cert.pem");
|
||||
auto alt_server_ca = openssl::load_certificates("alt_server_ca_cert.pem");
|
||||
auto alt_server = openssl::load_certificates("alt_server_cert.pem");
|
||||
|
||||
openssl::certificate_list chain;
|
||||
std::move(server_ca.begin(), server_ca.end(), std::back_inserter(chain));
|
||||
std::move(alt_server_ca.begin(), alt_server_ca.end(), std::back_inserter(chain));
|
||||
|
||||
ASSERT_EQ(server_root.size(), 1);
|
||||
ASSERT_EQ(alt_server_root.size(), 1);
|
||||
|
||||
EXPECT_EQ(openssl::verify_certificate(server[0].get(), server_root, chain), openssl::verify_result_t::Verified);
|
||||
EXPECT_EQ(openssl::verify_certificate(alt_server[0].get(), alt_server_root, chain),
|
||||
openssl::verify_result_t::Verified);
|
||||
|
||||
EXPECT_EQ(openssl::verify_certificate(server[0].get(), alt_server_root, chain),
|
||||
openssl::verify_result_t::CertificateNotAllowed);
|
||||
EXPECT_EQ(openssl::verify_certificate(alt_server[0].get(), server_root, chain),
|
||||
openssl::verify_result_t::CertificateNotAllowed);
|
||||
}
|
||||
|
||||
TEST(TrustedCaKeys, matchNone) {
|
||||
|
||||
trusted_ca_keys_t keys;
|
||||
chain_t chain;
|
||||
|
||||
EXPECT_FALSE(match(keys, chain));
|
||||
|
||||
auto root = load_certificates("server_root_cert.pem");
|
||||
const auto* root_cert = root[0].get();
|
||||
|
||||
sha_1_digest_t digest;
|
||||
|
||||
keys.x509_name.emplace_back(certificate_subject_der(root_cert));
|
||||
EXPECT_TRUE(certificate_sha_1(digest, root_cert));
|
||||
keys.cert_sha1_hash.push_back(digest);
|
||||
EXPECT_TRUE(certificate_subject_public_key_sha_1(digest, root_cert));
|
||||
keys.key_sha1_hash.push_back(digest);
|
||||
|
||||
EXPECT_FALSE(match(keys, chain));
|
||||
|
||||
auto alt_root = load_certificates("client_root_cert.pem");
|
||||
chain.chain.trust_anchors = std::move(alt_root);
|
||||
EXPECT_FALSE(match(keys, chain));
|
||||
}
|
||||
|
||||
TEST(TrustedCaKeys, matchName) {
|
||||
|
||||
trusted_ca_keys_t keys;
|
||||
chain_t chain;
|
||||
|
||||
auto root = load_certificates("server_root_cert.pem");
|
||||
chain.chain.trust_anchors = std::move(root);
|
||||
|
||||
const auto* root_cert = chain.chain.trust_anchors[0].get();
|
||||
|
||||
keys.x509_name.emplace_back(certificate_subject_der(root_cert));
|
||||
EXPECT_TRUE(match(keys, chain));
|
||||
|
||||
sha_1_digest_t digest;
|
||||
|
||||
EXPECT_TRUE(certificate_sha_1(digest, root_cert));
|
||||
keys.cert_sha1_hash.push_back(digest);
|
||||
EXPECT_TRUE(match(keys, chain));
|
||||
EXPECT_TRUE(certificate_subject_public_key_sha_1(digest, root_cert));
|
||||
keys.key_sha1_hash.push_back(digest);
|
||||
EXPECT_TRUE(match(keys, chain));
|
||||
}
|
||||
|
||||
TEST(TrustedCaKeys, matchCertHash) {
|
||||
|
||||
trusted_ca_keys_t keys;
|
||||
chain_t chain;
|
||||
|
||||
auto root = load_certificates("server_root_cert.pem");
|
||||
chain.chain.trust_anchors.emplace_back(std::move(root[0]));
|
||||
root = load_certificates("client_root_cert.pem");
|
||||
chain.chain.trust_anchors.emplace_back(std::move(root[0]));
|
||||
ASSERT_EQ(chain.chain.trust_anchors.size(), 2);
|
||||
|
||||
const auto* server_root_cert = chain.chain.trust_anchors[0].get();
|
||||
const auto* client_root_cert = chain.chain.trust_anchors[1].get();
|
||||
|
||||
sha_1_digest_t digest;
|
||||
EXPECT_TRUE(certificate_sha_1(digest, client_root_cert));
|
||||
keys.cert_sha1_hash.push_back(digest);
|
||||
EXPECT_TRUE(match(keys, chain));
|
||||
|
||||
EXPECT_TRUE(certificate_sha_1(digest, server_root_cert));
|
||||
keys.cert_sha1_hash.clear();
|
||||
keys.cert_sha1_hash.push_back(digest);
|
||||
EXPECT_TRUE(match(keys, chain));
|
||||
}
|
||||
|
||||
TEST(TrustedCaKeys, matchKeyHash) {
|
||||
|
||||
trusted_ca_keys_t keys;
|
||||
chain_t chain;
|
||||
|
||||
auto root = load_certificates("server_root_cert.pem");
|
||||
chain.chain.trust_anchors.emplace_back(std::move(root[0]));
|
||||
root = load_certificates("client_root_cert.pem");
|
||||
chain.chain.trust_anchors.emplace_back(std::move(root[0]));
|
||||
ASSERT_EQ(chain.chain.trust_anchors.size(), 2);
|
||||
|
||||
const auto* server_root_cert = chain.chain.trust_anchors[0].get();
|
||||
const auto* client_root_cert = chain.chain.trust_anchors[1].get();
|
||||
|
||||
sha_1_digest_t digest;
|
||||
EXPECT_TRUE(certificate_subject_public_key_sha_1(digest, client_root_cert));
|
||||
keys.key_sha1_hash.push_back(digest);
|
||||
EXPECT_TRUE(match(keys, chain));
|
||||
|
||||
EXPECT_TRUE(certificate_subject_public_key_sha_1(digest, server_root_cert));
|
||||
keys.key_sha1_hash.clear();
|
||||
keys.key_sha1_hash.push_back(digest);
|
||||
EXPECT_TRUE(match(keys, chain));
|
||||
}
|
||||
|
||||
TEST(TrustedCaKeys, selectNone) {
|
||||
|
||||
trusted_ca_keys_t keys;
|
||||
chain_list chains;
|
||||
EXPECT_EQ(select(keys, chains), nullptr);
|
||||
|
||||
chains.emplace_back();
|
||||
auto root = load_certificates("server_root_cert.pem");
|
||||
chains[0].chain.trust_anchors.emplace_back(std::move(root[0]));
|
||||
EXPECT_EQ(select(keys, chains), nullptr);
|
||||
|
||||
chains.emplace_back();
|
||||
root = load_certificates("alt_server_root_cert.pem");
|
||||
chains[1].chain.trust_anchors.emplace_back(std::move(root[0]));
|
||||
EXPECT_EQ(select(keys, chains), nullptr);
|
||||
|
||||
sha_1_digest_t digest;
|
||||
root = load_certificates("client_root_cert.pem");
|
||||
auto* client_root_cert = root[0].get();
|
||||
EXPECT_TRUE(certificate_sha_1(digest, client_root_cert));
|
||||
keys.cert_sha1_hash.push_back(digest);
|
||||
EXPECT_TRUE(certificate_subject_public_key_sha_1(digest, client_root_cert));
|
||||
keys.key_sha1_hash.push_back(digest);
|
||||
}
|
||||
|
||||
TEST(TrustedCaKeys, select) {
|
||||
|
||||
trusted_ca_keys_t keys;
|
||||
chain_list chains;
|
||||
EXPECT_EQ(select(keys, chains), nullptr);
|
||||
|
||||
chains.emplace_back();
|
||||
auto root = load_certificates("server_root_cert.pem");
|
||||
chains[0].chain.trust_anchors.emplace_back(std::move(root[0]));
|
||||
EXPECT_EQ(select(keys, chains), nullptr);
|
||||
|
||||
chains.emplace_back();
|
||||
root = load_certificates("alt_server_root_cert.pem");
|
||||
chains[1].chain.trust_anchors.emplace_back(std::move(root[0]));
|
||||
EXPECT_EQ(select(keys, chains), nullptr);
|
||||
|
||||
sha_1_digest_t digest;
|
||||
root = load_certificates("client_root_cert.pem");
|
||||
auto* client_root_cert = root[0].get();
|
||||
EXPECT_TRUE(certificate_sha_1(digest, client_root_cert));
|
||||
keys.cert_sha1_hash.push_back(digest);
|
||||
EXPECT_TRUE(certificate_subject_public_key_sha_1(digest, client_root_cert));
|
||||
keys.key_sha1_hash.push_back(digest);
|
||||
|
||||
chains.emplace_back();
|
||||
root = load_certificates("client_root_cert.pem");
|
||||
chains[2].chain.trust_anchors.emplace_back(std::move(root[0]));
|
||||
auto result = select(keys, chains);
|
||||
EXPECT_NE(result, nullptr);
|
||||
EXPECT_EQ(result, &chains[2]);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
106
tools/EVerest-main/lib/everest/tls/tests/trusted_ca_keys_decode.py
Executable file
106
tools/EVerest-main/lib/everest/tls/tests/trusted_ca_keys_decode.py
Executable file
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
trusted ca keys hex string
|
||||
"0069011d484406bca8888997a7462416445e7db117114c017f204de30f1cd42c9e6dae91b6a8ac9b8d481ba601597be7013ad6fc397b78b01d90cea1b7f909f145011d484406bca8888997a7462416445e7db117114c0100fae3900795c888a4d4d7bd9fdffa60418ac19f"
|
||||
|
||||
length 0069
|
||||
"01 1d484406bca8888997a7462416445e7db117114c"
|
||||
"01 7f204de30f1cd42c9e6dae91b6a8ac9b8d481ba6"
|
||||
"01 597be7013ad6fc397b78b01d90cea1b7f909f145"
|
||||
"01 1d484406bca8888997a7462416445e7db117114c"
|
||||
"01 00fae3900795c888a4d4d7bd9fdffa60418ac19f"
|
||||
|
||||
key hash from certificate
|
||||
openssl x509 -in cert.pem -pubkey -noout | openssl enc -base64 -d | openssl dgst -sha1
|
||||
"""
|
||||
|
||||
from cryptography import x509
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
|
||||
import argparse
|
||||
|
||||
|
||||
def certificate_key_hash(filename):
|
||||
with open(filename, "rb") as fp:
|
||||
cert = x509.load_pem_x509_certificate(fp.read())
|
||||
pub = cert.public_key()
|
||||
pub_der = pub.public_bytes(
|
||||
encoding=serialization.Encoding.DER,
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
||||
)
|
||||
dgst = hashes.Hash(hashes.SHA1())
|
||||
dgst.update(pub_der)
|
||||
sha1 = dgst.finalize()
|
||||
print(sha1.hex())
|
||||
|
||||
|
||||
def certificate_hash(filename):
|
||||
# note this is the hash of the whole certificate including signature
|
||||
with open(filename, "rb") as fp:
|
||||
cert = x509.load_pem_x509_certificate(fp.read())
|
||||
pub_der = cert.public_bytes(encoding=serialization.Encoding.DER)
|
||||
dgst = hashes.Hash(hashes.SHA1())
|
||||
dgst.update(pub_der)
|
||||
sha1 = dgst.finalize()
|
||||
print(sha1.hex())
|
||||
|
||||
|
||||
def trusted_ca_keys_decode(data):
|
||||
data_len = int.from_bytes(data[:2], "big", signed=False)
|
||||
data = data[2:]
|
||||
assert len(data) == data_len
|
||||
while data:
|
||||
entry_type = data[0]
|
||||
data = data[1:]
|
||||
if entry_type == 0:
|
||||
print("pre_agreed")
|
||||
elif entry_type == 1:
|
||||
sha1 = data[:20]
|
||||
data = data[20:]
|
||||
print("key_sha1_hash: %s" % sha1.hex())
|
||||
elif entry_type == 2:
|
||||
print("x509_name (not decoded yet)")
|
||||
elif entry_type == 3:
|
||||
sha1 = data[:20]
|
||||
data = data[20:]
|
||||
print("cert_sha1_hash: %s" % sha1.hex())
|
||||
|
||||
|
||||
def trusted_ca_keys_decode_file(filename):
|
||||
with open(filename, "rb") as fp:
|
||||
trusted_ca_keys_decode(fp.read())
|
||||
|
||||
|
||||
def trusted_ca_keys_decode_hex(hexstr):
|
||||
trusted_ca_keys_decode(bytes.fromhex(hexstr))
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--key",
|
||||
action="store",
|
||||
help="filename: Print sha1 hash of certificate public key",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--cert", action="store", help="filename: Print sha1 hash of certificate"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--file", action="store", help="filename: Parse trusted ca keys"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--hex", action="store", help="parse trusted ca keys hex string"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
if args.key:
|
||||
certificate_key_hash(args.key)
|
||||
if args.cert:
|
||||
certificate_hash(args.cert)
|
||||
if args.file:
|
||||
trusted_ca_keys_decode_file(args.file)
|
||||
if args.hex:
|
||||
trusted_ca_keys_decode_hex(args.hex)
|
||||
Reference in New Issue
Block a user