Skip to content

Commit

Permalink
Add MLSMessage framework (#262)
Browse files Browse the repository at this point in the history
* Add MLSMessage framework

Add MLSMessage

Add tests of MLSMessage framework functionality

Begin adding support for MLSMessage-formatted proposals

Add MLSMessage-based Commit

Migrate State tests to new MLSMessage methods

Move Session over to MLSMessage framework

Convert application data protect/unprotect to MLSMessage

Move external join to MLSMessage framework

Remove MLSPlaintext/MLSCiphertext from state.h/.cpp

Remove MLSPlaintext/MLSCiphertext from the remainder of src/*

Remove old MLSPlaintext and MLSCiphertext

Remove namespace mls2

Update test vectors to use MLSMessage

Dead code removal

Remove rvalue reference silliness

clang-format

* Fix build errors

* Revert refactor of confirmation tag computation

* clang-tidy

* CI errors

* CI errors

* Apply suggestions from code review

* Respond to @bifurcation review comments

* Update the interop harness

* CI errors

* clang-format

* Respond to comments from @suhasHere

* CI errors

* Noop commit to trigger CI

* Fix some clang-tidy errors

* More clang-tidy errors

* clang-format

* Ignore cast formatting errors, too many false positives

* Revert "More clang-tidy errors"

This reverts commit 2d2c438.

* Revert "Fix some clang-tidy errors"

This reverts commit 9cbc05a.

* Cleanup from reverts

* Add missing comma in .clang-tidy

* Adjust NOLINT

* Update clang-format-action to latest

* Format the interop harness

* Apply clang-format-14

* Suppress some clang-tidy warnings

* More clang-tidy

* More clang-tidy

* clang-format

* Enable ASan on Windows

* Enable stricter compilation on Windows

* Dial back MSVC errors when they stop making sense

* Revert some stuff

* Don't have global objects that use the heap

* Attempt a different bytes import constructor

* Actually use the new ctor

* Properly declare sanitization with MSVC

* Don't use static const closures with std::visit

* Fix compile errors

* More static const closures

* clang-format
  • Loading branch information
bifurcation committed Jul 25, 2022
1 parent fb48d7b commit b21adfa
Show file tree
Hide file tree
Showing 28 changed files with 198,205 additions and 17,568 deletions.
12 changes: 10 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,22 @@ elseif(MSVC)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
endif()

if (SANITIZERS AND (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU"))
set(SANITIZERS "-fsanitize=address -fsanitize=undefined")
if (SANITIZERS)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set(SANITIZERS "-fsanitize=address -fsanitize=undefined")
elseif(MSVC)
set(SANITIZERS "/fsanitize=address")
endif()

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SANITIZERS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZERS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZERS}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${SANITIZERS}")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${SANITIZERS}")
add_definitions(-DSANITIZERS)
elseif (SANITIZERS AND MSVC)
message("Enabling sanitizers")
add_definitions("/fsanitize=address")
endif()

if(CLANG_TIDY)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ Conventions
* Snake case for variables, functions, members (`derive_epoch_keys`)
* Private member variables start with underscore (`_`)
* In general, prefer descriptive names

14 changes: 9 additions & 5 deletions cmd/interop/src/json_details.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ struct tls_serializer
TLS_SERIALIZER(mls::HPKEPublicKey)
TLS_SERIALIZER(mls::TreeKEMPublicKey)
TLS_SERIALIZER(mls::Credential)
TLS_SERIALIZER(mls::MLSMessageContentAuth)
TLS_SERIALIZER(mls::MLSPlaintext)
TLS_SERIALIZER(mls::LeafNode)
TLS_SERIALIZER(mls::UpdatePath)
Expand Down Expand Up @@ -126,18 +127,21 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(EncryptionTestVector::SenderDataInfo,
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(EncryptionTestVector::RatchetStep,
key,
nonce,
plaintext,
ciphertext)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(EncryptionTestVector::LeafInfo,
generations,
handshake_content_auth,
application_content_auth,
handshake,
application)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(EncryptionTestVector,
cipher_suite,
tree,
encryption_secret,
sender_data_secret,
padding_size,
sender_data_info,
authenticated_data,
leaves)

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(KeyScheduleTestVector::ExternalPSKInfo,
Expand Down Expand Up @@ -177,7 +181,6 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(TranscriptTestVector,
tree_hash_before,
confirmed_transcript_hash_before,
interim_transcript_hash_before,
membership_key,
confirmation_key,
credential,
commit,
Expand Down Expand Up @@ -216,9 +219,10 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(MessagesTestVector,
external_init_proposal,
app_ack_proposal,
commit,
mls_plaintext_application,
mls_plaintext_proposal,
mls_plaintext_commit,
content_auth_app,
content_auth_proposal,
content_auth_commit,
mls_plaintext,
mls_ciphertext)

} // namespace mls_vectors
61 changes: 33 additions & 28 deletions cmd/interop/src/mls_client_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,12 @@ MLSClientImpl::load_join(uint32_t join_id)
}

// Cached group state
mls::MessageOpts
MLSClientImpl::CachedState::message_opts() const
{
return { {}, encrypt_handshake, 0 };
}

void
MLSClientImpl::CachedState::reset_pending()
{
Expand All @@ -249,25 +255,15 @@ MLSClientImpl::CachedState::reset_pending()
}

std::string
MLSClientImpl::CachedState::marshal(const mls::MLSPlaintext& pt)
MLSClientImpl::CachedState::marshal(const mls::MLSMessage& msg)
{
if (encrypt_handshake) {
auto ct = state.encrypt(pt);
return bytes_to_string(tls::marshal(ct));
}

return bytes_to_string(tls::marshal(pt));
return bytes_to_string(tls::marshal(msg));
}

mls::MLSPlaintext
mls::MLSMessage
MLSClientImpl::CachedState::unmarshal(const std::string& wire)
{
if (encrypt_handshake) {
auto ct = tls::get<mls::MLSCiphertext>(string_to_bytes(wire));
return state.decrypt(ct);
}

return tls::get<mls::MLSPlaintext>(string_to_bytes(wire));
return tls::get<mls::MLSMessage>(string_to_bytes(wire));
}

uint32_t
Expand Down Expand Up @@ -513,11 +509,12 @@ MLSClientImpl::external_join(const ExternalJoinRequest* request,

auto kp = mls::KeyPackage(suite, init_priv.public_key, leaf, {}, sig_priv);

auto encrypt = request->encrypt_handshake();
auto leaf_secret = mls::random_bytes(suite.secret_size());
auto [commit, state] = mls::State::external_join(
leaf_secret, sig_priv, kp, group_info, std::nullopt);
leaf_secret, sig_priv, kp, group_info, std::nullopt, { {}, encrypt, 0 });
auto commit_data = tls::marshal(commit);
auto state_id = store_state(std::move(state), request->encrypt_handshake());
auto state_id = store_state(std::move(state), encrypt);

response->set_state_id(state_id);
response->set_commit(bytes_to_string(commit_data));
Expand Down Expand Up @@ -564,7 +561,7 @@ MLSClientImpl::protect(CachedState& entry,
ProtectResponse* response)
{
auto pt = string_to_bytes(request->application_data());
auto ct = entry.state.protect(pt);
auto ct = entry.state.protect({}, pt, 0);
auto ct_data = tls::marshal(ct);
response->set_ciphertext(bytes_to_string(ct_data));
return Status::OK;
Expand All @@ -577,7 +574,11 @@ MLSClientImpl::unprotect(CachedState& entry,
{
auto ct_data = string_to_bytes(request->ciphertext());
auto ct = tls::get<mls::MLSCiphertext>(ct_data);
auto pt = entry.state.unprotect(ct);
auto [aad, pt] = entry.state.unprotect(ct);
mls::silence_unused(aad);

// TODO(RLB) We should update the gRPC spec so that it returns the AAD as well
// as the plaintext.
response->set_application_data(bytes_to_string(pt));
return Status::OK;
}
Expand All @@ -591,9 +592,9 @@ MLSClientImpl::add_proposal(CachedState& entry,
auto key_package_data = string_to_bytes(request->key_package());
auto key_package = tls::get<mls::KeyPackage>(key_package_data);

auto proposal = entry.state.add(key_package);
auto message = entry.state.add(key_package, entry.message_opts());

response->set_proposal(entry.marshal(proposal));
response->set_proposal(entry.marshal(message));
return Status::OK;
}

Expand All @@ -605,16 +606,18 @@ MLSClientImpl::commit(CachedState& entry,
// Unmarshal and handle external / by_reference proposals
const auto by_reference_size = request->by_reference_size();
for (int i = 0; i < by_reference_size; i++) {
auto pt = entry.unmarshal(request->by_reference(i));
auto should_be_null = entry.state.handle(pt);
auto msg = entry.unmarshal(request->by_reference(i));
auto should_be_null = entry.state.handle(msg);
if (should_be_null) {
throw std::runtime_error("Commit included among proposals");
}
}

auto inline_proposals = std::vector<mls::Proposal>(request->by_value_size());
auto inline_proposals = std::vector<mls::Proposal>();
#if 0
// TODO(#265): Re-enable
for (size_t i = 0; i < inline_proposals.size(); i++) {
auto pt = entry.unmarshal(request->by_value(i));
auto msg = entry.unmarshal(request->by_value(i));
if (pt.sender.sender != entry.state.index().val) {
return Status(grpc::INVALID_ARGUMENT,
"Inline proposal not from this member");
Expand All @@ -627,12 +630,14 @@ MLSClientImpl::commit(CachedState& entry,

inline_proposals[i] = std::move(*proposal);
}
#endif // 0

auto leaf_secret =
mls::random_bytes(entry.state.cipher_suite().secret_size());
auto [commit, welcome, next] = entry.state.commit(
leaf_secret,
mls::CommitOpts{ inline_proposals, true, entry.encrypt_handshake, {} });
mls::CommitOpts{ inline_proposals, true, entry.encrypt_handshake, {} },
entry.message_opts());

auto next_id = store_state(std::move(next), entry.encrypt_handshake);

Expand Down Expand Up @@ -667,8 +672,8 @@ MLSClientImpl::handle_commit(CachedState& entry,
// Handle the provided proposals, then the commit
const auto proposal_size = request->proposal_size();
for (int i = 0; i < proposal_size; i++) {
auto pt = entry.unmarshal(request->proposal(i));
auto should_be_null = entry.state.handle(pt);
auto msg = entry.unmarshal(request->proposal(i));
auto should_be_null = entry.state.handle(msg);
if (should_be_null) {
throw std::runtime_error("Commit included among proposals");
}
Expand All @@ -693,7 +698,7 @@ MLSClientImpl::handle_external_commit(
HandleExternalCommitResponse* response)
{
auto commit_data = string_to_bytes(request->commit());
auto commit = tls::get<mls::MLSPlaintext>(commit_data);
auto commit = tls::get<mls::MLSMessage>(commit_data);
auto should_be_next = entry.state.handle(commit);
if (!should_be_next) {
throw std::runtime_error("Commit failed to produce a new state");
Expand Down
5 changes: 3 additions & 2 deletions cmd/interop/src/mls_client_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,15 @@ class MLSClientImpl final : public MLSClient::Service
{
mls::State state;
bool encrypt_handshake;
mls::MessageOpts message_opts() const;

std::optional<std::string> pending_commit;
std::optional<uint32_t> pending_state_id;
void reset_pending();

// Marshal/unmarshal with encryption as required
std::string marshal(const mls::MLSPlaintext& pt);
mls::MLSPlaintext unmarshal(const std::string& wire);
std::string marshal(const mls::MLSMessage& msg);
mls::MLSMessage unmarshal(const std::string& wire);
};

std::map<uint32_t, CachedState> state_cache;
Expand Down
29 changes: 13 additions & 16 deletions include/mls/key_schedule.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ struct SecretTree
size_t secret_size;
};

using ReuseGuard = std::array<uint8_t, 4>;

struct GroupKeySource
{
enum struct RatchetType
Expand All @@ -64,17 +66,13 @@ struct GroupKeySource
LeafCount group_size,
bytes encryption_secret);

std::tuple<uint32_t, KeyAndNonce> next(RatchetType type, LeafIndex sender);
KeyAndNonce get(RatchetType type, LeafIndex sender, uint32_t generation);
void erase(RatchetType type, LeafIndex sender, uint32_t generation);

MLSCiphertext encrypt(const TreeKEMPublicKey& tree,
LeafIndex index,
const bytes& sender_data_secret,
const MLSPlaintext& pt);
MLSPlaintext decrypt(const TreeKEMPublicKey& tree,
const bytes& sender_data_secret,
const MLSCiphertext& ct);
std::tuple<uint32_t, ReuseGuard, KeyAndNonce> next(ContentType content_type,
LeafIndex sender);
KeyAndNonce get(ContentType content_type,
LeafIndex sender,
uint32_t generation,
ReuseGuard reuse_guard);
void erase(ContentType type, LeafIndex sender, uint32_t generation);

private:
CipherSuite suite;
Expand All @@ -84,6 +82,7 @@ struct GroupKeySource
std::map<Key, HashRatchet> chains;

HashRatchet& chain(RatchetType type, LeafIndex sender);
HashRatchet& chain(ContentType type, LeafIndex sender);

static const std::array<RatchetType, 2> all_ratchet_types;
};
Expand Down Expand Up @@ -144,8 +143,6 @@ struct KeyScheduleEpoch
const bytes& context) const;

GroupKeySource encryption_keys(LeafCount size) const;
bytes membership_tag(const GroupContext& context,
const MLSPlaintext& pt) const;
bytes confirmation_tag(const bytes& confirmed_transcript_hash) const;
bytes do_export(const std::string& label,
const bytes& context,
Expand Down Expand Up @@ -178,10 +175,10 @@ struct TranscriptHash
bytes confirmed_in,
const bytes& confirmation_tag);

void update(const MLSPlaintext& pt);
void update_confirmed(const MLSPlaintext& pt);
void update(const MLSMessageContentAuth& content_auth);
void update_confirmed(const MLSMessageContentAuth& content_auth);
void update_interim(const bytes& confirmation_tag);
void update_interim(const MLSPlaintext& pt);
void update_interim(const MLSMessageContentAuth& content_auth);
};

bool
Expand Down
Loading

0 comments on commit b21adfa

Please sign in to comment.