Skip to content

Commit

Permalink
Add unit test to split_x509_cert_bundle (#6323)
Browse files Browse the repository at this point in the history
  • Loading branch information
achamayou authored Jul 3, 2024
1 parent 329288c commit e30a3fa
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 55 deletions.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,9 @@ if(BUILD_TESTS)
)
target_link_libraries(base64_test PRIVATE ${CMAKE_THREAD_LIBS_INIT})

add_unit_test(pem_test ${CMAKE_CURRENT_SOURCE_DIR}/src/crypto/test/pem.cpp)
target_link_libraries(pem_test PRIVATE ${CMAKE_THREAD_LIBS_INIT})

add_test_bin(
kp_cert_test ${CMAKE_CURRENT_SOURCE_DIR}/src/crypto/test/kp_cert.cpp
)
Expand Down
1 change: 1 addition & 0 deletions cmake/crypto.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ set(CCFCRYPTO_SRC
${CCF_DIR}/src/crypto/verifier.cpp
${CCF_DIR}/src/crypto/key_wrap.cpp
${CCF_DIR}/src/crypto/hmac.cpp
${CCF_DIR}/src/crypto/pem.cpp
${CCF_DIR}/src/crypto/ecdsa.cpp
${CCF_DIR}/src/crypto/openssl/symmetric_key.cpp
${CCF_DIR}/src/crypto/openssl/public_key.cpp
Expand Down
69 changes: 14 additions & 55 deletions include/ccf/crypto/pem.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,84 +18,57 @@ namespace ccf::crypto
{
private:
std::string s;

void check_pem_format()
{
if (s.find("-----BEGIN") == std::string::npos)
{
throw std::runtime_error(
fmt::format("PEM constructed with non-PEM data: {}", s));
}
}
void check_pem_format();

public:
Pem() = default;

Pem(const std::string& s_) : s(s_)
{
check_pem_format();
}

Pem(const uint8_t* data, size_t size)
{
if (size == 0)
throw std::logic_error("Got PEM of size 0.");

// If it's already null-terminated, don't suffix again
const auto null_terminated = *(data + size - 1) == 0;
if (null_terminated)
size -= 1;

s.assign(reinterpret_cast<const char*>(data), size);

check_pem_format();
}
Pem(const std::string& s_);
Pem(const uint8_t* data, size_t size);

explicit Pem(std::span<const uint8_t> s) : Pem(s.data(), s.size()) {}

explicit Pem(const std::vector<uint8_t>& v) : Pem(v.data(), v.size()) {}

bool operator==(const Pem& rhs) const
inline bool operator==(const Pem& rhs) const
{
return s == rhs.s;
}

bool operator!=(const Pem& rhs) const
inline bool operator!=(const Pem& rhs) const
{
return !(*this == rhs);
}

bool operator<(const Pem& rhs) const
inline bool operator<(const Pem& rhs) const
{
return s < rhs.s;
}

const std::string& str() const
inline const std::string& str() const
{
return s;
}

uint8_t* data()
inline uint8_t* data()
{
return reinterpret_cast<uint8_t*>(s.data());
}

const uint8_t* data() const
inline const uint8_t* data() const
{
return reinterpret_cast<const uint8_t*>(s.data());
}

size_t size() const
inline size_t size() const
{
return s.size();
}

bool empty() const
inline bool empty() const
{
return s.empty();
}

std::vector<uint8_t> raw() const
inline std::vector<uint8_t> raw() const
{
return {data(), data() + size()};
}
Expand Down Expand Up @@ -128,22 +101,8 @@ namespace ccf::crypto
return "Pem";
}

static std::vector<ccf::crypto::Pem> split_x509_cert_bundle(
const std::string_view& pem)
{
std::string separator("-----END CERTIFICATE-----");
std::vector<ccf::crypto::Pem> pems;
auto separator_end = 0;
auto next_separator_start = pem.find(separator);
while (next_separator_start != std::string_view::npos)
{
pems.emplace_back(std::string(
pem.substr(separator_end, next_separator_start + separator.size())));
separator_end = next_separator_start + separator.size();
next_separator_start = pem.find(separator, separator_end);
}
return pems;
}
std::vector<ccf::crypto::Pem> split_x509_cert_bundle(
const std::string_view& pem);

inline void fill_json_schema(nlohmann::json& schema, const Pem*)
{
Expand Down
52 changes: 52 additions & 0 deletions src/crypto/pem.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#include "ccf/crypto/pem.h"

namespace ccf::crypto
{
void Pem::check_pem_format()
{
if (s.find("-----BEGIN") == std::string::npos)
{
throw std::runtime_error(
fmt::format("PEM constructed with non-PEM data: {}", s));
}
}

Pem::Pem(const std::string& s_) : s(s_)
{
check_pem_format();
}

Pem::Pem(const uint8_t* data, size_t size)
{
if (size == 0)
throw std::logic_error("Got PEM of size 0.");

// If it's already null-terminated, don't suffix again
const auto null_terminated = *(data + size - 1) == 0;
if (null_terminated)
size -= 1;

s.assign(reinterpret_cast<const char*>(data), size);

check_pem_format();
}

std::vector<ccf::crypto::Pem> split_x509_cert_bundle(
const std::string_view& pem)
{
std::string separator("-----END CERTIFICATE-----");
std::vector<ccf::crypto::Pem> pems;
auto separator_end = 0;
auto next_separator_start = pem.find(separator);
while (next_separator_start != std::string_view::npos)
{
pems.emplace_back(std::string(
pem.substr(separator_end, next_separator_start + separator.size())));
separator_end = next_separator_start + separator.size();
next_separator_start = pem.find(separator, separator_end);
}
return pems;
}
}
58 changes: 58 additions & 0 deletions src/crypto/test/pem.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "ccf/crypto/pem.h"

#include <chrono>
#include <doctest/doctest.h>
#include <string>

using namespace std;
using namespace ccf::crypto;

TEST_CASE("Split x509 cert bundle")
{
REQUIRE(split_x509_cert_bundle("") == std::vector<Pem>{});

const std::string single_cert =
"-----BEGIN "
"CERTIFICATE-----"
"\nMIIByDCCAU6gAwIBAgIQOBe5SrcwReWmSzTjzj2HDjAKBggqhkjOPQQDAzATMREw\nDwYDVQ"
"QDDAhDQ0YgTm9kZTAeFw0yMzA1MTcxMzUwMzFaFw0yMzA1MTgxMzUwMzBa\nMBMxETAPBgNVBA"
"MMCENDRiBOb2RlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE74qL\nAc/"
"45tiriN5MuquYhHVdMGQRvYSm08HBfYcODtET88qC0A39o6Y2TmfbIn6BdjMG\nkD58o377ZMT"
"aApQu/oJcwt7qZ9/LE8j8WU2qHn0cPTlpwH/"
"2tiud2w+U3voSo2cw\nZTASBgNVHRMBAf8ECDAGAQH/"
"AgEAMB0GA1UdDgQWBBS9FJNwWSXtUpHaBV57EwTW\noM8vHjAfBgNVHSMEGDAWgBS9FJNwWSXt"
"UpHaBV57EwTWoM8vHjAPBgNVHREECDAG\nhwR/"
"xF96MAoGCCqGSM49BAMDA2gAMGUCMQDKxpjPToJ7VSqKqQSeMuW9tr4iL+"
"9I\n7gTGdGwiIYV1qTSS35Sk9XQZ0VpSa58c/"
"5UCMEgmH71k7XlTGVUypm4jAgjpC46H\ns+hJpGMvyD9dKzEpZgmZYtghbyakUkwBiqmFQA=="
"\n-----END CERTIFICATE-----";
auto bundle = split_x509_cert_bundle(single_cert);
const auto cert_pem = Pem(single_cert);
REQUIRE(bundle.size() == 1);
REQUIRE(bundle[0] == cert_pem);

const std::string two_certs = single_cert + single_cert;
bundle = split_x509_cert_bundle(two_certs);
REQUIRE(bundle.size() == 2);
REQUIRE(bundle[0] == cert_pem);
REQUIRE(bundle[1] == cert_pem);

std::string bundle_with_invalid_suffix = single_cert + "ignored suffix";
bundle = split_x509_cert_bundle(bundle_with_invalid_suffix);
REQUIRE(bundle.size() == 1);
REQUIRE(bundle[0] == cert_pem);

bundle_with_invalid_suffix =
single_cert + "-----BEGIN CERTIFICATE-----\nignored suffix";
bundle = split_x509_cert_bundle(bundle_with_invalid_suffix);
REQUIRE(bundle.size() == 1);
REQUIRE(bundle[0] == cert_pem);

const std::string bundle_with_very_invalid_pem =
single_cert + "not a cert\n-----END CERTIFICATE-----";
REQUIRE_THROWS_AS(
split_x509_cert_bundle(bundle_with_very_invalid_pem), std::runtime_error);
}
1 change: 1 addition & 0 deletions tests/perf-system/submitter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ set(CCFCRYPTO_SRC
${CCF_DIR}/src/crypto/verifier.cpp
${CCF_DIR}/src/crypto/key_wrap.cpp
${CCF_DIR}/src/crypto/hmac.cpp
${CCF_DIR}/src/crypto/pem.cpp
${CCF_DIR}/src/crypto/openssl/symmetric_key.cpp
${CCF_DIR}/src/crypto/openssl/public_key.cpp
${CCF_DIR}/src/crypto/openssl/key_pair.cpp
Expand Down

0 comments on commit e30a3fa

Please sign in to comment.