Skip to content

Commit 6503550

Browse files
plqbifurcation
andauthored
Add DER parsing support for private keys (#348)
* add EC cert test * Implement SignaturePrivateKey::parse_der * add missing ICU dependency --------- Co-authored-by: Richard Barnes <rlb@ipv.sx>
1 parent 61e4d76 commit 6503550

8 files changed

Lines changed: 102 additions & 4 deletions

File tree

cmd/interop/CMakeLists.txt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ find_package(OpenSSL 1.1 REQUIRED)
3838
find_package(Protobuf REQUIRED)
3939
find_package(gRPC CONFIG REQUIRED)
4040
find_package(gflags REQUIRED)
41+
find_package(ICU REQUIRED COMPONENTS uc data)
4142

4243
# mlspp
4344
set(CMAKE_EXPORT_PACKAGE_REGISTRY ON)
@@ -50,7 +51,7 @@ find_package(nlohmann_json 3.2 REQUIRED)
5051
### Protobuf generation
5152
###
5253

53-
# Get the proto file from the interop repo
54+
# Get the proto file from the interop repo
5455
include( ExternalProject )
5556
find_package( Git REQUIRED )
5657
set( MLS_IMPLEMENTATIONS_REPO_URL https://github.com/mlswg/mls-implementations.git )
@@ -99,7 +100,7 @@ file(GLOB_RECURSE BIN_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
99100
add_executable(${APP_NAME} ${BIN_SOURCES} ${PB_SRC} ${GRPC_SRC})
100101
add_dependencies(${APP_NAME} mls-interop-extern)
101102
target_include_directories(${APP_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
102-
target_link_libraries(${APP_NAME}
103-
gflags
104-
gRPC::grpc++ protobuf::libprotobuf
103+
target_link_libraries(${APP_NAME}
104+
gflags ICU::uc ICU::data
105+
gRPC::grpc++ protobuf::libprotobuf
105106
MLSPP::mlspp MLSPP::mls_vectors MLSPP::tls_syntax)

include/mls/crypto.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ struct SignaturePrivateKey
256256
{
257257
static SignaturePrivateKey generate(CipherSuite suite);
258258
static SignaturePrivateKey parse(CipherSuite suite, const bytes& data);
259+
static SignaturePrivateKey parse_der(CipherSuite suite, const bytes& data);
259260
static SignaturePrivateKey derive(CipherSuite suite, const bytes& secret);
260261
static SignaturePrivateKey from_jwk(CipherSuite suite,
261262
const std::string& json_str);

lib/hpke/include/hpke/signature.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ struct Signature
7676
virtual std::string export_jwk_private(const PrivateKey& env) const = 0;
7777
virtual std::string export_jwk(const PublicKey& env) const = 0;
7878

79+
virtual std::unique_ptr<PrivateKey> deserialize_private_der(
80+
const bytes& der) const;
81+
7982
virtual bytes sign(const bytes& data, const PrivateKey& sk) const = 0;
8083
virtual bool verify(const bytes& data,
8184
const bytes& sig,

lib/hpke/src/group.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "openssl/ec.h"
1111
#include "openssl/evp.h"
1212
#include "openssl/obj_mac.h"
13+
#include "openssl/pem.h"
1314
#if defined(WITH_OPENSSL3)
1415
#include "openssl/core_names.h"
1516
#include "openssl/param_build.h"
@@ -761,6 +762,22 @@ struct ECKeyGroup : public EVPGroup
761762
#endif
762763
}
763764

765+
std::unique_ptr<Group::PrivateKey> deserialize_private_der(
766+
const bytes& der) const override
767+
{
768+
BIO* mem = BIO_new_mem_buf(der.data(), static_cast<int>(der.size()));
769+
if (!mem) {
770+
throw openssl_error();
771+
}
772+
EVP_PKEY* pkey = d2i_PrivateKey_bio(mem, NULL);
773+
BIO_free(mem);
774+
if (!pkey) {
775+
throw openssl_error();
776+
}
777+
778+
return std::make_unique<EVPGroup::PrivateKey>(pkey);
779+
}
780+
764781
private:
765782
int curve_nid;
766783

@@ -896,6 +913,22 @@ struct RawKeyGroup : public EVPGroup
896913
return std::make_unique<EVPGroup::PrivateKey>(pkey);
897914
}
898915

916+
std::unique_ptr<Group::PrivateKey> deserialize_private_der(
917+
const bytes& der) const override
918+
{
919+
BIO* mem = BIO_new_mem_buf(der.data(), static_cast<int>(der.size()));
920+
if (!mem) {
921+
throw openssl_error();
922+
}
923+
EVP_PKEY* pkey = d2i_PrivateKey_bio(mem, NULL);
924+
BIO_free(mem);
925+
if (!pkey) {
926+
throw openssl_error();
927+
}
928+
929+
return std::make_unique<RawKeyGroup::PrivateKey>(pkey);
930+
}
931+
899932
// Raw Key
900933
std::tuple<bytes, bytes> coordinates(
901934
const Group::PublicKey& pk) const override

lib/hpke/src/group.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ struct Group
6161
virtual bytes serialize_private(const PrivateKey& sk) const = 0;
6262
virtual std::unique_ptr<PrivateKey> deserialize_private(
6363
const bytes& skm) const = 0;
64+
virtual std::unique_ptr<PrivateKey> deserialize_private_der(
65+
const bytes& der) const = 0;
6466

6567
virtual bytes dh(const PrivateKey& sk, const PublicKey& pk) const = 0;
6668

lib/hpke/src/signature.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <openssl/bn.h>
1313
#include <openssl/ec.h>
1414
#include <openssl/evp.h>
15+
#include <openssl/pem.h>
1516
#include <openssl/rsa.h>
1617

1718
using nlohmann::json;
@@ -98,6 +99,13 @@ struct GroupSignature : public Signature
9899
group.deserialize_private(skm).release());
99100
}
100101

102+
std::unique_ptr<Signature::PrivateKey> deserialize_private_der(
103+
const bytes& der) const override
104+
{
105+
return std::make_unique<PrivateKey>(
106+
group.deserialize_private_der(der).release());
107+
}
108+
101109
bytes sign(const bytes& data, const Signature::PrivateKey& sk) const override
102110
{
103111
const auto& rsk = dynamic_cast<const PrivateKey&>(sk);
@@ -275,6 +283,12 @@ Signature::Signature(Signature::ID id_in)
275283
{
276284
}
277285

286+
std::unique_ptr<Signature::PrivateKey>
287+
Signature::deserialize_private_der(const bytes&) const
288+
{
289+
throw std::runtime_error("Not implemented");
290+
}
291+
278292
std::unique_ptr<Signature::PrivateKey>
279293
Signature::generate_rsa(size_t bits)
280294
{

src/crypto.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,16 @@ SignaturePrivateKey::parse(CipherSuite suite, const bytes& data)
478478
return { data, pub_data };
479479
}
480480

481+
SignaturePrivateKey
482+
SignaturePrivateKey::parse_der(CipherSuite suite, const bytes& data)
483+
{
484+
auto priv = suite.sig().deserialize_private_der(data);
485+
auto pub = priv->public_key();
486+
auto pub_data = suite.sig().serialize(*pub);
487+
auto priv_data = suite.sig().serialize_private(*priv);
488+
return { priv_data, pub_data };
489+
}
490+
481491
SignaturePrivateKey
482492
SignaturePrivateKey::derive(CipherSuite suite, const bytes& secret)
483493
{

test/credential.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,40 @@ TEST_CASE("X509 Credential Depth 2")
143143
CHECK(x509.der_chain == x509_original.der_chain);
144144
}
145145

146+
TEST_CASE("X509 Credential EC certificates")
147+
{
148+
// Chain is of depth 2
149+
const auto keydata =
150+
from_hex("30770201010420e32d0df2b096edadd778b3e884e84b14454d02668d3ef9f81e7"
151+
"d9425ca9acf82a00a06082a8648ce3d030107a144034200049c37d6e8722c509c"
152+
"c71bfda2389d4d3f6da6088b605c8bd6bd6dde6ccf77d3aa8c4f205cc273ac47b"
153+
"e9e353e43debf2ad9a226fe0ea7a11b5bb06eb5d932e045");
154+
const auto cert = from_hex(
155+
"308201e230820187a00302010202045a49733d300a06082a8648ce3d040302307331133011"
156+
"060a0992268993f22c6401191603636f6d31173015060a0992268993f22c64011916076d61"
157+
"696c6f757331143012060a0992268993f22c640101130474657374312d302b060355040b13"
158+
"2431313436386463622d663830342d346563362d616166362d363263623437333931333733"
159+
"301e170d3233303731353136303135305a170d3233303831353136303135305a3044311330"
160+
"11060a0992268993f22c6401191603636f6d31173015060a0992268993f22c64011916076d"
161+
"61696c6f757331143012060a0992268993f22c6401011304746573743059301306072a8648"
162+
"ce3d020106082a8648ce3d030107034200049c37d6e8722c509cc71bfda2389d4d3f6da608"
163+
"8b605c8bd6bd6dde6ccf77d3aa8c4f205cc273ac47be9e353e43debf2ad9a226fe0ea7a11b"
164+
"5bb06eb5d932e045a3383036300e0603551d0f0101ff0404030204f030160603551d250101"
165+
"ff040c300a06082b06010505070301300c0603551d130101ff04023000300a06082a8648ce"
166+
"3d0403020349003046022100d670fb29f08dd0bc5d81cd6258ade6a8e5b6a5f630b510272c"
167+
"ffa8d77f79f24e022100d166a8ae636916c84e1d854def33a57549e06b5c1d2c5e4ea7a797"
168+
"df74401531");
169+
170+
const std::vector<bytes> der_in{ cert };
171+
172+
auto key = SignaturePrivateKey::parse_der(
173+
mls::CipherSuite::ID::P256_AES128GCM_SHA256_P256, keydata);
174+
175+
auto cred = Credential::x509(der_in);
176+
auto x509 = cred.get<X509Credential>();
177+
CHECK(x509.public_key() == key.public_key);
178+
}
179+
146180
TEST_CASE("X509 Credential Depth 2 Marshal/Unmarshal")
147181
{
148182
// Chain is of depth 2

0 commit comments

Comments
 (0)