Page MenuHomePhorge

No OneTemporary

Size
22 KB
Referenced Files
None
Subscribers
None
diff --git a/cpp/Makefile b/cpp/Makefile
index 4c6ec30..827caad 100644
--- a/cpp/Makefile
+++ b/cpp/Makefile
@@ -1,62 +1,63 @@
rust-lib:
cargo build
account-test: rust-lib tests/account.cpp
g++ \
-Wall -g \
-pthread -std=c++17 \
tests/account.cpp \
-lgtest_main -lgtest \
-lolm -lboost_json \
-lpthread \
-I ../target/cxxbridge \
-L ../target/debug/ \
-l vodozemac \
-ldl \
-o account-test
session-test: rust-lib tests/session.cpp
g++ \
-Wall -g \
-pthread -std=c++17 \
tests/session.cpp \
-lgtest_main -lgtest \
+ -lolm -lboost_json \
-lpthread \
-I ../target/cxxbridge \
-L ../target/debug/ \
-l vodozemac \
-ldl \
-o session-test
sas-test: rust-lib tests/sas.cpp
g++ \
-Wall -g \
-pthread -std=c++17 \
tests/sas.cpp \
-lgtest_main -lgtest \
-lpthread \
-I ../target/cxxbridge \
-L ../target/debug/ \
-l vodozemac \
-ldl \
-o sas-test
group-session-test: rust-lib tests/group_session.cpp
g++ \
-Wall -g \
-pthread -std=c++17 \
tests/group_session.cpp \
-lgtest_main -lgtest \
-lpthread \
-I ../target/cxxbridge \
-L ../target/debug/ \
-l vodozemac \
-ldl \
-o group-session-test
test: account-test session-test sas-test group-session-test
./account-test --gtest_color=yes
./session-test --gtest_color=yes
./sas-test --gtest_color=yes
./group-session-test --gtest_color=yes
diff --git a/cpp/src/lib.rs b/cpp/src/lib.rs
index d292d5f..03f2d12 100644
--- a/cpp/src/lib.rs
+++ b/cpp/src/lib.rs
@@ -1,177 +1,178 @@
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, import_inbound_group_session,
inbound_group_session_from_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};
+use session::{session_from_pickle, session_from_libolm_pickle, Session};
use types::{
curve_key_from_base64, ed25519_key_from_base64, Curve25519PublicKey, Ed25519PublicKey,
Ed25519Signature,
};
#[cxx::bridge]
mod ffi {
#[namespace = "olm"]
struct OlmMessageParts {
message_type: usize,
ciphertext: String,
}
#[namespace = "olm"]
pub struct InboundCreationResult {
pub session: Box<Session>,
pub plaintext: Vec<u8>,
}
#[namespace = "olm"]
struct OneTimeKey {
key_id: String,
key: Box<Curve25519PublicKey>,
}
#[namespace = "olm"]
#[derive(PartialEq, Eq)]
struct SessionKeys {
identity_key: Box<Curve25519PublicKey>,
base_key: Box<Curve25519PublicKey>,
one_time_key: Box<Curve25519PublicKey>,
}
#[namespace = "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;
type Ed25519Signature;
}
#[namespace = "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"]
struct DecryptedMessage {
plaintext: Vec<u8>,
message_index: u32,
}
#[namespace = "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>>;
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>>;
}
#[namespace = "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/src/session.rs b/cpp/src/session.rs
index b639ba2..60bf8aa 100644
--- a/cpp/src/session.rs
+++ b/cpp/src/session.rs
@@ -1,47 +1,55 @@
use super::{ffi::SessionKeys, Curve25519PublicKey, OlmMessage};
pub struct Session(pub(crate) vodozemac::olm::Session);
impl Session {
pub fn session_id(&self) -> String {
self.0.session_id()
}
pub fn pickle(&self, pickle_key: &[u8; 32]) -> String {
self.0.pickle().encrypt(pickle_key)
}
pub fn encrypt(&mut self, plaintext: &str) -> Box<OlmMessage> {
OlmMessage(self.0.encrypt(plaintext)).into()
}
pub fn decrypt(&mut self, message: &OlmMessage) -> Result<Vec<u8>, anyhow::Error> {
Ok(self.0.decrypt(&message.0)?)
}
pub fn session_keys(&self) -> SessionKeys {
let session_keys = self.0.session_keys();
SessionKeys {
identity_key: Curve25519PublicKey(session_keys.identity_key).into(),
base_key: Curve25519PublicKey(session_keys.base_key).into(),
one_time_key: Curve25519PublicKey(session_keys.one_time_key).into(),
}
}
pub fn session_matches(&self, message: &OlmMessage) -> bool {
if let vodozemac::olm::OlmMessage::PreKey(m) = &message.0 {
self.0.session_keys() == m.session_keys()
} else {
false
}
}
}
pub fn session_from_pickle(
pickle: &str,
pickle_key: &[u8; 32],
) -> Result<Box<Session>, anyhow::Error> {
let pickle = vodozemac::olm::SessionPickle::from_encrypted(pickle, pickle_key)?;
Ok(Session(vodozemac::olm::Session::from_pickle(pickle)).into())
}
+
+pub fn session_from_libolm_pickle(
+ pickle: &str,
+ pickle_key: &[u8],
+) -> Result<Box<Session>, anyhow::Error> {
+ let res = vodozemac::olm::Session::from_libolm_pickle(pickle, pickle_key)?;
+ Ok(Session(res).into())
+}
diff --git a/cpp/tests/account.cpp b/cpp/tests/account.cpp
index 04c7ffa..e1788df 100644
--- a/cpp/tests/account.cpp
+++ b/cpp/tests/account.cpp
@@ -1,105 +1,102 @@
#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;
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 = std::vector<uint8_t>(olm_account_size());
- auto alice = olm_account(data.data());
- auto random = gen_random(olm_create_account_random_length(alice));
- check_olm_error(olm_create_account(alice, random.data(), random.size()));
- random = gen_random(olm_account_generate_one_time_keys_random_length(alice, 10));
+ 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);
}
}
diff --git a/cpp/tests/session.cpp b/cpp/tests/session.cpp
index 1e9899b..b991e39 100644
--- a/cpp/tests/session.cpp
+++ b/cpp/tests/session.cpp
@@ -1,156 +1,192 @@
#include "../../target/cxxbridge/vodozemac/src/lib.rs.h"
#include "gtest/gtest.h"
+#include <boost/json.hpp>
#include "util.hpp"
using namespace rust;
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(""));
}
diff --git a/cpp/tests/util.hpp b/cpp/tests/util.hpp
index eda31fd..407434c 100644
--- a/cpp/tests/util.hpp
+++ b/cpp/tests/util.hpp
@@ -1,30 +1,40 @@
#pragma once
#include <random>
#include <algorithm>
#include <array>
#include <vector>
#include <gtest/gtest.h>
#include <olm/olm.h>
inline std::array<uint8_t, 32> PICKLE_KEY = {};
// olm pickles are of variable length and are then hashed to obtain the actual key
inline std::vector<uint8_t> OLM_PICKLE_KEY = {0, 7, 2, 1};
[[nodiscard]] inline std::vector<uint8_t> gen_random(int len)
{
auto rd = std::random_device{};
auto ret = std::vector<uint8_t>(len, '\0');
std::generate(ret.begin(), ret.end(), [&] { return rd(); });
return ret;
}
inline void check_olm_error(std::size_t code)
{
EXPECT_NE(code, olm_error());
}
template<class T>
[[nodiscard]] inline std::string as_std_string(const T &range)
{
return std::string(range.begin(), range.end());
}
+
+std::pair<std::vector<uint8_t>, OlmAccount *> new_olm_account()
+{
+ auto data = std::vector<uint8_t>(olm_account_size());
+ auto account = olm_account(data.data());
+ auto random = gen_random(olm_create_account_random_length(account));
+ check_olm_error(olm_create_account(account, random.data(), random.size()));
+
+ return {std::move(data), account};
+}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Oct 11, 1:09 PM (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1785794
Default Alt Text
(22 KB)

Event Timeline