Skip to content

Commit

Permalink
Adding jwk export for pub/priv EC keys (only openssl 1.1.1)
Browse files Browse the repository at this point in the history
  • Loading branch information
Greg Hewett committed Jul 26, 2023
1 parent e1c598e commit 04dd82b
Show file tree
Hide file tree
Showing 10 changed files with 289 additions and 4 deletions.
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ endif()
###

# External libraries
find_package(nlohmann_json REQUIRED)
find_package(OpenSSL REQUIRED)
if ( OPENSSL_FOUND )
if (${OPENSSL_VERSION} VERSION_GREATER_EQUAL 3)
Expand Down Expand Up @@ -105,7 +106,11 @@ file(GLOB_RECURSE LIB_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src

add_library(${LIB_NAME} ${LIB_HEADERS} ${LIB_SOURCES})
add_dependencies(${LIB_NAME} bytes tls_syntax hpke)
target_link_libraries(${LIB_NAME} bytes tls_syntax hpke)
target_link_libraries(${LIB_NAME}
PUBLIC
bytes tls_syntax hpke
PRIVATE
nlohmann_json::nlohmann_json)
target_include_directories(${LIB_NAME}
PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
Expand Down
3 changes: 3 additions & 0 deletions include/mls/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ struct SignaturePublicKey
const bytes& message,
const bytes& signature) const;

std::string to_jwk(CipherSuite suite) const;

TLS_SERIALIZABLE(data)
};

Expand All @@ -236,6 +238,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
3 changes: 3 additions & 0 deletions lib/hpke/include/hpke/signature.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ struct Signature
virtual std::unique_ptr<PrivateKey> deserialize_private(
const bytes& skm) 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
85 changes: 85 additions & 0 deletions lib/hpke/src/group.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include "openssl/param_build.h"
#endif

#include <iostream>

namespace hpke {

static inline size_t
Expand Down Expand Up @@ -526,6 +528,42 @@ struct ECKeyGroup : public EVPGroup
#endif
}

void split_key(const Group::PublicKey& pk, bytes& x, bytes& y) const override
{
#if defined(WITH_OPENSSL3)
throw std::runtime_error("openssl 3... todo");
#endif
BIGNUM* bnX = BN_new();
BIGNUM* bnY = BN_new();
const auto& rpk = dynamic_cast<const PublicKey&>(pk);
EC_KEY* pub = EVP_PKEY_get0_EC_KEY(rpk.pkey.get());
const EC_POINT* point = EC_KEY_get0_public_key(pub);
const EC_GROUP* group = EC_KEY_get0_group(pub);

if (1 !=
EC_POINT_get_affine_coordinates_GFp(group, point, bnX, bnY, nullptr)) {
BN_free(bnX);
BN_free(bnY);
throw openssl_error();
}
auto outX = bytes(BN_num_bytes(bnX));
auto outY = bytes(BN_num_bytes(bnY));

if (BN_bn2bin(bnX, outX.data()) != int(outX.size())) {
throw openssl_error();
}

if (BN_bn2bin(bnY, outY.data()) != int(outY.size())) {
throw openssl_error();
}
const auto zeros_neededX = dh_size - outX.size();
const auto zeros_neededY = dh_size - outY.size();
auto leading_zerosX = bytes(zeros_neededX, 0);
auto leading_zerosY = bytes(zeros_neededY, 0);
x = leading_zerosX + outX;
y = leading_zerosY + outY;
}

private:
int curve_nid;

Expand Down Expand Up @@ -648,6 +686,13 @@ struct RawKeyGroup : public EVPGroup
return std::make_unique<EVPGroup::PrivateKey>(pkey);
}

void split_key(const Group::PublicKey& /*unused*/,
bytes& /*unused*/,
bytes& /*unused*/) const override
{
throw std::runtime_error("Unsupported group");
}

private:
const int evp_type;

Expand Down Expand Up @@ -809,11 +854,51 @@ group_sk_size(Group::ID group_id)
}
}

static inline std::string
group_jwt_curve_name(Group::ID group_id)
{
switch (group_id) {
case Group::ID::P256:
return "P-256";
case Group::ID::P384:
return "P-384";
case Group::ID::P521:
return "P-521";
case Group::ID::X25519:
case Group::ID::Ed25519:
case Group::ID::X448:
case Group::ID::Ed448:
throw std::runtime_error("Unsupported group");
default:
throw std::runtime_error("Unknown group");
}
}

