From e1b80b0a2bce29b784cbedfe95272fc5f75aa085 Mon Sep 17 00:00:00 2001 From: trigaux Date: Wed, 6 Mar 2024 16:14:38 -0500 Subject: [PATCH 1/2] Adding moctar. --- .gitmodules | 6 + cmd/.gitignore | 4 + cmd/moctar/CMakeLists.txt | 17 ++ cmd/moctar/dependencies/CMakeLists.txt | 8 + cmd/moctar/dependencies/libquicr | 1 + cmd/moctar/dependencies/mlspp | 1 + cmd/moctar/moctar.cpp | 343 +++++++++++++++++++++++++ 7 files changed, 380 insertions(+) create mode 100644 cmd/.gitignore create mode 100644 cmd/moctar/CMakeLists.txt create mode 100644 cmd/moctar/dependencies/CMakeLists.txt create mode 160000 cmd/moctar/dependencies/libquicr create mode 160000 cmd/moctar/dependencies/mlspp create mode 100644 cmd/moctar/moctar.cpp diff --git a/.gitmodules b/.gitmodules index d99adec7..129ad9d9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,3 +5,9 @@ [submodule "firmware/net/components/numero_uri/lib"] path = firmware/net/components/numero_uri/lib url = git@github.com:Quicr/numero-uri.git +[submodule "cmd/moctar/dependencies/libquicr"] + path = cmd/moctar/dependencies/libquicr + url = https://github.com/quicr/libquicr +[submodule "cmd/moctar/dependencies/mlspp"] + path = cmd/moctar/dependencies/mlspp + url = https://github.com/cisco/mlspp diff --git a/cmd/.gitignore b/cmd/.gitignore new file mode 100644 index 00000000..49faae98 --- /dev/null +++ b/cmd/.gitignore @@ -0,0 +1,4 @@ +.vscode + +build +.tmp.dprobes.h diff --git a/cmd/moctar/CMakeLists.txt b/cmd/moctar/CMakeLists.txt new file mode 100644 index 00000000..88fd7fad --- /dev/null +++ b/cmd/moctar/CMakeLists.txt @@ -0,0 +1,17 @@ +project(moctar) + +add_executable(moctar moctar.cpp) + +add_subdirectory(dependencies) +target_link_libraries(${PROJECT_NAME} LINK_PUBLIC quicr mlspp) + +target_compile_options(${PROJECT_NAME} + PRIVATE + $<$,$,$>: -Wpedantic -Wextra -Wall> + $<$: >) + +set_target_properties(${PROJECT_NAME} + PROPERTIES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED YES + CXX_EXTENSIONS OFF) diff --git a/cmd/moctar/dependencies/CMakeLists.txt b/cmd/moctar/dependencies/CMakeLists.txt new file mode 100644 index 00000000..d8b25861 --- /dev/null +++ b/cmd/moctar/dependencies/CMakeLists.txt @@ -0,0 +1,8 @@ +add_subdirectory(libquicr) + +set(OPENSSL_FOUND ON) +set(OPENSSL_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libquicr/dependencies/transport/dependencies/boringssl) +set(OPENSSL_INCLUDE_DIR ${OPENSSL_ROOT_DIR}/include) +set(OPENSSL_CRYPTO_LIBRARY ${CMAKE_BINARY_DIR}/dependencies/libquicr/dependencies/transport/dependencies/boringssl/crypto/libcrypto.a) +set(OPENSSL_SSL_LIBRARY ${CMAKE_BINARY_DIR}/dependencies/libquicr/dependencies/transport/dependencies/boringssl/ssl) +add_subdirectory(mlspp) diff --git a/cmd/moctar/dependencies/libquicr b/cmd/moctar/dependencies/libquicr new file mode 160000 index 00000000..ff97af43 --- /dev/null +++ b/cmd/moctar/dependencies/libquicr @@ -0,0 +1 @@ +Subproject commit ff97af43978d309f03b9c54740b1024e99037a0e diff --git a/cmd/moctar/dependencies/mlspp b/cmd/moctar/dependencies/mlspp new file mode 160000 index 00000000..f35e5d51 --- /dev/null +++ b/cmd/moctar/dependencies/mlspp @@ -0,0 +1 @@ +Subproject commit f35e5d51d257b40c341679e1af9cb0d0b995313f diff --git a/cmd/moctar/moctar.cpp b/cmd/moctar/moctar.cpp new file mode 100644 index 00000000..8aea9321 --- /dev/null +++ b/cmd/moctar/moctar.cpp @@ -0,0 +1,343 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace mls; + +static const CipherSuite cipher_suite = CipherSuite{ + CipherSuite::ID::P256_AES128GCM_SHA256_P256 +}; + +static const bytes group_id = from_ascii("group_id"); + +enum struct MlsMessageType : uint8_t { + key_package = 1, + welcome = 2, + commit = 3, + message = 4, +}; + +struct MLSState; + +struct PreJoinedState { + SignaturePrivateKey identity_priv; + HPKEPrivateKey init_priv; + HPKEPrivateKey leaf_priv; + LeafNode leaf_node; + KeyPackage key_package; + bytes key_package_data; + + PreJoinedState(const std::string& username); + MLSState create(); + MLSState join(const bytes& welcome_data); +}; + +struct MLSState { + bool should_commit() const; + std::tuple add(const bytes& key_package_data); + void handle(const bytes& commit_data); + + bytes protect(const bytes& plaintext); + bytes unprotect(const bytes& ciphertext); + + mls::State state; +}; + +static bytes frame(MlsMessageType msg_type, const bytes& msg) { + const auto msg_type_8 = static_cast(msg_type); + const auto type_vec = std::vector(1, msg_type_8); + return bytes(type_vec) + msg; +} + +static std::pair unframe(const bytes& framed) { + const auto& data = framed.as_vec(); + const auto msg_type = static_cast(data.at(0)); + const auto msg_data = bytes(std::vector(data.begin() + 1, data.end())); + return { msg_type, msg_data }; +} + +PreJoinedState::PreJoinedState(const std::string& username) + : identity_priv(SignaturePrivateKey::generate(cipher_suite)) + , init_priv(HPKEPrivateKey::generate(cipher_suite)) + , leaf_priv(HPKEPrivateKey::generate(cipher_suite)) +{ + auto credential = Credential::basic(from_ascii(username)); + leaf_node = LeafNode{ cipher_suite, + leaf_priv.public_key, + identity_priv.public_key, + credential, + Capabilities::create_default(), + Lifetime::create_default(), + {}, + identity_priv }; + + key_package = KeyPackage{ cipher_suite, + init_priv.public_key, + leaf_node, + {}, + identity_priv }; + + key_package_data = tls::marshal(key_package); +} + +MLSState PreJoinedState::create() { + return { State{ group_id, + cipher_suite, + leaf_priv, + identity_priv, + leaf_node, + {} } }; +} + +MLSState PreJoinedState::join(const bytes& welcome_data) { + const auto welcome = tls::get(welcome_data); + return MLSState{ State{ init_priv, + leaf_priv, + identity_priv, + key_package, + welcome, + std::nullopt, + {} } }; +} + +bool MLSState::should_commit() const +{ + return state.index() == LeafIndex{ 0 }; +} + +std::tuple MLSState::add(const bytes& key_package_data) +{ + const auto fresh_secret = random_bytes(32); + const auto key_package = tls::get(key_package_data); + const auto add = state.add_proposal(key_package); + auto [commit, welcome, next_state] = + state.commit(fresh_secret, CommitOpts{ { add }, true, false, {} }, {}); + + state = std::move(next_state); + return { tls::marshal(commit), tls::marshal(welcome) }; +} + +void MLSState::handle(const bytes& commit_data) +{ + const auto commit = tls::get(commit_data); + auto maybe_next_state = state.handle(commit); + state = std::move(maybe_next_state.value()); +} + +bytes MLSState::protect(const bytes& plaintext) +{ + const auto private_message = state.protect({}, plaintext, 0); + return tls::marshal(private_message); +} + +bytes MLSState::unprotect(const bytes& ciphertext) +{ + const auto private_message = tls::get(ciphertext); + const auto [aad, pt] = state.unprotect(private_message); + return pt; +} + +static std::optional pre_joined_state = std::nullopt; +static std::optional mls_state = std::nullopt; + +class SubDelegate : public quicr::SubscriberDelegate +{ + public: + explicit SubDelegate(cantina::LoggerPointer &logger) : logger(std::make_shared("SDEL", logger)) + { + } + + void onSubscribeResponse([[maybe_unused]] const quicr::Namespace &quicr_namespace, + [[maybe_unused]] const quicr::SubscribeResult &result) override + { + LOGGER_INFO(logger, "onSubscriptionResponse: ns=" << quicr_namespace << " status=" << static_cast(result.status)); + } + + void onSubscriptionEnded(const quicr::Namespace &quicr_namespace, + [[maybe_unused]] const quicr::SubscribeResult::SubscribeStatus &reason) override + { + LOGGER_INFO(logger, "onSubscriptionEnded: name: " << quicr_namespace); + } + + void onSubscribedObject(const quicr::Name &quicr_name, [[maybe_unused]] uint8_t priority, + [[maybe_unused]] uint16_t expiry_age_ms, [[maybe_unused]] bool use_reliable_transport, + quicr::bytes &&data) override + { + LOGGER_INFO(logger, "recv object: name: " << quicr_name << " data sz: " << data.size()); + + if (data.empty()) + { + return; + } + + std::string msg(data.begin(), data.end()); + + const auto msg_bytes = from_ascii(msg); + const auto [msg_type, msg_data] = unframe(msg_bytes); + + switch (msg_type) { + case MlsMessageType::welcome: { + if (!pre_joined_state) { + // Can't join by welcome + LOGGER_ERROR(logger, "[MLS] Ignoring Welcome; not pre-joined"); + break; + } + + LOGGER_DEBUG(logger, "[MLS] Joining"); + mls_state = pre_joined_state->join(msg_data); + pre_joined_state = std::nullopt; + break; + } + + case MlsMessageType::commit: { + if (!mls_state) { + // Can't handle commits before join + LOGGER_ERROR(logger, "[MLS] Ignoring Commit; no MLS state"); + break; + } + + LOGGER_DEBUG(logger, "[MLS] Processing commit"); + mls_state->handle(msg_data); + break; + } + + case MlsMessageType::message: { + // If we don't have MLS state, we can't decrypt + if (!mls_state) { + LOGGER_ERROR(logger, "[MLS] Ignoring message; no MLS state"); + LOGGER_INFO(logger, "[decryption failure]"); + break; + } + + LOGGER_DEBUG(logger, "[MLS] Decrypting message"); + auto plaintext = mls_state->unprotect(msg_data); + auto plaintext_str = std::string(to_ascii(plaintext)); + LOGGER_INFO(logger, plaintext_str); + break; + } + default: + break; + } + } + + void onSubscribedObjectFragment([[maybe_unused]] const quicr::Name &quicr_name, [[maybe_unused]] uint8_t priority, + [[maybe_unused]] uint16_t expiry_age_ms, + [[maybe_unused]] bool use_reliable_transport, + [[maybe_unused]] const uint64_t &offset, [[maybe_unused]] bool is_last_fragment, + [[maybe_unused]] quicr::bytes &&data) override + { + } + + private: + cantina::LoggerPointer logger; +}; + +std::atomic_bool can_publish = false; +class PubDelegate : public quicr::PublisherDelegate +{ + public: + explicit PubDelegate(cantina::LoggerPointer &logger) : logger(std::make_shared("PDEL", logger)) + { + } + + void onPublishIntentResponse(const quicr::Namespace &quicr_namespace, + const quicr::PublishIntentResult &result) override + { + LOGGER_INFO(logger, "Received PublishIntentResponse for " << quicr_namespace << ": " << static_cast(result.status)); + } + + private: + cantina::LoggerPointer logger; +}; + +static cantina::LoggerPointer logger = std::make_shared("moctar"); + +int main(int argc, char *argv[]) +try +{ + + if (argc < 4) + { + LOGGER_ERROR(logger, "Relay address and port must be provided"); + LOGGER_ERROR(logger, "Usage reallyTest 127.0.0.1 1234 FF0001"); + return EXIT_FAILURE; + } + + const std::string relayName = argv[1]; + const int port = std::stoi(std::string(argv[2])); + const quicr::Name name = std::string_view(argv[3]); + + LOGGER_INFO(logger, "Name = " << name); + LOGGER_INFO(logger, "Connecting to " << relayName << ":" << port); + + const auto relay = + quicr::RelayInfo{.hostname = relayName, .port = uint16_t(port), .proto = quicr::RelayInfo::Protocol::UDP}; + + const auto tcfg = qtransport::TransportConfig{ + .tls_cert_filename = nullptr, + .tls_key_filename = nullptr, + }; + + quicr::Client client(relay, tcfg, logger); + auto pd = std::make_shared(logger); + auto sd = std::make_shared(logger); + + if (!client.connect()) + { + logger->Log(cantina::LogLevel::Critical, "Transport connect failed"); + return EXIT_FAILURE; + } + + auto nspace = quicr::Namespace(name, 84); + + LOGGER_INFO(logger, "Publish Intent for " << nspace); + client.publishIntent(pd, nspace, {}, {}, {}); + + LOGGER_INFO(logger, "Subscribe to " << nspace); + client.subscribe(sd, nspace, quicr::SubscribeIntent::immediate, "", false, "", quicr::bytes{}); + + pre_joined_state = PreJoinedState{"mocky"}; + { + auto framed = frame(MlsMessageType::key_package, pre_joined_state->key_package_data); + client.publishNamedObject(name, 0, 1000, false, std::move(framed)); + } + + std::cout << "Send Messages (Ctrl + D to exit)" << std::endl; + std::cout << "> "; + + std::string user_entered_data; + while(std::cin >> user_entered_data) + { + if (!mls_state) continue; + + const auto plaintext_data = from_ascii(user_entered_data); + const auto ciphertext = mls_state->protect(plaintext_data); + auto&& framed = frame(MlsMessageType::message, ciphertext); + if (!framed.empty()) + { + logger->Log("Publish"); + client.publishNamedObject(name, 0, 1000, false, std::move(framed)); + } + + std::cout << "Send Messages (Ctrl + D to exit)" << std::endl; + std::cout << "> "; + } + + logger->Log("Now unsubscribing"); + client.unsubscribe(nspace, {}, {}); + + return EXIT_SUCCESS; +} +catch(const std::exception& e) +{ + LOGGER_ERROR(logger, "Caught exception: " << e.what()); + return EXIT_FAILURE; +} From de4d4a665d8eafaacb39ae44f2943b3ea4a39df2 Mon Sep 17 00:00:00 2001 From: trigaux Date: Fri, 8 Mar 2024 12:01:53 -0500 Subject: [PATCH 2/2] Adding variable size to mls signing. --- cmd/moctar/moctar.cpp | 309 ++++++++++++++++------------- firmware/ui/src/mlspp/hpke/p256.cc | 47 ++++- 2 files changed, 218 insertions(+), 138 deletions(-) diff --git a/cmd/moctar/moctar.cpp b/cmd/moctar/moctar.cpp index 8aea9321..2886209d 100644 --- a/cmd/moctar/moctar.cpp +++ b/cmd/moctar/moctar.cpp @@ -25,32 +25,104 @@ enum struct MlsMessageType : uint8_t { message = 4, }; -struct MLSState; - -struct PreJoinedState { - SignaturePrivateKey identity_priv; - HPKEPrivateKey init_priv; - HPKEPrivateKey leaf_priv; - LeafNode leaf_node; - KeyPackage key_package; - bytes key_package_data; - - PreJoinedState(const std::string& username); - MLSState create(); - MLSState join(const bytes& welcome_data); -}; +struct MLSState +{ + bool should_commit() const + { + return state.index() == LeafIndex{ 0 }; + } -struct MLSState { - bool should_commit() const; - std::tuple add(const bytes& key_package_data); - void handle(const bytes& commit_data); + std::tuple add(const bytes& key_package_data) + { + const auto fresh_secret = random_bytes(32); + const auto key_package = tls::get(key_package_data); + const auto add = state.add_proposal(key_package); + auto [commit, welcome, next_state] = + state.commit(fresh_secret, CommitOpts{ { add }, true, false, {} }, {}); + + state = std::move(next_state); + return { tls::marshal(commit), tls::marshal(welcome) }; + } - bytes protect(const bytes& plaintext); - bytes unprotect(const bytes& ciphertext); + void handle(const bytes& commit_data) + { + const auto commit = tls::get(commit_data); + auto maybe_next_state = state.handle(commit); + state = std::move(maybe_next_state.value()); + } + + bytes protect(const bytes& plaintext) + { + const auto private_message = state.protect({}, plaintext, 0); + return tls::marshal(private_message); + } + + bytes unprotect(const bytes& ciphertext) + { + const auto private_message = tls::get(ciphertext); + const auto [aad, pt] = state.unprotect(private_message); + return pt; + } mls::State state; }; +struct PreJoinedState +{ + SignaturePrivateKey identity_priv; + HPKEPrivateKey init_priv; + HPKEPrivateKey leaf_priv; + LeafNode leaf_node; + KeyPackage key_package; + bytes key_package_data; + + PreJoinedState(const std::string& username) + : identity_priv(SignaturePrivateKey::generate(cipher_suite)) + , init_priv(HPKEPrivateKey::generate(cipher_suite)) + , leaf_priv(HPKEPrivateKey::generate(cipher_suite)) + { + auto credential = Credential::basic(from_ascii(username)); + leaf_node = LeafNode{ cipher_suite, + leaf_priv.public_key, + identity_priv.public_key, + credential, + Capabilities::create_default(), + Lifetime::create_default(), + {}, + identity_priv }; + + key_package = KeyPackage{ cipher_suite, + init_priv.public_key, + leaf_node, + {}, + identity_priv }; + + key_package_data = tls::marshal(key_package); + } + + MLSState create() + { + return { State{ group_id, + cipher_suite, + leaf_priv, + identity_priv, + leaf_node, + {} } }; + } + + MLSState join(const bytes& welcome_data) + { + const auto welcome = tls::get(welcome_data); + return MLSState{ State{ init_priv, + leaf_priv, + identity_priv, + key_package, + welcome, + std::nullopt, + {} } }; + } +}; + static bytes frame(MlsMessageType msg_type, const bytes& msg) { const auto msg_type_8 = static_cast(msg_type); const auto type_vec = std::vector(1, msg_type_8); @@ -64,99 +136,18 @@ static std::pair unframe(const bytes& framed) { return { msg_type, msg_data }; } -PreJoinedState::PreJoinedState(const std::string& username) - : identity_priv(SignaturePrivateKey::generate(cipher_suite)) - , init_priv(HPKEPrivateKey::generate(cipher_suite)) - , leaf_priv(HPKEPrivateKey::generate(cipher_suite)) -{ - auto credential = Credential::basic(from_ascii(username)); - leaf_node = LeafNode{ cipher_suite, - leaf_priv.public_key, - identity_priv.public_key, - credential, - Capabilities::create_default(), - Lifetime::create_default(), - {}, - identity_priv }; - - key_package = KeyPackage{ cipher_suite, - init_priv.public_key, - leaf_node, - {}, - identity_priv }; - - key_package_data = tls::marshal(key_package); -} - -MLSState PreJoinedState::create() { - return { State{ group_id, - cipher_suite, - leaf_priv, - identity_priv, - leaf_node, - {} } }; -} - -MLSState PreJoinedState::join(const bytes& welcome_data) { - const auto welcome = tls::get(welcome_data); - return MLSState{ State{ init_priv, - leaf_priv, - identity_priv, - key_package, - welcome, - std::nullopt, - {} } }; -} - -bool MLSState::should_commit() const -{ - return state.index() == LeafIndex{ 0 }; -} - -std::tuple MLSState::add(const bytes& key_package_data) -{ - const auto fresh_secret = random_bytes(32); - const auto key_package = tls::get(key_package_data); - const auto add = state.add_proposal(key_package); - auto [commit, welcome, next_state] = - state.commit(fresh_secret, CommitOpts{ { add }, true, false, {} }, {}); - - state = std::move(next_state); - return { tls::marshal(commit), tls::marshal(welcome) }; -} - -void MLSState::handle(const bytes& commit_data) -{ - const auto commit = tls::get(commit_data); - auto maybe_next_state = state.handle(commit); - state = std::move(maybe_next_state.value()); -} - -bytes MLSState::protect(const bytes& plaintext) -{ - const auto private_message = state.protect({}, plaintext, 0); - return tls::marshal(private_message); -} - -bytes MLSState::unprotect(const bytes& ciphertext) -{ - const auto private_message = tls::get(ciphertext); - const auto [aad, pt] = state.unprotect(private_message); - return pt; -} - static std::optional pre_joined_state = std::nullopt; static std::optional mls_state = std::nullopt; class SubDelegate : public quicr::SubscriberDelegate { public: - explicit SubDelegate(cantina::LoggerPointer &logger) : logger(std::make_shared("SDEL", logger)) + explicit SubDelegate(cantina::LoggerPointer &logger, quicr::Client& c) + : logger(std::make_shared("SDEL", logger)), client{c} { } - void onSubscribeResponse([[maybe_unused]] const quicr::Namespace &quicr_namespace, - [[maybe_unused]] const quicr::SubscribeResult &result) override + void onSubscribeResponse(const quicr::Namespace &quicr_namespace, const quicr::SubscribeResult &result) override { LOGGER_INFO(logger, "onSubscriptionResponse: ns=" << quicr_namespace << " status=" << static_cast(result.status)); } @@ -167,23 +158,64 @@ class SubDelegate : public quicr::SubscriberDelegate LOGGER_INFO(logger, "onSubscriptionEnded: name: " << quicr_namespace); } - void onSubscribedObject(const quicr::Name &quicr_name, [[maybe_unused]] uint8_t priority, - [[maybe_unused]] uint16_t expiry_age_ms, [[maybe_unused]] bool use_reliable_transport, + void onSubscribedObject(const quicr::Name &name, + [[maybe_unused]] uint8_t priority, + [[maybe_unused]] uint16_t expiry_age_ms, + [[maybe_unused]] bool use_reliable_transport, quicr::bytes &&data) override + try { - LOGGER_INFO(logger, "recv object: name: " << quicr_name << " data sz: " << data.size()); + LOGGER_INFO(logger, "recv object: name: " << name << " data sz: " << data.size()); if (data.empty()) { return; } - std::string msg(data.begin(), data.end()); + const auto [msg_type, msg_data] = unframe(data); - const auto msg_bytes = from_ascii(msg); - const auto [msg_type, msg_data] = unframe(msg_bytes); + LOGGER_INFO(logger, "[MLS] Msg Type: " << static_cast(msg_type)); + LOGGER_INFO(logger, "[MLS] Msg Data: " << msg_data); switch (msg_type) { + case MlsMessageType::key_package: { + // If this is the initial creation, create the group + if (pre_joined_state) { + auto state = pre_joined_state->create(); + const auto [commit, welcome] = state.add(msg_data); + + pre_joined_state = std::nullopt; + mls_state = std::move(state); + + auto framed_welcome = frame(MlsMessageType::welcome, welcome); + client.publishNamedObject(name, 0, 1000, false, std::move(framed_welcome)); + break; + } + + // Otherwise, we should have MLS state ready + if (!mls_state) + { + LOGGER_ERROR(logger, "MLS State not set"); + break; + } + + if (!mls_state->should_commit()) + { + // We are not the committer + LOGGER_INFO(logger, "Not the committer, do nothing"); + break; + } + + const auto [commit, welcome] = mls_state->add(msg_data); + + auto framed_welcome = frame(MlsMessageType::welcome, welcome); + client.publishNamedObject(name, 0, 1000, false, std::move(framed_welcome)); + + auto framed_commit = frame(MlsMessageType::commit, commit); + client.publishNamedObject(name, 0, 1000, false, std::move(framed_commit)); + break; + } + case MlsMessageType::welcome: { if (!pre_joined_state) { // Can't join by welcome @@ -227,20 +259,26 @@ class SubDelegate : public quicr::SubscriberDelegate break; } } + catch (const std::exception& e) + { + LOGGER_ERROR(logger, "Caught exception: " << e.what()); + } - void onSubscribedObjectFragment([[maybe_unused]] const quicr::Name &quicr_name, [[maybe_unused]] uint8_t priority, + void onSubscribedObjectFragment([[maybe_unused]] const quicr::Name &quicr_name, + [[maybe_unused]] uint8_t priority, [[maybe_unused]] uint16_t expiry_age_ms, [[maybe_unused]] bool use_reliable_transport, - [[maybe_unused]] const uint64_t &offset, [[maybe_unused]] bool is_last_fragment, + [[maybe_unused]] const uint64_t &offset, + [[maybe_unused]] bool is_last_fragment, [[maybe_unused]] quicr::bytes &&data) override { } private: cantina::LoggerPointer logger; + quicr::Client& client; }; -std::atomic_bool can_publish = false; class PubDelegate : public quicr::PublisherDelegate { public: @@ -260,43 +298,45 @@ class PubDelegate : public quicr::PublisherDelegate static cantina::LoggerPointer logger = std::make_shared("moctar"); -int main(int argc, char *argv[]) +int main(int argc, char** argv) try { - if (argc < 4) { - LOGGER_ERROR(logger, "Relay address and port must be provided"); - LOGGER_ERROR(logger, "Usage reallyTest 127.0.0.1 1234 FF0001"); + LOGGER_ERROR(logger, "Relay address, port, and name must be provided"); + LOGGER_ERROR(logger, "Usage moctar 127.0.0.1 1234 FF0001"); return EXIT_FAILURE; } - const std::string relayName = argv[1]; + const std::string relay_ip = argv[1]; const int port = std::stoi(std::string(argv[2])); const quicr::Name name = std::string_view(argv[3]); - LOGGER_INFO(logger, "Name = " << name); - LOGGER_INFO(logger, "Connecting to " << relayName << ":" << port); + LOGGER_INFO(logger, "Connecting to " << relay_ip << ":" << port); - const auto relay = - quicr::RelayInfo{.hostname = relayName, .port = uint16_t(port), .proto = quicr::RelayInfo::Protocol::UDP}; + const quicr::RelayInfo relay{ + .hostname = relay_ip, + .port = uint16_t(port), + .proto = quicr::RelayInfo::Protocol::UDP, + }; - const auto tcfg = qtransport::TransportConfig{ + const qtransport::TransportConfig tcfg{ .tls_cert_filename = nullptr, .tls_key_filename = nullptr, }; + pre_joined_state = PreJoinedState{"mocky"}; quicr::Client client(relay, tcfg, logger); - auto pd = std::make_shared(logger); - auto sd = std::make_shared(logger); - if (!client.connect()) { logger->Log(cantina::LogLevel::Critical, "Transport connect failed"); return EXIT_FAILURE; } - auto nspace = quicr::Namespace(name, 84); + auto pd = std::make_shared(logger); + auto sd = std::make_shared(logger, client); + + const auto nspace = quicr::Namespace(name, 84); LOGGER_INFO(logger, "Publish Intent for " << nspace); client.publishIntent(pd, nspace, {}, {}, {}); @@ -304,26 +344,29 @@ try LOGGER_INFO(logger, "Subscribe to " << nspace); client.subscribe(sd, nspace, quicr::SubscribeIntent::immediate, "", false, "", quicr::bytes{}); - pre_joined_state = PreJoinedState{"mocky"}; + auto framed_key_package = frame(MlsMessageType::key_package, pre_joined_state->key_package_data); + client.publishNamedObject(name, 0, 1000, false, std::move(framed_key_package)); + + while (pre_joined_state != std::nullopt) { - auto framed = frame(MlsMessageType::key_package, pre_joined_state->key_package_data); - client.publishNamedObject(name, 0, 1000, false, std::move(framed)); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } std::cout << "Send Messages (Ctrl + D to exit)" << std::endl; std::cout << "> "; std::string user_entered_data; - while(std::cin >> user_entered_data) + while(std::getline(std::cin, user_entered_data)) { if (!mls_state) continue; const auto plaintext_data = from_ascii(user_entered_data); const auto ciphertext = mls_state->protect(plaintext_data); auto&& framed = frame(MlsMessageType::message, ciphertext); + if (!framed.empty()) { - logger->Log("Publish"); + LOGGER_INFO(logger, "Publishing data"); client.publishNamedObject(name, 0, 1000, false, std::move(framed)); } @@ -331,7 +374,7 @@ try std::cout << "> "; } - logger->Log("Now unsubscribing"); + LOGGER_INFO(logger, "Unsubscribing"); client.unsubscribe(nspace, {}, {}); return EXIT_SUCCESS; diff --git a/firmware/ui/src/mlspp/hpke/p256.cc b/firmware/ui/src/mlspp/hpke/p256.cc index 44db3e30..f344affa 100644 --- a/firmware/ui/src/mlspp/hpke/p256.cc +++ b/firmware/ui/src/mlspp/hpke/p256.cc @@ -64,11 +64,11 @@ PrivateKey generate_key_pair(){ } bytes serialize(const PublicKey& pk){ - return pk.pub; + return bytes(1, 0x04) + pk.pub; } PublicKey deserialize(const bytes& enc){ - return { enc }; + return { enc.slice(1, enc.size()) }; } bytes serialize_private(const PrivateKey& sk){ @@ -130,13 +130,50 @@ bytes sign(const bytes& data, const PrivateKey& sk) { } sig.resize(sig_size); - return sig; + + const auto half_size = sig_size / 2; + + const auto r = sig.slice(0, half_size); + auto r_header = bytes{0x02, static_cast(r.size())}; + if ((r.at(0) & 0x80) == 1) + { + r_header.at(1) += 1; + r_header.push_back(0x00); + } + + const auto s = sig.slice(half_size, sig_size); + auto s_header = bytes{0x02, static_cast(s.size())}; + if ((s.at(0) & 0x80) == 1) + { + s_header.at(1) += 1; + s_header.push_back(0x00); + } + + const auto sequence_data = r_header + r + s_header + s; + const auto sequence_header = bytes{0x80, static_cast(sequence_data.size())}; + + return sequence_header + sequence_data; } bool verify(const bytes& data, const bytes& sig, const PublicKey& pk) { const auto sha256 = Digest::get(); const auto digest = sha256.hash(data); + static constexpr auto sequence_header_size = 2; + static constexpr auto sig_int_size = 32; + + const auto sequence_data = sig.slice(sequence_header_size, sig.size()); + + const auto r_size = sequence_data.at(1); + auto r = sig.slice(sequence_header_size, r_size); + if (r_size > sig_int_size) { r = r.slice(1, r.size()); } + + const auto s_size = sequence_data.at(sequence_header_size + r_size + 1); + auto s = sig.slice(sequence_header_size * 2 + r.size(), sequence_data.size()); + if (s_size > sig_int_size) { s = s.slice(1, s.size()); } + + const auto real_sig = r + s; + auto fault_check = uint32_t(0); auto ctx = ECCContext(); const auto rv = cmox_ecdsa_verify(ctx.get(), @@ -145,8 +182,8 @@ bool verify(const bytes& data, const bytes& sig, const PublicKey& pk) { pk.pub.size(), digest.data(), digest.size(), - sig.data(), - sig.size(), + real_sig.data(), + real_sig.size(), &fault_check); if (rv != CMOX_ECC_AUTH_SUCCESS) {