Please do not hesitate to update, polish, enrich or correct this document if you see fit.
This document aims to provide you an overview of how to port your Matrix application from libolm to vodozemac.
= Definitions =
We will be using the following definitions in the document:
- The **E2EE library** means either libolm or vodozemac.
- The **application** means any code that uses the E2EE library, which may be another library.
- The **user** means anyone who interacts with the application in any way other than writing code.
= Differences =
[[ https://lily-is.land/kazv/vodozemac-bindings | vodozemac-bindings ]] has a C++ public API, as opposed to the C API of libolm. There are a couple of differences in the API:
- Memory allocations are managed by vodozemac, not by the application.
- You can no longer pass random data into the E2EE library, as vodozemac embeds the rust's random generator in it. This renders the [[https://spec.matrix.org/v1.11/appendices/#cryptographic-test-vectors | "test vectors"]] in the spec effectively useless.
- ~~You have to have exceptions support in your compiler. This is a limitation of cxx.rs , which is used to generate the bindings. See https://github.com/dtolnay/cxx/issues/1052 for more information. This might change later.~~ You no longer need to have exceptions support to use this binding. See below for more details.
- Pickle keys are now 32 bytes, instead of any string.
= Overview of vodozemac-bindings =
Everything is in the `vodozemac` namespace. A function may throw an exception if it returns anything other than `Result<T>` in rust code. In the generated C++ header, it corresponds to the `noexcept` specifier. If a function is declared `noexcept`, it does not throw an exception; otherwise, it may.
You can use the following code to check for exceptions:
```
template<class Func>
auto checkVodozemacError(Func &&func) -> std::optional<std::invoke_result_t<Func>>
{
try {
return std::forward<Func>(func)();
} catch (const std::exception &e) {
return std::nullopt;
}
}
```
With https://lily-is.land/kazv/vodozemac-bindings/-/merge_requests/6 , every vodozemac function that may throw has a `noexcept` counterpart, with the name `FUNC_NAME_noexcept` where `FUNC_NAME` is the original function name. If you wish to use no exceptions in your code, you can use the `noexcept` functions instead. These functions will return a `Box<Maybe<ValueType>>` where `ValueType` is the original return type. The `Maybe` types have the following signature (due to [[ https://cxx.rs | cxx ]] restrictions, they are actually not template types in C++):
```
template<class T>
struct Maybe<T> {
/// return true iff the Maybe is valid
bool is_valid() const noexcept;
// **the following functions can only be used if the Maybe is valid.**
/// return true iff the Maybe contains a value.
bool has_value() const noexcept;
/// return the contained value. can only be used if the Maybe is valid and contains a value.
/// **the Maybe will become invalid after calling.**
::rust::Box<T> take_value() noexcept;
/// return the contained error as a rust String. can only be used if the Maybe is valid and does not contain a value.
/// **the Maybe will become invalid after calling.**
::rust::String take_error() noexcept;
~Maybe() = delete;
};
```
You can use it like this:
```
// result is a Box<Maybe<T>>
auto result = FUNC_NAME_noexcept();
if (result->has_value()) {
auto value = result->take_value();
// from now on, result cannot be used again (except the is_valid() method).
// deal with value
} else {
auto error = result->take_error();
// from now on, result cannot be used again (except the is_valid() method).
// deal with error
}
```
= Converting from libolm =
vodozemac-bindings provides function to convert data from libolm pickle format. They are called `*_from_libolm_pickle` and takes two arguments: the first one is the pickle data, as `rust::Str`, the other being the pickle key, as `rust::Slice<const unsigned char>`. `rust::Str` can be directly converted from a `std::string`, while `rust::Slice` needs some work, for example:
```
auto session = checkVodozemacError([&]() {
return vodozemac::megolm::inbound_group_session_from_libolm_pickle(pickleData, rust::Slice<const unsigned char>(pickleKey.data(), pickleKey.size()));
});
```
= Changes to first inbound olm message =
One important change in vodozemac is that when you create an inbound olm session from an encrypted message, you instantly get the decrypted content as return, and decrypt() will NOT work for the first message.
```
auto res = checkVodozemacError([&]() {
auto identityKey = vodozemac::types::curve_key_from_base64(rust::Str(theirIdentityKey));
auto msg = vodozemac::olm::olm_message_from_parts(vodozemac::olm::OlmMessageParts{
0, // pre-key message
message,
});
return account->create_inbound_session(
*identityKey,
*msg
);
});
if (res.has_value()) {
session = std::move(res->session);
firstDecrypted = std::string(res->plaintext.begin(), res->plaintext.end());
}
```
= Sample code =
The commits referenced in https://iron.lily-is.land/T125 provides a sample code to migrate from libolm to vodozemac.