static inline std::string
group_jwt_key_type(Group::ID group_id)
{
switch (group_id) {
case Group::ID::P256:
case Group::ID::P384:
case Group::ID::P521:
return "EC";
case Group::ID::X25519:
case Group::ID::Ed25519:
case Group::ID::X448:
case Group::ID::Ed448:
throw std::runtime_error("Unsupported group");
default:
throw std::runtime_error("Unknown group");
}
}

Group::Group(ID group_id_in, const KDF& kdf_in)
: id(group_id_in)
, dh_size(group_dh_size(group_id_in))
, pk_size(group_pk_size(group_id_in))
, sk_size(group_sk_size(group_id_in))
, jwt_key_type(group_jwt_key_type(group_id_in))
, jwt_curve_name(group_jwt_curve_name(group_id_in))
, kdf(kdf_in)
{
}
Expand Down
6 changes: 6 additions & 0 deletions lib/hpke/src/group.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ struct Group
const size_t dh_size;
const size_t pk_size;
const size_t sk_size;
const std::string jwt_key_type;
const std::string jwt_curve_name;

virtual std::unique_ptr<PrivateKey> generate_key_pair() const = 0;
virtual std::unique_ptr<PrivateKey> derive_key_pair(
Expand All @@ -63,6 +65,10 @@ struct Group
const bytes& sig,
const PublicKey& pk) const = 0;

virtual void split_key(const Group::PublicKey& pk,
bytes& x,
bytes& y) const = 0;

protected:
const KDF& kdf;

Expand Down
60 changes: 60 additions & 0 deletions lib/hpke/src/signature.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
#include <hpke/digest.h>
#include <hpke/signature.h>
#include <string>

#include "dhkem.h"

#include "common.h"
#include "group.h"
#include "rsa.h"
#include <nlohmann/json.hpp>
#include <openssl/bio.h>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>

Expand Down Expand Up @@ -103,6 +108,49 @@ struct GroupSignature : public Signature
return group.verify(data, sig, rpk);
}

// hpke::GroupSignature
std::string export_jwk(const bytes& enc) const override
{
bytes x;
bytes y;
nlohmann::json json_jwk;
json_jwk["crv"] = group.jwt_curve_name;
json_jwk["kty"] = group.jwt_key_type;

std::unique_ptr<hpke::Signature::PublicKey> pk = deserialize(enc);
const auto& rpk =
dynamic_cast<const hpke::Group::PublicKey&>(*(pk.release()));

group.split_key(rpk, x, y);
json_jwk["x"] = to_base64url(x);
json_jwk["y"] = to_base64url(y);

return json_jwk.dump();
}

// hpke::GroupSignature
std::string export_jwk_private(const bytes& enc) const override
{
bytes x;
bytes y;
nlohmann::json json_jwk;
json_jwk["crv"] = group.jwt_curve_name;
json_jwk["kty"] = group.jwt_key_type;

// encode the private key
json_jwk["d"] = to_base64url(enc);

const auto priv = deserialize_private(enc);
const auto& rpk =
dynamic_cast<const Group::PublicKey&>(*(priv->public_key().release()));

group.split_key(rpk, x, y);
json_jwk["x"] = to_base64url(x);
json_jwk["y"] = to_base64url(y);

return json_jwk.dump();
}

private:
const Group& group;
};
Expand Down Expand Up @@ -182,6 +230,18 @@ Signature::serialize_private(const PrivateKey& /* unused */) const
throw std::runtime_error("Not implemented");
}

std::string
Signature::export_jwk(const bytes& /* unused */) const
{
throw std::runtime_error("Not implemented.");
}

std::string
Signature::export_jwk_private(const bytes& /* unused */) const
{
throw std::runtime_error("Not implemented.");
}

std::unique_ptr<Signature::PrivateKey>
Signature::deserialize_private(const bytes& /* unused */) const
{
Expand Down
12 changes: 12 additions & 0 deletions src/crypto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,12 @@ SignaturePublicKey::verify(const CipherSuite& suite,
return suite.sig().verify(content, signature, *pub);
}

std::string
SignaturePublicKey::to_jwk(CipherSuite suite) const
{
return suite.sig().export_jwk(data);
}

SignaturePrivateKey
SignaturePrivateKey::generate(CipherSuite suite)
{
Expand Down Expand Up @@ -438,4 +444,10 @@ SignaturePrivateKey::set_public_key(CipherSuite suite)
public_key.data = suite.sig().serialize(*pub);
}

std::string
SignaturePrivateKey::to_jwk(CipherSuite suite) const
{
return suite.sig().export_jwk_private(data);
}

} // namespace mls
Loading

0 comments on commit 04dd82b

Please sign in to comment.