Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85805489
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
22 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Oct 11, 1:09 PM (1 d, 17 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1785794
Default Alt Text
(22 KB)
Attached To
Mode
rVB vodozemac-bindings
Attached
Detach File
Event Timeline
Log In to Comment