diff --git a/cmd/interop/CMakeLists.txt b/cmd/interop/CMakeLists.txt index 9ea99071..a824ed4a 100644 --- a/cmd/interop/CMakeLists.txt +++ b/cmd/interop/CMakeLists.txt @@ -38,6 +38,7 @@ find_package(OpenSSL 1.1 REQUIRED) find_package(Protobuf REQUIRED) find_package(gRPC CONFIG REQUIRED) find_package(gflags REQUIRED) +find_package(ICU REQUIRED COMPONENTS uc data) # mlspp set(CMAKE_EXPORT_PACKAGE_REGISTRY ON) @@ -50,7 +51,7 @@ find_package(nlohmann_json 3.2 REQUIRED) ### Protobuf generation ### -# Get the proto file from the interop repo +# Get the proto file from the interop repo include( ExternalProject ) find_package( Git REQUIRED ) 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") add_executable(${APP_NAME} ${BIN_SOURCES} ${PB_SRC} ${GRPC_SRC}) add_dependencies(${APP_NAME} mls-interop-extern) target_include_directories(${APP_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) -target_link_libraries(${APP_NAME} - gflags - gRPC::grpc++ protobuf::libprotobuf +target_link_libraries(${APP_NAME} + gflags ICU::uc ICU::data + gRPC::grpc++ protobuf::libprotobuf MLSPP::mlspp MLSPP::mls_vectors MLSPP::tls_syntax) diff --git a/include/mls/crypto.h b/include/mls/crypto.h index dc467c0d..baa9fb03 100644 --- a/include/mls/crypto.h +++ b/include/mls/crypto.h @@ -238,6 +238,7 @@ struct SignaturePrivateKey { static SignaturePrivateKey generate(CipherSuite suite); static SignaturePrivateKey parse(CipherSuite suite, const bytes& data); + static SignaturePrivateKey parse_der(CipherSuite suite, const bytes& data); static SignaturePrivateKey derive(CipherSuite suite, const bytes& secret); static SignaturePrivateKey from_jwk(CipherSuite suite, const std::string& json_str); diff --git a/lib/hpke/include/hpke/signature.h b/lib/hpke/include/hpke/signature.h index 311baa18..f1d2ebdd 100644 --- a/lib/hpke/include/hpke/signature.h +++ b/lib/hpke/include/hpke/signature.h @@ -74,6 +74,9 @@ struct Signature virtual std::string export_jwk_private(const PrivateKey& env) const = 0; virtual std::string export_jwk(const PublicKey& env) const = 0; + virtual std::unique_ptr deserialize_private_der( + const bytes& der) const; + virtual bytes sign(const bytes& data, const PrivateKey& sk) const = 0; virtual bool verify(const bytes& data, const bytes& sig, diff --git a/lib/hpke/src/group.cpp b/lib/hpke/src/group.cpp index cf02b1e0..cc4d684f 100644 --- a/lib/hpke/src/group.cpp +++ b/lib/hpke/src/group.cpp @@ -9,6 +9,7 @@ #include "openssl/ec.h" #include "openssl/evp.h" #include "openssl/obj_mac.h" +#include "openssl/pem.h" #if defined(WITH_OPENSSL3) #include "openssl/core_names.h" #include "openssl/param_build.h" @@ -702,6 +703,22 @@ struct ECKeyGroup : public EVPGroup #endif } + std::unique_ptr deserialize_private_der( + const bytes& der) const override + { + BIO* mem = BIO_new_mem_buf(der.data(), static_cast(der.size())); + if (!mem) { + throw openssl_error(); + } + EVP_PKEY* pkey = d2i_PrivateKey_bio(mem, NULL); + BIO_free(mem); + if (!pkey) { + throw openssl_error(); + } + + return std::make_unique(pkey); + } + private: int curve_nid; @@ -827,6 +844,22 @@ struct RawKeyGroup : public EVPGroup return std::make_unique(pkey); } + std::unique_ptr deserialize_private_der( + const bytes& der) const override + { + BIO* mem = BIO_new_mem_buf(der.data(), static_cast(der.size())); + if (!mem) { + throw openssl_error(); + } + EVP_PKEY* pkey = d2i_PrivateKey_bio(mem, NULL); + BIO_free(mem); + if (!pkey) { + throw openssl_error(); + } + + return std::make_unique(pkey); + } + // Raw Key std::tuple coordinates( const Group::PublicKey& pk) const override diff --git a/lib/hpke/src/group.h b/lib/hpke/src/group.h index d871e1ed..5f72bf9a 100644 --- a/lib/hpke/src/group.h +++ b/lib/hpke/src/group.h @@ -58,6 +58,8 @@ struct Group virtual bytes serialize_private(const PrivateKey& sk) const = 0; virtual std::unique_ptr deserialize_private( const bytes& skm) const = 0; + virtual std::unique_ptr deserialize_private_der( + const bytes& der) const = 0; virtual bytes dh(const PrivateKey& sk, const PublicKey& pk) const = 0; diff --git a/lib/hpke/src/signature.cpp b/lib/hpke/src/signature.cpp index 16cfe117..abca44e8 100644 --- a/lib/hpke/src/signature.cpp +++ b/lib/hpke/src/signature.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include using nlohmann::json; @@ -96,6 +97,13 @@ struct GroupSignature : public Signature group.deserialize_private(skm).release()); } + std::unique_ptr deserialize_private_der( + const bytes& der) const override + { + return std::make_unique( + group.deserialize_private_der(der).release()); + } + bytes sign(const bytes& data, const Signature::PrivateKey& sk) const override { const auto& rsk = dynamic_cast(sk); @@ -271,6 +279,12 @@ Signature::Signature(Signature::ID id_in) { } +std::unique_ptr +Signature::deserialize_private_der(const bytes&) const +{ + throw std::runtime_error("Not implemented"); +} + std::unique_ptr Signature::generate_rsa(size_t bits) { diff --git a/src/crypto.cpp b/src/crypto.cpp index 1986659c..58b937d0 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -428,6 +428,16 @@ SignaturePrivateKey::parse(CipherSuite suite, const bytes& data) return { data, pub_data }; } +SignaturePrivateKey +SignaturePrivateKey::parse_der(CipherSuite suite, const bytes& data) +{ + auto priv = suite.sig().deserialize_private_der(data); + auto pub = priv->public_key(); + auto pub_data = suite.sig().serialize(*pub); + auto priv_data = suite.sig().serialize_private(*priv); + return { priv_data, pub_data }; +} + SignaturePrivateKey SignaturePrivateKey::derive(CipherSuite suite, const bytes& secret) { diff --git a/test/credential.cpp b/test/credential.cpp index d8939747..f2be6116 100644 --- a/test/credential.cpp +++ b/test/credential.cpp @@ -143,6 +143,40 @@ TEST_CASE("X509 Credential Depth 2") CHECK(x509.der_chain == x509_original.der_chain); } +TEST_CASE("X509 Credential EC certificates") +{ + // Chain is of depth 2 + const auto keydata = + from_hex("30770201010420e32d0df2b096edadd778b3e884e84b14454d02668d3ef9f81e7" + "d9425ca9acf82a00a06082a8648ce3d030107a144034200049c37d6e8722c509c" + "c71bfda2389d4d3f6da6088b605c8bd6bd6dde6ccf77d3aa8c4f205cc273ac47b" + "e9e353e43debf2ad9a226fe0ea7a11b5bb06eb5d932e045"); + const auto cert = from_hex( + "308201e230820187a00302010202045a49733d300a06082a8648ce3d040302307331133011" + "060a0992268993f22c6401191603636f6d31173015060a0992268993f22c64011916076d61" + "696c6f757331143012060a0992268993f22c640101130474657374312d302b060355040b13" + "2431313436386463622d663830342d346563362d616166362d363263623437333931333733" + "301e170d3233303731353136303135305a170d3233303831353136303135305a3044311330" + "11060a0992268993f22c6401191603636f6d31173015060a0992268993f22c64011916076d" + "61696c6f757331143012060a0992268993f22c6401011304746573743059301306072a8648" + "ce3d020106082a8648ce3d030107034200049c37d6e8722c509cc71bfda2389d4d3f6da608" + "8b605c8bd6bd6dde6ccf77d3aa8c4f205cc273ac47be9e353e43debf2ad9a226fe0ea7a11b" + "5bb06eb5d932e045a3383036300e0603551d0f0101ff0404030204f030160603551d250101" + "ff040c300a06082b06010505070301300c0603551d130101ff04023000300a06082a8648ce" + "3d0403020349003046022100d670fb29f08dd0bc5d81cd6258ade6a8e5b6a5f630b510272c" + "ffa8d77f79f24e022100d166a8ae636916c84e1d854def33a57549e06b5c1d2c5e4ea7a797" + "df74401531"); + + const std::vector der_in{ cert }; + + auto key = SignaturePrivateKey::parse_der( + mls::CipherSuite::ID::P256_AES128GCM_SHA256_P256, keydata); + + auto cred = Credential::x509(der_in); + auto x509 = cred.get(); + CHECK(x509.public_key() == key.public_key); +} + TEST_CASE("X509 Credential Depth 2 Marshal/Unmarshal") { // Chain is of depth 2