diff --git a/cpp/src/lib.rs b/cpp/src/lib.rs index 81fd57c..4d4dd7a 100644 --- a/cpp/src/lib.rs +++ b/cpp/src/lib.rs @@ -1,189 +1,189 @@ mod account; mod group_sessions; mod sas; mod session; mod types; use account::{account_from_pickle, account_from_libolm_pickle, new_account, olm_message_from_parts, Account, OlmMessage}; use group_sessions::{ exported_session_key_from_base64, group_session_from_pickle, group_session_from_libolm_pickle, import_inbound_group_session, inbound_group_session_from_pickle, inbound_group_session_from_libolm_pickle, megolm_message_from_base64, new_group_session, new_inbound_group_session, session_key_from_base64, ExportedSessionKey, GroupSession, InboundGroupSession, MegolmMessage, SessionKey, }; use sas::{mac_from_base64, new_sas, EstablishedSas, Mac, Sas, SasBytes}; use session::{session_from_pickle, session_from_libolm_pickle, Session}; use types::{ curve_key_from_base64, ed25519_key_from_base64, ed25519_signature_from_base64, Curve25519PublicKey, Ed25519PublicKey, Ed25519Signature, }; #[cxx::bridge] mod ffi { - #[namespace = "olm"] + #[namespace = "vodozemac::olm"] struct OlmMessageParts { message_type: usize, ciphertext: String, } - #[namespace = "olm"] + #[namespace = "vodozemac::olm"] pub struct InboundCreationResult { pub session: Box<Session>, pub plaintext: Vec<u8>, } - #[namespace = "olm"] + #[namespace = "vodozemac::olm"] struct OneTimeKey { key_id: String, key: Box<Curve25519PublicKey>, } - #[namespace = "olm"] + #[namespace = "vodozemac::olm"] #[derive(PartialEq, Eq)] struct SessionKeys { identity_key: Box<Curve25519PublicKey>, base_key: Box<Curve25519PublicKey>, one_time_key: Box<Curve25519PublicKey>, } - #[namespace = "types"] + #[namespace = "vodozemac::types"] extern "Rust" { type Curve25519PublicKey; fn curve_key_from_base64(key: &str) -> Result<Box<Curve25519PublicKey>>; fn to_base64(self: &Curve25519PublicKey) -> String; type Ed25519PublicKey; fn ed25519_key_from_base64(key: &str) -> Result<Box<Ed25519PublicKey>>; fn to_base64(self: &Ed25519PublicKey) -> String; fn verify(self: &Ed25519PublicKey, message: &str, signature: &Ed25519Signature) -> Result<()>; type Ed25519Signature; fn ed25519_signature_from_base64(key: &str) -> Result<Box<Ed25519Signature>>; fn to_base64(self: &Ed25519Signature) -> String; } - #[namespace = "olm"] + #[namespace = "vodozemac::olm"] extern "Rust" { type Account; fn new_account() -> Box<Account>; fn ed25519_key(self: &Account) -> Box<Ed25519PublicKey>; fn curve25519_key(self: &Account) -> Box<Curve25519PublicKey>; fn sign(self: &Account, message: &str) -> Box<Ed25519Signature>; fn generate_one_time_keys(self: &mut Account, count: usize); fn one_time_keys(self: &Account) -> Vec<OneTimeKey>; fn generate_fallback_key(self: &mut Account); fn fallback_key(self: &Account) -> Vec<OneTimeKey>; fn mark_keys_as_published(self: &mut Account); fn max_number_of_one_time_keys(self: &Account) -> usize; fn account_from_pickle(pickle: &str, pickle_key: &[u8; 32]) -> Result<Box<Account>>; fn account_from_libolm_pickle(pickle: &str, pickle_key: &[u8]) -> Result<Box<Account>>; fn pickle(self: &Account, pickle_key: &[u8; 32]) -> String; fn create_outbound_session( self: &Account, identity_key: &Curve25519PublicKey, one_time_key: &Curve25519PublicKey, ) -> Result<Box<Session>>; fn create_inbound_session( self: &mut Account, identity_key: &Curve25519PublicKey, message: &OlmMessage, ) -> Result<InboundCreationResult>; type Session; fn session_id(self: &Session) -> String; fn session_keys(self: &Session) -> SessionKeys; fn session_matches(self: &Session, message: &OlmMessage) -> bool; fn encrypt(self: &mut Session, plaintext: &str) -> Box<OlmMessage>; fn decrypt(self: &mut Session, message: &OlmMessage) -> Result<Vec<u8>>; fn session_from_pickle(pickle: &str, pickle_key: &[u8; 32]) -> Result<Box<Session>>; fn session_from_libolm_pickle(pickle: &str, pickle_key: &[u8]) -> Result<Box<Session>>; fn pickle(self: &Session, pickle_key: &[u8; 32]) -> String; type OlmMessage; fn to_parts(self: &OlmMessage) -> OlmMessageParts; fn olm_message_from_parts(parts: &OlmMessageParts) -> Result<Box<OlmMessage>>; } - #[namespace = "megolm"] + #[namespace = "vodozemac::megolm"] struct DecryptedMessage { plaintext: Vec<u8>, message_index: u32, } - #[namespace = "megolm"] + #[namespace = "vodozemac::megolm"] extern "Rust" { type MegolmMessage; fn megolm_message_from_base64(message: &str) -> Result<Box<MegolmMessage>>; fn to_base64(self: &MegolmMessage) -> String; type SessionKey; fn session_key_from_base64(key: &str) -> Result<Box<SessionKey>>; fn to_base64(self: &SessionKey) -> String; type ExportedSessionKey; fn exported_session_key_from_base64(key: &str) -> Result<Box<ExportedSessionKey>>; fn to_base64(self: &ExportedSessionKey) -> String; type GroupSession; fn new_group_session() -> Box<GroupSession>; fn encrypt(self: &mut GroupSession, plaintext: &str) -> Box<MegolmMessage>; fn session_id(self: &GroupSession) -> String; fn session_key(self: &GroupSession) -> Box<SessionKey>; fn message_index(self: &GroupSession) -> u32; fn pickle(self: &GroupSession, pickle_key: &[u8; 32]) -> String; fn group_session_from_pickle( pickle: &str, pickle_key: &[u8; 32], ) -> Result<Box<GroupSession>>; fn group_session_from_libolm_pickle( pickle: &str, pickle_key: &[u8], ) -> Result<Box<GroupSession>>; type InboundGroupSession; fn new_inbound_group_session(session_key: &SessionKey) -> Box<InboundGroupSession>; fn import_inbound_group_session( session_key: &ExportedSessionKey, ) -> Box<InboundGroupSession>; fn decrypt( self: &mut InboundGroupSession, message: &MegolmMessage, ) -> Result<DecryptedMessage>; fn session_id(self: &InboundGroupSession) -> String; fn first_known_index(self: &InboundGroupSession) -> u32; fn export_at( self: &mut InboundGroupSession, message_index: u32, ) -> Result<Box<ExportedSessionKey>>; fn pickle(self: &InboundGroupSession, pickle_key: &[u8; 32]) -> String; fn inbound_group_session_from_pickle( pickle: &str, pickle_key: &[u8; 32], ) -> Result<Box<InboundGroupSession>>; fn inbound_group_session_from_libolm_pickle( pickle: &str, pickle_key: &[u8], ) -> Result<Box<InboundGroupSession>>; } - #[namespace = "sas"] + #[namespace = "vodozemac::sas"] extern "Rust" { type Mac; fn mac_from_base64(mac: &str) -> Result<Box<Mac>>; fn to_base64(self: &Mac) -> String; type Sas; fn new_sas() -> Box<Sas>; fn public_key(self: &Sas) -> Box<Curve25519PublicKey>; fn diffie_hellman( self: &mut Sas, other_public_key: &Curve25519PublicKey, ) -> Result<Box<EstablishedSas>>; type EstablishedSas; fn bytes(self: &EstablishedSas, info: &str) -> Box<SasBytes>; fn calculate_mac(self: &EstablishedSas, input: &str, info: &str) -> Box<Mac>; fn verify_mac(self: &EstablishedSas, input: &str, info: &str, mac: &Mac) -> Result<()>; type SasBytes; fn emoji_indices(self: &SasBytes) -> [u8; 7]; fn decimals(self: &SasBytes) -> [u16; 3]; } } diff --git a/cpp/tests/account.cpp b/cpp/tests/account.cpp index 6c517db..89b94c3 100644 --- a/cpp/tests/account.cpp +++ b/cpp/tests/account.cpp @@ -1,123 +1,124 @@ #include "../../target/cxxbridge/vodozemac/src/lib.rs.h" #include <string> #include <unordered_set> #include <olm/olm.h> #include <boost/json.hpp> #include "gtest/gtest.h" #include "util.hpp" using namespace rust; +using namespace vodozemac; TEST(AccountTest, AccountCreation) { auto alice = olm::new_account(); auto key = alice->ed25519_key(); auto encoded_key = key->to_base64(); EXPECT_NE(encoded_key.length(), 0); } TEST(AccountTest, OneTimeKeyGeneration) { auto alice = olm::new_account(); EXPECT_EQ(alice->one_time_keys().size(), 0); alice->generate_one_time_keys(10); EXPECT_EQ(alice->one_time_keys().size(), 10); alice->mark_keys_as_published(); EXPECT_EQ(alice->one_time_keys().size(), 0); } TEST(AccountTest, FallbackKeyGeneration) { auto alice = olm::new_account(); EXPECT_EQ(alice->fallback_key().size(), 0); alice->generate_fallback_key(); EXPECT_EQ(alice->fallback_key().size(), 1); alice->mark_keys_as_published(); EXPECT_EQ(alice->one_time_keys().size(), 0); } TEST(AccountTest, MaxKeysTest) { auto alice = olm::new_account(); auto max_keys = alice->max_number_of_one_time_keys(); EXPECT_GT(max_keys, 0); EXPECT_LT(max_keys, 1000); } TEST(AccountTest, PickleTest) { auto alice = olm::new_account(); auto pickle = alice->pickle(PICKLE_KEY); auto unpickled = olm::account_from_pickle(pickle, PICKLE_KEY); EXPECT_EQ(alice->curve25519_key()->to_base64(), unpickled->curve25519_key()->to_base64()); } TEST(AccountTest, PickleFromLibolmTest) { auto [data, alice] = new_olm_account(); auto random = gen_random(olm_account_generate_one_time_keys_random_length(alice, 10)); check_olm_error(olm_account_generate_one_time_keys(alice, 10, random.data(), random.size())); auto pickle = std::string(olm_pickle_account_length(alice), '\0'); check_olm_error(olm_pickle_account(alice, OLM_PICKLE_KEY.data(), OLM_PICKLE_KEY.size(), pickle.data(), pickle.size())); auto unpickled = olm::account_from_libolm_pickle(pickle, Slice<const unsigned char>(OLM_PICKLE_KEY.data(), OLM_PICKLE_KEY.size())); { auto identity_keys = std::string(olm_account_identity_keys_length(alice), '\0'); check_olm_error(olm_account_identity_keys(alice, identity_keys.data(), identity_keys.size())); auto parsed_keys = boost::json::parse(identity_keys).as_object(); auto curve25519_key = String(as_std_string(parsed_keys.at("curve25519").as_string())); auto ed25519_key = String(as_std_string(parsed_keys.at("ed25519").as_string())); EXPECT_EQ(curve25519_key, unpickled->curve25519_key()->to_base64()); EXPECT_EQ(ed25519_key, unpickled->ed25519_key()->to_base64()); } { auto one_time_keys = std::string(olm_account_one_time_keys_length(alice), '\0'); check_olm_error(olm_account_one_time_keys(alice, one_time_keys.data(), one_time_keys.size())); auto parsed_keys = boost::json::parse(one_time_keys).as_object().at("curve25519").as_object(); // The key id will change from libolm to vodozemac, but the key themselves won't. auto one_time_keys_set = std::unordered_set<std::string>(); for (const auto &pair : parsed_keys) { one_time_keys_set.insert(as_std_string(pair.value().as_string())); } auto unpickled_one_time_keys = unpickled->one_time_keys(); auto unpickled_one_time_keys_set = std::unordered_set<std::string>(); for (const auto &k : unpickled_one_time_keys) { unpickled_one_time_keys_set.insert(static_cast<std::string>(k.key->to_base64())); } EXPECT_EQ(one_time_keys_set, unpickled_one_time_keys_set); } } TEST(AccountTest, SignAndVerify) { auto alice = olm::new_account(); auto key = alice->ed25519_key(); std::string str = "{}"; auto signature = alice->sign(Str(str)); EXPECT_NO_THROW(key->verify(Str(str), *signature)); { auto cloned_signature = types::ed25519_signature_from_base64(signature->to_base64()); EXPECT_NO_THROW(key->verify(Str(str), *cloned_signature)); } { auto cloned_key = types::ed25519_key_from_base64(key->to_base64()); EXPECT_NO_THROW(cloned_key->verify(Str(str), *signature)); } std::string str2 = "{\"foo\": \"bar\"}"; auto signature2 = alice->sign(Str(str2)); EXPECT_ANY_THROW(key->verify(Str(str2), *signature)); EXPECT_ANY_THROW(key->verify(Str(str), *signature2)); } diff --git a/cpp/tests/group_session.cpp b/cpp/tests/group_session.cpp index f06c198..8acb49d 100644 --- a/cpp/tests/group_session.cpp +++ b/cpp/tests/group_session.cpp @@ -1,184 +1,185 @@ #include "../../target/cxxbridge/vodozemac/src/lib.rs.h" #include <olm/outbound_group_session.h> #include <olm/inbound_group_session.h> #include "gtest/gtest.h" #include "util.hpp" using namespace rust; +using namespace vodozemac; struct SessionCreationResult { Box<megolm::GroupSession> outbound; Box<megolm::InboundGroupSession> inbound; }; SessionCreationResult create_session() { auto outbound = megolm::new_group_session(); auto session_key = outbound->session_key(); auto inbound = megolm::new_inbound_group_session(*session_key); auto ret = SessionCreationResult{ std::move(outbound), std::move(inbound), }; return ret; } struct LibolmSessionCreationResult { std::vector<uint8_t> outbound_data; OlmOutboundGroupSession *outbound; std::vector<uint8_t> inbound_data; OlmInboundGroupSession *inbound; }; LibolmSessionCreationResult create_libolm_session() { LibolmSessionCreationResult res{ std::vector<uint8_t>(olm_outbound_group_session_size()), 0, std::vector<uint8_t>(olm_inbound_group_session_size()), 0, }; res.outbound = olm_outbound_group_session(res.outbound_data.data()); auto random = gen_random(olm_init_outbound_group_session_random_length(res.outbound)); check_olm_error(olm_init_outbound_group_session(res.outbound, random.data(), random.size())); auto session_key = std::vector<uint8_t>(olm_outbound_group_session_key_length(res.outbound)); check_olm_error(olm_outbound_group_session_key(res.outbound, session_key.data(), session_key.size())); res.inbound = olm_inbound_group_session(res.inbound_data.data()); olm_init_inbound_group_session(res.inbound, session_key.data(), session_key.size()); return res; } TEST(GroupSessionTest, Creation) { auto [outbound, inbound] = create_session(); auto outbound_id = outbound->session_id(); auto inbound_id = inbound->session_id(); EXPECT_NE(outbound_id.length(), 0); EXPECT_STREQ(outbound_id.c_str(), inbound_id.c_str()); } TEST(GroupSessionTest, MessageIndex) { auto [outbound, inbound] = create_session(); EXPECT_EQ(outbound->message_index(), 0); EXPECT_EQ(inbound->first_known_index(), 0); outbound->encrypt("Hello"); auto inbound2 = megolm::new_inbound_group_session(*outbound->session_key()); EXPECT_EQ(outbound->message_index(), 1); EXPECT_EQ(inbound2->first_known_index(), 1); } TEST(GroupSessionTest, Pickle) { auto session = megolm::new_group_session(); auto pickle = session->pickle(PICKLE_KEY); auto unpickled = megolm::group_session_from_pickle(pickle, PICKLE_KEY); ASSERT_STREQ(session->session_id().c_str(), unpickled->session_id().c_str()); EXPECT_EQ(session->message_index(), unpickled->message_index()); } TEST(GroupSessionTest, PickleLibolm) { auto [_1, outbound, _2, inbound] = create_libolm_session(); auto pickle = std::string(olm_pickle_outbound_group_session_length(outbound), '\0'); check_olm_error(olm_pickle_outbound_group_session(outbound, OLM_PICKLE_KEY.data(), OLM_PICKLE_KEY.size(), pickle.data(), pickle.size())); auto unpickled = megolm::group_session_from_libolm_pickle(String(pickle), Slice<const unsigned char>(OLM_PICKLE_KEY.data(), OLM_PICKLE_KEY.size())); auto session_id = std::vector<uint8_t>(olm_outbound_group_session_id_length(outbound)); check_olm_error(olm_outbound_group_session_id(outbound, session_id.data(), session_id.size())); ASSERT_EQ(as_std_string(session_id), static_cast<std::string>(unpickled->session_id())); auto message_index = olm_outbound_group_session_message_index(outbound); EXPECT_EQ(message_index, unpickled->message_index()); } TEST(GroupSessionTest, PickleInbound) { auto [outbound, inbound] = create_session(); auto pickle = inbound->pickle(PICKLE_KEY); auto unpickled = megolm::inbound_group_session_from_pickle(pickle, PICKLE_KEY); ASSERT_STREQ(inbound->session_id().c_str(), unpickled->session_id().c_str()); EXPECT_EQ(inbound->first_known_index(), unpickled->first_known_index()); } TEST(GroupSessionTest, PickleInboundLibolm) { auto [_1, outbound, _2, inbound] = create_libolm_session(); auto pickle = std::string(olm_pickle_inbound_group_session_length(inbound), '\0'); check_olm_error(olm_pickle_inbound_group_session(inbound, OLM_PICKLE_KEY.data(), OLM_PICKLE_KEY.size(), pickle.data(), pickle.size())); auto unpickled = megolm::inbound_group_session_from_libolm_pickle(pickle, Slice<const unsigned char>(OLM_PICKLE_KEY.data(), OLM_PICKLE_KEY.size())); auto session_id = std::vector<uint8_t>(olm_inbound_group_session_id_length(inbound)); check_olm_error(olm_inbound_group_session_id(inbound, session_id.data(), session_id.size())); ASSERT_EQ(as_std_string(session_id), static_cast<std::string>(unpickled->session_id())); auto first_known_index = olm_inbound_group_session_first_known_index(inbound); EXPECT_EQ(first_known_index, unpickled->first_known_index()); } TEST(GroupSessionTest, UnpicklingFail) { EXPECT_ANY_THROW(megolm::group_session_from_pickle("", PICKLE_KEY)); EXPECT_ANY_THROW(megolm::inbound_group_session_from_pickle("", PICKLE_KEY)); } TEST(GroupSessionTest, DecryptionFail) { auto [outbound, inbound] = create_session(); auto outbound2 = megolm::new_group_session(); auto message = outbound2->encrypt("Hello"); EXPECT_ANY_THROW(inbound->decrypt(*message)); } TEST(GroupSessionTest, Encryption) { auto [outbound, inbound] = create_session(); auto plaintext = "It's a secret to everybody"; auto message = outbound->encrypt(plaintext); auto decrypted = inbound->decrypt(*message); EXPECT_EQ(as_std_string(decrypted.plaintext), std::string(plaintext)); EXPECT_EQ(decrypted.message_index, 0); plaintext = "Another secret"; message = outbound->encrypt(plaintext); decrypted = inbound->decrypt(*message); EXPECT_EQ(as_std_string(decrypted.plaintext), std::string(plaintext)); EXPECT_EQ(decrypted.message_index, 1); } TEST(GroupSessionTest, SessionExport) { auto [outbound, inbound] = create_session(); auto imported = megolm::import_inbound_group_session(*inbound->export_at(0)); EXPECT_STREQ(outbound->session_id().c_str(), imported->session_id().c_str()); auto plaintext = "It's a secret to everybody"; auto message = outbound->encrypt(plaintext); auto decrypted = imported->decrypt(*message); EXPECT_EQ(as_std_string(decrypted.plaintext), std::string(plaintext)); EXPECT_EQ(decrypted.message_index, 0); plaintext = "Another secret"; message = outbound->encrypt(plaintext); decrypted = imported->decrypt(*message); EXPECT_EQ(as_std_string(decrypted.plaintext), std::string(plaintext)); EXPECT_EQ(decrypted.message_index, 1); } diff --git a/cpp/tests/sas.cpp b/cpp/tests/sas.cpp index 7e1de46..f5364ec 100644 --- a/cpp/tests/sas.cpp +++ b/cpp/tests/sas.cpp @@ -1,62 +1,63 @@ #include "../../target/cxxbridge/vodozemac/src/lib.rs.h" #include "gtest/gtest.h" using namespace rust; +using namespace vodozemac; TEST(SasTest, Creation) { auto alice = sas::new_sas(); auto bob = sas::new_sas(); auto alice_key = alice->public_key()->to_base64(); auto bob_key = bob->public_key()->to_base64(); EXPECT_STRNE(alice_key.c_str(), bob_key.c_str()); } TEST(SasTest, SharedSecret) { auto alice = sas::new_sas(); auto bob = sas::new_sas(); auto alice_key = alice->public_key(); auto bob_key = bob->public_key(); auto alice_established = alice->diffie_hellman(*bob_key); auto bob_established = bob->diffie_hellman(*alice_key); } TEST(SasTest, ShortAuthString) { auto alice = sas::new_sas(); auto bob = sas::new_sas(); auto alice_key = alice->public_key(); auto bob_key = bob->public_key(); auto alice_established = alice->diffie_hellman(*bob_key); auto bob_established = bob->diffie_hellman(*alice_key); auto alice_bytes = alice_established->bytes(""); auto bob_bytes = bob_established->bytes(""); ASSERT_EQ(alice_bytes->emoji_indices(), bob_bytes->emoji_indices()); ASSERT_EQ(alice_bytes->decimals(), bob_bytes->decimals()); } TEST(SasTest, CalculateMac) { auto alice = sas::new_sas(); auto bob = sas::new_sas(); auto alice_key = alice->public_key(); auto bob_key = bob->public_key(); auto alice_established = alice->diffie_hellman(*bob_key); auto bob_established = bob->diffie_hellman(*alice_key); auto alice_mac = alice_established->calculate_mac("Hello", "world"); auto bob_mac = bob_established->calculate_mac("Hello", "world"); ASSERT_STREQ(alice_mac->to_base64().c_str(), bob_mac->to_base64().c_str()); EXPECT_NO_THROW(alice_established->verify_mac("Hello", "world", *bob_mac)); EXPECT_NO_THROW(bob_established->verify_mac("Hello", "world", *alice_mac)); EXPECT_ANY_THROW(bob_established->verify_mac("", "", *alice_mac)); } diff --git a/cpp/tests/session.cpp b/cpp/tests/session.cpp index b991e39..04745ba 100644 --- a/cpp/tests/session.cpp +++ b/cpp/tests/session.cpp @@ -1,192 +1,193 @@ #include "../../target/cxxbridge/vodozemac/src/lib.rs.h" #include "gtest/gtest.h" #include <boost/json.hpp> #include "util.hpp" using namespace rust; +using namespace vodozemac; struct SessionCreationResult { Box<olm::Account> alice; Box<olm::Account> bob; Box<olm::Session> session; }; SessionCreationResult create_session() { Box<olm::Account> alice = olm::new_account(); auto bob = olm::new_account(); bob->generate_one_time_keys(1); auto one_time_keys = bob->one_time_keys(); auto [key_id, one_time_key] = std::move(one_time_keys.front()); auto identity_key = bob->curve25519_key(); auto session = alice->create_outbound_session(*identity_key, *one_time_key); auto ret = SessionCreationResult{ std::move(alice), std::move(bob), std::move(session), }; return ret; } TEST(SessionTest, Creation) { auto [alice, bob, session] = create_session(); auto session_id = session->session_id(); EXPECT_NE(session_id.length(), 0); } TEST(SessionTest, IdUniqueness) { auto [alice1, bob1, session] = create_session(); auto [alice2, bob2, session2] = create_session(); auto session_id = session->session_id(); auto session2_id = session2->session_id(); EXPECT_STRNE(session_id.c_str(), session2_id.c_str()); } TEST(SessionTest, Pickle) { auto [alice, bob, session] = create_session(); auto pickle = session->pickle(PICKLE_KEY); auto unpickled = olm::session_from_pickle(pickle, PICKLE_KEY); auto session_id = session->session_id(); auto session2_id = unpickled->session_id(); EXPECT_STREQ(session_id.c_str(), session2_id.c_str()); } TEST(SessionTest, PickleFromLibolm) { auto [_1, alice] = new_olm_account(); auto [_2, bob] = new_olm_account(); auto random = gen_random(olm_account_generate_one_time_keys_random_length(bob, 1)); check_olm_error(olm_account_generate_one_time_keys(bob, 1, random.data(), random.size())); auto one_time_keys = std::string(olm_account_one_time_keys_length(bob), '\0'); check_olm_error(olm_account_one_time_keys(bob, one_time_keys.data(), one_time_keys.size())); auto parsed_keys = boost::json::parse(one_time_keys).as_object().at("curve25519").as_object(); auto key_id = as_std_string(parsed_keys.begin()->key()); auto one_time_key = as_std_string(parsed_keys.begin()->value().as_string()); auto identity_keys = std::string(olm_account_identity_keys_length(alice), '\0'); check_olm_error(olm_account_identity_keys(alice, identity_keys.data(), identity_keys.size())); parsed_keys = boost::json::parse(identity_keys).as_object(); auto identity_key = String(as_std_string(parsed_keys.at("curve25519").as_string())); auto data = std::vector<uint8_t>(olm_session_size()); auto session = olm_session(data.data()); random = gen_random(olm_create_outbound_session_random_length(session)); check_olm_error(olm_create_outbound_session(session, alice, identity_key.data(), identity_key.size(), one_time_key.data(), one_time_key.size(), random.data(), random.size())); auto pickle = std::string(olm_pickle_session_length(session), '\0'); check_olm_error(olm_pickle_session(session, OLM_PICKLE_KEY.data(), OLM_PICKLE_KEY.size(), pickle.data(), pickle.size())); auto unpickled = olm::session_from_libolm_pickle(pickle, Slice<const unsigned char>(OLM_PICKLE_KEY.data(), OLM_PICKLE_KEY.size())); auto session_id = std::string(olm_session_id_length(session), '\0'); check_olm_error(olm_session_id(session, session_id.data(), session_id.size())); auto session2_id = unpickled->session_id(); EXPECT_STREQ(session_id.c_str(), session2_id.c_str()); } TEST(SessionTest, InvalidPickle) { EXPECT_ANY_THROW(olm::session_from_pickle("", PICKLE_KEY)); } TEST(SessionTest, Encryption) { auto [alice, bob, session] = create_session(); auto alice_key = alice->curve25519_key(); auto plaintext = "It's a secret to everybody"; auto message = session->encrypt(plaintext); auto [bob_session, decrypted] = bob->create_inbound_session(*alice_key, *message); EXPECT_STREQ(session->session_id().c_str(), bob_session->session_id().c_str()); EXPECT_EQ(std::string(plaintext), as_std_string(decrypted)); } TEST(SessionTest, InvalidDecryption) { auto parts = olm::OlmMessageParts{ 0, "", }; EXPECT_ANY_THROW(olm::olm_message_from_parts(parts)); } TEST(SessionTest, MultipleMessageDecryption) { auto [alice, bob, session] = create_session(); auto alice_key = alice->curve25519_key(); auto plaintext = "It's a secret to everybody"; auto message = session->encrypt(plaintext); auto [bob_session, decrypted] = bob->create_inbound_session(*alice_key, *message); EXPECT_STREQ(session->session_id().c_str(), bob_session->session_id().c_str()); EXPECT_EQ(std::string(plaintext), as_std_string(decrypted)); plaintext = "Grumble grumble"; message = bob_session->encrypt(plaintext); decrypted = session->decrypt(*message); EXPECT_EQ(std::string(plaintext), as_std_string(decrypted)); } TEST(SessionTest, PreKeyMatches) { auto [alice, bob, session] = create_session(); auto alice_key = alice->curve25519_key(); auto plaintext = "It's a secret to everybody"; auto message = session->encrypt(plaintext); auto [bob_session, decrypted] = bob->create_inbound_session(*alice_key, *message); plaintext = "Grumble grumble"; message = session->encrypt(plaintext); EXPECT_TRUE(bob_session->session_matches(*message)); } TEST(SessionTest, PreKeyDoesNotMatch) { auto [alice, bob, session] = create_session(); auto [alice2, bob2, session2] = create_session(); auto alice_key = alice->curve25519_key(); auto plaintext = "It's a secret to everybody"; auto message = session->encrypt(plaintext); auto [bob_session, decrypted] = bob->create_inbound_session(*alice_key, *message); plaintext = "Grumble grumble"; message = session2->encrypt(plaintext); EXPECT_FALSE(bob_session->session_matches(*message)); } TEST(SessionTest, InvalidOneTimeKey) { EXPECT_ANY_THROW(types::curve_key_from_base64("")); }