Skip to content

Commit

Permalink
adding import and export jwk keys into signatures
Browse files Browse the repository at this point in the history
  • Loading branch information
Greg Hewett committed Jul 31, 2023
1 parent 3b780c9 commit 121dd13
Show file tree
Hide file tree
Showing 17 changed files with 730 additions and 16 deletions.
19 changes: 13 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ target_include_directories(${LIB_NAME}
${OPENSSL_INCLUDE_DIR}
)

install(TARGETS ${LIB_NAME} EXPORT mlspp-targets)

###
### Tests
###
Expand All @@ -125,9 +127,7 @@ endif()
### Exports
###
set(CMAKE_EXPORT_PACKAGE_REGISTRY ON)
export(EXPORT mlspp-targets
NAMESPACE MLSPP::
FILE ${CMAKE_CURRENT_BINARY_DIR}/mlspp-targets.cmake)
export(EXPORT mlspp-targets NAMESPACE MLSPP:: FILE mlspp-targets.cmake)
export(PACKAGE MLSPP)

configure_package_config_file(cmake/config.cmake.in
Expand All @@ -144,8 +144,6 @@ write_basic_package_version_file(
### Install
###

install(TARGETS ${LIB_NAME} EXPORT mlspp-targets)

install(
DIRECTORY
include/
Expand All @@ -156,7 +154,16 @@ install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/mlspp-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/mlspp-config-version.cmake
${CMAKE_CURRENT_BINARY_DIR}/mlspp-targets.cmake
DESTINATION
${CMAKE_INSTALL_DATADIR}/mlspp)

install(
EXPORT
mlspp-targets
NAMESPACE
MLSPP::
FILE
mlspp-targets.cmake
DESTINATION
${CMAKE_INSTALL_DATADIR}/mlspp)

3 changes: 2 additions & 1 deletion alternatives/openssl_3/vcpkg.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"name": "openssl",
"version>=": "3.0.7"
},
"doctest"
"doctest",
"nlohmann-json"
],
"builtin-baseline": "5908d702d61cea1429b223a0b7a10ab86bad4c78",
"overrides": [
Expand Down
4 changes: 2 additions & 2 deletions include/mls/credential.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ namespace mls {
// } BasicCredential;
struct BasicCredential
{
BasicCredential() = default;
BasicCredential() {}

explicit BasicCredential(bytes identity_in)
BasicCredential(bytes identity_in)
: identity(std::move(identity_in))
{
}
Expand Down
8 changes: 8 additions & 0 deletions include/mls/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,13 +210,18 @@ extern const std::string multi_credential;

struct SignaturePublicKey
{
static SignaturePublicKey from_jwk(CipherSuite suite,
const std::string& json_str);

bytes data;

bool verify(const CipherSuite& suite,
const std::string& label,
const bytes& message,
const bytes& signature) const;

std::string to_jwk(CipherSuite suite) const;

TLS_SERIALIZABLE(data)
};

Expand All @@ -225,6 +230,8 @@ struct SignaturePrivateKey
static SignaturePrivateKey generate(CipherSuite suite);
static SignaturePrivateKey parse(CipherSuite suite, const bytes& data);
static SignaturePrivateKey derive(CipherSuite suite, const bytes& secret);
static SignaturePrivateKey from_jwk(CipherSuite suite,
const std::string& json_str);

SignaturePrivateKey() = default;

Expand All @@ -236,6 +243,7 @@ struct SignaturePrivateKey
const bytes& message) const;

void set_public_key(CipherSuite suite);
std::string to_jwk(CipherSuite suite) const;

TLS_SERIALIZABLE(data)

Expand Down
6 changes: 5 additions & 1 deletion lib/bytes/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ file(GLOB_RECURSE LIB_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src

add_library(${CURRENT_LIB_NAME} ${LIB_HEADERS} ${LIB_SOURCES})
add_dependencies(${CURRENT_LIB_NAME} tls_syntax)
target_link_libraries(${CURRENT_LIB_NAME} tls_syntax)
target_link_libraries(${CURRENT_LIB_NAME}
PUBLIC
tls_syntax
PRIVATE
OpenSSL::Crypto)
target_include_directories(${CURRENT_LIB_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
Expand Down
12 changes: 12 additions & 0 deletions lib/bytes/include/bytes/bytes.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,16 @@ to_hex(const bytes& data);
bytes
from_hex(const std::string& hex);

std::string
to_base64(const bytes& data);

std::string
to_base64url(const bytes& data);

bytes
from_base64(const std::string& enc);

bytes
from_base64url(const std::string& enc);

} // namespace bytes_ns
124 changes: 123 additions & 1 deletion lib/bytes/src/bytes.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#include <bytes/bytes.h>

#include <array>
#include <iomanip>
#include <iostream>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <sstream>
#include <stdexcept>

Expand Down Expand Up @@ -137,4 +140,123 @@ operator!=(const std::vector<uint8_t>& lhs, const bytes_ns::bytes& rhs)
return rhs != lhs;
}

std::string
to_base64(const bytes& data)
{
bool done = false;
int result = 0;

if (data.empty()) {
return "";
}

BIO* b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
BIO* out = BIO_new(BIO_s_mem());
BIO_push(b64, out);

while (!done) {
result = BIO_write(b64, data.data(), static_cast<int>(data.size()));

if (result <= 0) {
if (BIO_should_retry(b64)) {
continue;
}
throw std::runtime_error("base64 encode failed");
}
done = true;
}
BIO_flush(b64);
char* string_ptr = nullptr;
// long string_len = BIO_get_mem_data(out, &string_ptr);
// BIO_get_mem_data failed clang-tidy
long string_len = BIO_ctrl(out, BIO_CTRL_INFO, 0, &string_ptr);
auto return_value = std::string(string_ptr, string_len);

BIO_set_close(out, BIO_NOCLOSE);
BIO_free(b64);
BIO_free(out);
return return_value;
}

std::string
to_base64url(const bytes& data)
{
if (data.empty()) {
return "";
}

std::string return_value = to_base64(data);

// remove the end padding
auto sz = return_value.find_first_of('=');

if (sz != std::string::npos) {
return_value = return_value.substr(0, sz);
}

// replace plus with hyphen
std::replace(return_value.begin(), return_value.end(), '+', '-');

// replace slash with underscore
std::replace(return_value.begin(), return_value.end(), '/', '_');
return return_value;
}

bytes
from_base64(const std::string& enc)
{
if (enc.length() == 0) {
return {};
}

if (enc.length() % 4 != 0) {
throw std::runtime_error("Base64 length is not divisible by 4");
}
bytes input = from_ascii(enc);
bytes output(input.size() / 4 * 3);
int output_buffer_length = static_cast<int>(output.size());
EVP_ENCODE_CTX* ctx = EVP_ENCODE_CTX_new();
EVP_DecodeInit(ctx);

int result = EVP_DecodeUpdate(ctx,
output.data(),
&output_buffer_length,
input.data(),
static_cast<int>(input.size()));

if (result == -1) {
auto code = ERR_get_error();
throw std::runtime_error(ERR_error_string(code, nullptr));
}

if (result == 0 && enc.substr(enc.length() - 2, enc.length()) == "==") {
output = output.slice(0, output.size() - 2);
} else if (result == 0 && enc.substr(enc.length() - 1, enc.length()) == "=") {
output = output.slice(0, output.size() - 1);
} else if (result == 0) {
throw std::runtime_error("Base64 padding was malformed.");
}
EVP_DecodeFinal(ctx, output.data(), &output_buffer_length);
EVP_ENCODE_CTX_free(ctx);
return output;
}

bytes
from_base64url(const std::string& enc)
{
if (enc.empty()) {
return {};
}
std::string enc_copy = enc; // copy
std::replace(enc_copy.begin(), enc_copy.end(), '-', '+');
std::replace(enc_copy.begin(), enc_copy.end(), '_', '/');

while (enc_copy.length() % 4 != 0) {
enc_copy += "=";
}
bytes return_value = from_base64(enc_copy);
return return_value;
}

} // namespace bytes_ns
28 changes: 28 additions & 0 deletions lib/bytes/test/bytes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <doctest/doctest.h>
#include <memory>
#include <sstream>
#include <vector>

using namespace bytes_ns;
using namespace std::literals::string_literals;
Expand Down Expand Up @@ -40,6 +41,33 @@ TEST_CASE("To/from hex/ASCII")
REQUIRE(from_ascii(str) == ascii);
}

TEST_CASE("To Base64 / To Base64Url")
{
struct KnownAnswerTest
{
bytes data;
std::string base64;
std::string base64u;
};

const std::vector<KnownAnswerTest> cases{
{ from_ascii("hello there"), "aGVsbG8gdGhlcmU=", "aGVsbG8gdGhlcmU" },
{ from_ascii("A B C D E F "), "QSBCIEMgRCBFIEYg", "QSBCIEMgRCBFIEYg" },
{ from_ascii("hello\xfethere"), "aGVsbG/+dGhlcmU=", "aGVsbG_-dGhlcmU" },
{ from_ascii("\xfe"), "/g==", "_g" },
{ from_ascii("\x01\x02"), "AQI=", "AQI" },
{ from_ascii("\x01"), "AQ==", "AQ" },
{ from_ascii(""), "", "" },
};

for (const auto& tc : cases) {
REQUIRE(to_base64(tc.data) == tc.base64);
REQUIRE(to_base64url(tc.data) == tc.base64u);
REQUIRE(from_base64(tc.base64) == tc.data);
REQUIRE(from_base64url(tc.base64u) == tc.data);
}
}

TEST_CASE("Operators")
{
const auto lhs = from_hex("00010203");
Expand Down
7 changes: 6 additions & 1 deletion lib/hpke/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ set(CURRENT_LIB_NAME hpke)
###
### Dependencies
###
find_package(nlohmann_json REQUIRED)
find_package(OpenSSL 1.1 REQUIRED)

###
Expand All @@ -14,7 +15,11 @@ file(GLOB_RECURSE LIB_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src

add_library(${CURRENT_LIB_NAME} ${LIB_HEADERS} ${LIB_SOURCES})
add_dependencies(${CURRENT_LIB_NAME} bytes tls_syntax)
target_link_libraries(${CURRENT_LIB_NAME} PRIVATE bytes tls_syntax OpenSSL::Crypto)
target_link_libraries(${CURRENT_LIB_NAME}
PRIVATE
nlohmann_json::nlohmann_json OpenSSL::Crypto
PUBLIC
bytes tls_syntax)
target_include_directories(${CURRENT_LIB_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
Expand Down
7 changes: 7 additions & 0 deletions lib/hpke/include/hpke/signature.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ struct Signature
virtual std::unique_ptr<PrivateKey> deserialize_private(
const bytes& skm) const;

virtual std::unique_ptr<PrivateKey> import_jwk_private(
const std::string& json_str) const;
virtual std::unique_ptr<PublicKey> import_jwk(
const std::string& json_str) const;
virtual std::string export_jwk_private(const bytes& env) const;
virtual std::string export_jwk(const bytes& env) const;

virtual bytes sign(const bytes& data, const PrivateKey& sk) const = 0;
virtual bool verify(const bytes& data,
const bytes& sig,
Expand Down
Loading

0 comments on commit 121dd13

Please sign in to comment.