diff --git a/src/crypto.cpp b/src/crypto.cpp
index 1aa77ad13c2dbd6ef54bcf2bc00420e02551697b..61947de854d5c39d8498460d92c97315572ed11b 100644
--- a/src/crypto.cpp
+++ b/src/crypto.cpp
@@ -6,163 +6,205 @@
 using json = nlohmann::json;
 using namespace mtx::client::crypto;
 
-constexpr std::size_t SIGNATURE_SIZE = 64;
-
-std::shared_ptr<olm::Account>
-mtx::client::crypto::olm_new_account()
+void
+OlmClient::create_new_account()
 {
-        auto olm_account = std::make_shared<olm::Account>();
+        // The method has no effect if the account is already initialized.
+        if (account_)
+                return;
 
-        const auto nbytes = olm_account->new_account_random_length();
-        auto buf          = create_buffer(nbytes);
+        account_ =
+          std::unique_ptr<OlmAccount, OlmDeleter>(olm_account(new uint8_t[olm_account_size()]));
 
-        int result = olm_account->new_account(buf->data(), buf->size());
+        auto tmp_buf  = create_buffer(olm_create_account_random_length(account_.get()));
+        const int ret = olm_create_account(account_.get(), tmp_buf->data(), tmp_buf->size());
 
-        if (result == -1)
-                throw olm_exception("olm_new_account", olm_account->last_error);
+        if (ret == -1) {
+                account_.reset();
+                throw olm_exception("create_new_account", account_.get());
+        }
+}
 
-        return olm_account;
+void
+OlmClient::create_new_utility()
+{
+        // The method has no effect if the account is already initialized.
+        if (utility_)
+                return;
+
+        utility_ =
+          std::unique_ptr<OlmUtility, OlmDeleter>(olm_utility(new uint8_t[olm_utility_size()]));
 }
 
-IdentityKeys
-mtx::client::crypto::identity_keys(std::shared_ptr<olm::Account> account)
+std::unique_ptr<OlmSession, OlmDeleter>
+OlmClient::create_new_session()
 {
-        const auto nbytes = account->get_identity_json_length();
-        auto buf          = create_buffer(nbytes);
+        return std::unique_ptr<OlmSession, OlmDeleter>(
+          olm_session(new uint8_t[olm_session_size()]));
+}
 
-        int result = account->get_identity_json(buf->data(), buf->size());
+IdentityKeys
+OlmClient::identity_keys()
+{
+        auto tmp_buf = create_buffer(olm_account_identity_keys_length(account_.get()));
+        int result =
+          olm_account_identity_keys(account_.get(), (void *)tmp_buf->data(), tmp_buf->size());
 
         if (result == -1)
-                throw olm_exception("identity_keys", account->last_error);
+                throw olm_exception("identity_keys", account_.get());
+
+        return json::parse(std::string(tmp_buf->begin(), tmp_buf->end()));
+}
+
+std::unique_ptr<BinaryBuf>
+OlmClient::sign_message(const std::string &msg)
+{
+        // Message buffer
+        auto buf = str_to_buffer(msg);
 
-        std::string data(buf->begin(), buf->end());
-        IdentityKeys keys = json::parse(data);
+        // Signature buffer
+        auto signature_buf = create_buffer(olm_account_signature_length(account_.get()));
+        olm_account_sign(
+          account_.get(), buf->data(), buf->size(), signature_buf->data(), signature_buf->size());
 
-        return keys;
+        return signature_buf;
 }
 
 std::string
-mtx::client::crypto::sign_identity_keys(std::shared_ptr<olm::Account> account,
-                                        const IdentityKeys &keys,
-                                        const mtx::identifiers::User &user_id,
-                                        const std::string &device_id)
+OlmClient::sign_identity_keys()
 {
+        auto keys = identity_keys();
+
         json body{{"algorithms", {"m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"}},
-                  {"user_id", user_id.to_string()},
-                  {"device_id", device_id},
+                  {"user_id", user_id_},
+                  {"device_id", device_id_},
                   {"keys",
                    {
-                     {"curve25519:" + device_id, keys.curve25519},
-                     {"ed25519:" + device_id, keys.ed25519},
+                     {"curve25519:" + device_id_, keys.curve25519},
+                     {"ed25519:" + device_id_, keys.ed25519},
                    }}};
 
-        return encode_base64(sign_message(account, body.dump())->data(), SIGNATURE_SIZE);
+        return encode_base64(sign_message(body.dump())->data(),
+                             olm_account_signature_length(account_.get()));
 }
 
 std::size_t
-mtx::client::crypto::generate_one_time_keys(std::shared_ptr<olm::Account> account,
-                                            std::size_t number_of_keys)
+OlmClient::generate_one_time_keys(std::size_t number_of_keys)
 {
-        const auto nbytes = account->generate_one_time_keys_random_length(number_of_keys);
+        const std::size_t nbytes =
+          olm_account_generate_one_time_keys_random_length(account_.get(), number_of_keys);
 
         auto buf = create_buffer(nbytes);
-        return account->generate_one_time_keys(number_of_keys, buf->data(), buf->size());
+
+        const int ret = olm_account_generate_one_time_keys(
+          account_.get(), number_of_keys, buf->data(), buf->size());
+
+        if (ret == -1)
+                throw olm_exception("generate_one_time_keys", account_.get());
+
+        return ret;
 }
 
-json
-mtx::client::crypto::one_time_keys(std::shared_ptr<olm::Account> account)
+OneTimeKeys
+OlmClient::one_time_keys()
 {
-        const auto nbytes = account->get_one_time_keys_json_length();
-        auto buf          = create_buffer(nbytes);
+        auto buf = create_buffer(olm_account_one_time_keys_length(account_.get()));
 
-        int result = account->get_one_time_keys_json(buf->data(), buf->size());
+        const int ret = olm_account_one_time_keys(account_.get(), buf->data(), buf->size());
 
-        if (result == -1)
-                throw olm_exception("one_time_keys", account->last_error);
+        if (ret == -1)
+                throw olm_exception("one_time_keys", account_.get());
 
         return json::parse(std::string(buf->begin(), buf->end()));
 }
 
 std::string
-mtx::client::crypto::sign_one_time_key(std::shared_ptr<olm::Account> account,
-                                       const std::string &key)
+OlmClient::sign_one_time_key(const std::string &key)
 {
         json j{{"key", key}};
-        auto str_json = j.dump();
+        auto str_json  = j.dump();
+        auto signature = sign_message(j.dump());
 
-        auto signature_buf = sign_message(account, j.dump());
-
-        return encode_base64(signature_buf->data(), signature_buf->size());
+        return encode_base64(signature->data(), signature->size());
 }
 
 std::map<std::string, json>
-mtx::client::crypto::sign_one_time_keys(std::shared_ptr<olm::Account> account,
-                                        const mtx::client::crypto::OneTimeKeys &keys,
-                                        const mtx::identifiers::User &user_id,
-                                        const std::string &device_id)
+OlmClient::sign_one_time_keys(const OneTimeKeys &keys)
 {
         // Sign & append the one time keys.
         std::map<std::string, json> signed_one_time_keys;
         for (const auto &elem : keys.curve25519) {
-                auto sig = sign_one_time_key(account, elem.second);
+                auto sig = sign_one_time_key(elem.second);
 
                 signed_one_time_keys["signed_curve25519:" + elem.first] =
-                  signed_one_time_key_json(user_id, device_id, elem.second, sig);
+                  signed_one_time_key_json(elem.second, sig);
         }
 
         return signed_one_time_keys;
 }
 
+json
+OlmClient::signed_one_time_key_json(const std::string &key, const std::string &signature)
+{
+        return json{{"key", key},
+                    {"signatures", {{user_id_, {{"ed25519:" + device_id_, signature}}}}}};
+}
+
+mtx::requests::UploadKeys
+OlmClient::create_upload_keys_request()
+{
+        return create_upload_keys_request(one_time_keys());
+}
+
 mtx::requests::UploadKeys
-mtx::client::crypto::create_upload_keys_request(
-  std::shared_ptr<olm::Account> account,
-  const mtx::client::crypto::IdentityKeys &identity_keys,
-  const mtx::client::crypto::OneTimeKeys &one_time_keys,
-  const mtx::identifiers::User &user_id,
-  const std::string &device_id)
+OlmClient::create_upload_keys_request(const mtx::client::crypto::OneTimeKeys &one_time_keys)
 {
         mtx::requests::UploadKeys req;
-        req.device_keys.user_id   = user_id.to_string();
-        req.device_keys.device_id = device_id;
+        req.device_keys.user_id   = user_id_;
+        req.device_keys.device_id = device_id_;
+
+        auto id_keys = identity_keys();
 
-        req.device_keys.keys["curve25519:" + device_id] = identity_keys.curve25519;
-        req.device_keys.keys["ed25519:" + device_id]    = identity_keys.ed25519;
+        req.device_keys.keys["curve25519:" + device_id_] = id_keys.curve25519;
+        req.device_keys.keys["ed25519:" + device_id_]    = id_keys.ed25519;
 
         // Generate and add the signature to the request.
-        auto sig = sign_identity_keys(account, identity_keys, user_id, device_id);
-        req.device_keys.signatures[user_id.to_string()]["ed25519:" + device_id] = sig;
+        auto sig = sign_identity_keys();
+
+        req.device_keys.signatures[user_id_]["ed25519:" + device_id_] = sig;
 
         if (one_time_keys.curve25519.empty())
                 return req;
 
         // Sign & append the one time keys.
-        req.one_time_keys =
-          mtx::client::crypto::sign_one_time_keys(account, one_time_keys, user_id, device_id);
+        req.one_time_keys = sign_one_time_keys(one_time_keys);
 
         return req;
 }
 
-std::unique_ptr<BinaryBuf>
-mtx::client::crypto::sign_message(std::shared_ptr<olm::Account> account, const std::string &msg)
+std::unique_ptr<OlmSession, OlmDeleter>
+OlmClient::create_outbound_group_session(const std::string &peer_identity_key,
+                                         const std::string &peer_one_time_key)
 {
-        // Message buffer
-        auto buf = str_to_buffer(msg);
+        auto session = create_new_session();
+        auto tmp_buf = create_buffer(olm_create_outbound_session_random_length(session.get()));
 
-        // Signature buffer
-        auto signature_buf = create_buffer(SIGNATURE_SIZE);
-        account->sign(buf->data(), buf->size(), signature_buf->data(), signature_buf->size());
+        auto idk_buf = str_to_buffer(peer_identity_key);
+        auto otk_buf = str_to_buffer(peer_one_time_key);
 
-        return signature_buf;
-}
+        const int ret = olm_create_outbound_session(session.get(),
+                                                    account_.get(),
+                                                    idk_buf->data(),
+                                                    idk_buf->size(),
+                                                    otk_buf->data(),
+                                                    otk_buf->size(),
+                                                    tmp_buf->data(),
+                                                    tmp_buf->size());
 
-json
-mtx::client::crypto::signed_one_time_key_json(const mtx::identifiers::User &user_id,
-                                              const std::string &device_id,
-                                              const std::string &key,
-                                              const std::string &signature)
-{
-        return json{{"key", key},
-                    {"signatures", {{user_id.to_string(), {{"ed25519:" + device_id, signature}}}}}};
+        if (ret == -1)
+                throw olm_exception("init_outbound_group_session", session.get());
+
+        return session;
 }
 
 std::unique_ptr<BinaryBuf>
@@ -213,34 +255,3 @@ mtx::client::crypto::json_to_buffer(const nlohmann::json &obj)
 {
         return str_to_buffer(obj.dump());
 }
-
-_olm_curve25519_public_key
-mtx::client::crypto::str_to_curve25519_pk(const std::string &data)
-{
-        auto decoded = decode_base64(data);
-
-        if (decoded->size() != CURVE25519_KEY_LENGTH)
-                throw olm_exception("str_to_curve25519_pk: invalid input size");
-
-        _olm_curve25519_public_key pk;
-        std::copy(decoded->begin(), decoded->end(), pk.public_key);
-
-        return pk;
-}
-
-olm::Session
-mtx::client::crypto::init_outbound_group_session(std::shared_ptr<olm::Account> account,
-                                                 const std::string &peer_identity_key,
-                                                 const std::string &peer_one_time_key)
-{
-        olm::Session session;
-
-        auto buf = create_buffer(session.new_outbound_session_random_length());
-        session.new_outbound_session(*account,
-                                     str_to_curve25519_pk(peer_identity_key),
-                                     str_to_curve25519_pk(peer_one_time_key),
-                                     buf->data(),
-                                     buf->size());
-
-        return session;
-}
diff --git a/src/crypto.hpp b/src/crypto.hpp
index 32ba11ed8180e89e4a24d6e22e9ca656db8a437f..de803e29005136b5b807856277a41861757c584b 100644
--- a/src/crypto.hpp
+++ b/src/crypto.hpp
@@ -2,6 +2,7 @@
 
 #include <exception>
 #include <memory>
+#include <new>
 
 #include <json.hpp>
 #include <sodium.h>
@@ -13,6 +14,8 @@
 #include <olm/error.h>
 #include <olm/session.hh>
 
+#include <olm/olm.h>
+
 namespace mtx {
 namespace client {
 namespace crypto {
@@ -66,41 +69,28 @@ from_json(const nlohmann::json &obj, OneTimeKeys &keys)
 class olm_exception : public std::exception
 {
 public:
-        olm_exception(std::string msg, OlmErrorCode errcode)
-          : errcode_(errcode)
-          , msg_(msg + ": " + std::string(_olm_error_to_string(errcode)))
+        olm_exception(std::string func, OlmSession *s)
+          : msg_(func + ": " + std::string(olm_session_last_error(s)))
+        {}
+
+        olm_exception(std::string func, OlmAccount *acc)
+          : msg_(func + ": " + std::string(olm_account_last_error(acc)))
+        {}
+
+        olm_exception(std::string func, OlmUtility *util)
+          : msg_(func + ": " + std::string(olm_utility_last_error(util)))
         {}
 
         olm_exception(std::string msg)
           : msg_(msg)
         {}
 
-        OlmErrorCode get_errcode() const { return errcode_; }
-        const char *get_error() const { return _olm_error_to_string(errcode_); }
-
         virtual const char *what() const throw() { return msg_.c_str(); }
 
 private:
-        OlmErrorCode errcode_;
         std::string msg_;
 };
 
-//! Create a new olm Account.
-std::shared_ptr<olm::Account>
-olm_new_account();
-
-//! Retrieve the json representation of the identity keys for the given account.
-IdentityKeys
-identity_keys(std::shared_ptr<olm::Account> user);
-
-//! Generate a number of one time keys.
-std::size_t
-generate_one_time_keys(std::shared_ptr<olm::Account> account, std::size_t number_of_keys);
-
-//! Retrieve the json representation of the one time keys for the given account.
-nlohmann::json
-one_time_keys(std::shared_ptr<olm::Account> user);
-
 //! Create a uint8_t buffer which is initialized with random bytes.
 template<class T = BinaryBuf>
 std::unique_ptr<T>
@@ -112,42 +102,72 @@ create_buffer(std::size_t nbytes)
         return buf;
 }
 
-//! Sign the given one time keys and encode it to base64.
-std::string
-sign_one_time_key(std::shared_ptr<olm::Account> account, const std::string &key);
+struct OlmDeleter
+{
+        void operator()(OlmAccount *ptr) { operator delete(ptr, olm_account_size()); }
+        void operator()(OlmSession *ptr) { operator delete(ptr, olm_session_size()); }
+        void operator()(OlmUtility *ptr) { operator delete(ptr, olm_utility_size()); }
+};
 
-//! Sign the identity keys. The result should be used as part of the /keys/upload/ request.
-std::string
-sign_identity_keys(std::shared_ptr<olm::Account> account,
-                   const IdentityKeys &keys,
-                   const mtx::identifiers::User &user_id,
-                   const std::string &device_id);
+class OlmClient : public std::enable_shared_from_this<OlmClient>
+{
+public:
+        OlmClient() = default;
+        OlmClient(std::string user_id, std::string device_id)
+          : user_id_(std::move(user_id))
+          , device_id_(std::move(device_id))
+        {}
 
-//! Sign the given message.
-std::unique_ptr<BinaryBuf>
-sign_message(std::shared_ptr<olm::Account> account, const std::string &msg);
-
-//! Generate the json structure for the signed one time key.
-nlohmann::json
-signed_one_time_key_json(const mtx::identifiers::User &user_id,
-                         const std::string &device_id,
-                         const std::string &key,
-                         const std::string &signature);
-
-//! Sign one_time_keys and generate the appropriate structure for the /keys/upload request.
-std::map<std::string, nlohmann::json>
-sign_one_time_keys(std::shared_ptr<olm::Account> account,
-                   const mtx::client::crypto::OneTimeKeys &keys,
-                   const mtx::identifiers::User &user_id,
-                   const std::string &device_id);
-
-//! Prepare request for the /keys/upload endpoint by signing identity & one time keys.
-mtx::requests::UploadKeys
-create_upload_keys_request(std::shared_ptr<olm::Account> account,
-                           const mtx::client::crypto::IdentityKeys &identity_keys,
-                           const mtx::client::crypto::OneTimeKeys &one_time_keys,
-                           const mtx::identifiers::User &user_id,
-                           const std::string &device_id);
+        using Base64String      = std::string;
+        using SignedOneTimeKeys = std::map<std::string, json>;
+
+        void set_device_id(std::string device_id) { device_id_ = std::move(device_id); }
+        void set_user_id(std::string user_id) { user_id_ = std::move(user_id); }
+
+        //! Sign the given message.
+        std::unique_ptr<BinaryBuf> sign_message(const std::string &msg);
+
+        //! Create a new olm Account. Must be called before any other operation.
+        void create_new_account();
+        void create_new_utility();
+        std::unique_ptr<OlmSession, OlmDeleter> create_new_session();
+
+        //! Retrieve the json representation of the identity keys for the given account.
+        IdentityKeys identity_keys();
+        //! Sign the identity keys.
+        //! The result should be used as part of the /keys/upload/ request.
+        Base64String sign_identity_keys();
+
+        //! Generate a number of one time keys.
+        std::size_t generate_one_time_keys(std::size_t nkeys);
+        //! Retrieve the json representation of the one time keys for the given account.
+        OneTimeKeys one_time_keys();
+        //! Sign the given one time keys and encode it to base64.
+        Base64String sign_one_time_key(const Base64String &encoded_key);
+        //! Sign one_time_keys and generate the appropriate structure for the /keys/upload request.
+        SignedOneTimeKeys sign_one_time_keys(const OneTimeKeys &keys);
+        //! Generate the json structure for the signed one time key.
+        json signed_one_time_key_json(const std::string &key, const std::string &signature);
+
+        //! Prepare request for the /keys/upload endpoint by signing identity & one time keys.
+        mtx::requests::UploadKeys create_upload_keys_request(const OneTimeKeys &keys);
+        mtx::requests::UploadKeys create_upload_keys_request();
+
+        //! Create an outbount megolm session.
+        std::unique_ptr<OlmSession, OlmDeleter> create_outbound_group_session(
+          const std::string &peer_identity_key,
+          const std::string &peer_one_time_key);
+
+        OlmAccount *account() { return account_.get(); }
+        OlmUtility *utility() { return utility_.get(); }
+
+private:
+        std::string user_id_;
+        std::string device_id_;
+
+        std::unique_ptr<OlmAccount, OlmDeleter> account_;
+        std::unique_ptr<OlmUtility, OlmDeleter> utility_;
+};
 
 std::string
 encode_base64(const uint8_t *data, std::size_t len);
@@ -156,23 +176,13 @@ encode_base64(const uint8_t *data, std::size_t len);
 std::unique_ptr<BinaryBuf>
 decode_base64(const std::string &data);
 
-//! Convert the given string to an uint8_t buffer.
-std::unique_ptr<BinaryBuf>
-str_to_buffer(const std::string &data);
-
 //! Convert the given json struct to an uint8_t buffer.
 std::unique_ptr<BinaryBuf>
 json_to_buffer(const nlohmann::json &obj);
 
-//! Convert from base64 encoded public key.
-_olm_curve25519_public_key
-str_to_curve25519_pk(const std::string &data);
-
-//! Create an outbount megolm session.
-olm::Session
-init_outbound_group_session(std::shared_ptr<olm::Account> account,
-                            const std::string &peer_id_key,
-                            const std::string &peer_one_time_key);
+//! Convert the given string to an uint8_t buffer.
+std::unique_ptr<BinaryBuf>
+str_to_buffer(const std::string &data);
 
 } // namespace crypto
 } // namespace client
diff --git a/tests/e2ee.cpp b/tests/e2ee.cpp
index fd5b4f21c471fa55d061cca7a641c0e38f31aeed..f591e6bf5240820d75090aab37f0799d45cf7d09 100644
--- a/tests/e2ee.cpp
+++ b/tests/e2ee.cpp
@@ -23,23 +23,20 @@ using namespace mtx::responses;
 using namespace std;
 
 mtx::requests::UploadKeys
-generate_keys(std::shared_ptr<olm::Account> account,
-              const mtx::identifiers::User &user_id,
-              const std::string &device_id)
+generate_keys(std::shared_ptr<mtx::client::crypto::OlmClient> account)
 {
-        auto idks = mtx::client::crypto::identity_keys(account);
+        auto idks = account->identity_keys();
+        account->generate_one_time_keys(1);
+        auto otks = account->one_time_keys();
 
-        mtx::client::crypto::generate_one_time_keys(account, 1);
-        auto otks = mtx::client::crypto::one_time_keys(account);
-
-        return mtx::client::crypto::create_upload_keys_request(
-          account, idks, otks, user_id, device_id);
+        return account->create_upload_keys_request(otks);
 }
 
 TEST(Encryption, UploadIdentityKeys)
 {
         auto alice       = std::make_shared<Client>("localhost");
-        auto olm_account = mtx::client::crypto::olm_new_account();
+        auto olm_account = std::make_shared<mtx::client::crypto::OlmClient>();
+        olm_account->create_new_account();
 
         alice->login("alice", "secret", [](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -48,14 +45,16 @@ TEST(Encryption, UploadIdentityKeys)
         while (alice->access_token().empty())
                 sleep();
 
-        auto identity_keys = mtx::client::crypto::identity_keys(olm_account);
+        olm_account->set_user_id(alice->user_id().to_string());
+        olm_account->set_device_id(alice->device_id());
+
+        auto id_keys = olm_account->identity_keys();
 
-        ASSERT_TRUE(identity_keys.curve25519.size() > 10);
-        ASSERT_TRUE(identity_keys.curve25519.size() > 10);
+        ASSERT_TRUE(id_keys.curve25519.size() > 10);
+        ASSERT_TRUE(id_keys.curve25519.size() > 10);
 
         mtx::client::crypto::OneTimeKeys unused;
-        auto request = mtx::client::crypto::create_upload_keys_request(
-          olm_account, identity_keys, unused, alice->user_id(), alice->device_id());
+        auto request = olm_account->create_upload_keys_request(unused);
 
         // Make the request with the signed identity keys.
         alice->upload_keys(request, [](const mtx::responses::UploadKeys &res, RequestErr err) {
@@ -69,7 +68,8 @@ TEST(Encryption, UploadIdentityKeys)
 TEST(Encryption, UploadOneTimeKeys)
 {
         auto alice       = std::make_shared<Client>("localhost");
-        auto olm_account = mtx::client::crypto::olm_new_account();
+        auto olm_account = std::make_shared<mtx::client::crypto::OlmClient>();
+        olm_account->create_new_account();
 
         alice->login("alice", "secret", [](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -78,17 +78,20 @@ TEST(Encryption, UploadOneTimeKeys)
         while (alice->access_token().empty())
                 sleep();
 
-        auto nkeys = mtx::client::crypto::generate_one_time_keys(olm_account, 5);
+        olm_account->set_user_id(alice->user_id().to_string());
+        olm_account->set_device_id(alice->device_id());
+
+        auto nkeys = olm_account->generate_one_time_keys(5);
         EXPECT_EQ(nkeys, 5);
 
-        auto one_time_keys = mtx::client::crypto::one_time_keys(olm_account);
+        json otks = olm_account->one_time_keys();
 
         mtx::requests::UploadKeys req;
 
         // Create the proper structure for uploading.
         std::map<std::string, json> unsigned_keys;
 
-        auto obj = one_time_keys.at("curve25519");
+        auto obj = otks.at("curve25519");
         for (auto it = obj.begin(); it != obj.end(); ++it)
                 unsigned_keys["curve25519:" + it.key()] = it.value();
 
@@ -106,7 +109,8 @@ TEST(Encryption, UploadOneTimeKeys)
 TEST(Encryption, UploadSignedOneTimeKeys)
 {
         auto alice       = std::make_shared<Client>("localhost");
-        auto olm_account = mtx::client::crypto::olm_new_account();
+        auto olm_account = std::make_shared<mtx::client::crypto::OlmClient>();
+        olm_account->create_new_account();
 
         alice->login("alice", "secret", [](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -115,14 +119,16 @@ TEST(Encryption, UploadSignedOneTimeKeys)
         while (alice->access_token().empty())
                 sleep();
 
-        auto nkeys = mtx::client::crypto::generate_one_time_keys(olm_account, 5);
+        olm_account->set_user_id(alice->user_id().to_string());
+        olm_account->set_device_id(alice->device_id());
+
+        auto nkeys = olm_account->generate_one_time_keys(5);
         EXPECT_EQ(nkeys, 5);
 
-        auto one_time_keys = mtx::client::crypto::one_time_keys(olm_account);
+        auto one_time_keys = olm_account->one_time_keys();
 
         mtx::requests::UploadKeys req;
-        req.one_time_keys = mtx::client::crypto::sign_one_time_keys(
-          olm_account, one_time_keys, alice->user_id(), alice->device_id());
+        req.one_time_keys = olm_account->sign_one_time_keys(one_time_keys);
 
         alice->upload_keys(req, [nkeys](const mtx::responses::UploadKeys &res, RequestErr err) {
                 check_error(err);
@@ -136,7 +142,8 @@ TEST(Encryption, UploadSignedOneTimeKeys)
 TEST(Encryption, UploadKeys)
 {
         auto alice       = std::make_shared<Client>("localhost");
-        auto olm_account = mtx::client::crypto::olm_new_account();
+        auto olm_account = std::make_shared<mtx::client::crypto::OlmClient>();
+        olm_account->create_new_account();
 
         alice->login("alice", "secret", [](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -145,7 +152,10 @@ TEST(Encryption, UploadKeys)
         while (alice->access_token().empty())
                 sleep();
 
-        auto req = ::generate_keys(olm_account, alice->user_id(), alice->device_id());
+        olm_account->set_user_id(alice->user_id().to_string());
+        olm_account->set_device_id(alice->device_id());
+
+        auto req = generate_keys(olm_account);
 
         alice->upload_keys(req, [](const mtx::responses::UploadKeys &res, RequestErr err) {
                 check_error(err);
@@ -159,10 +169,13 @@ TEST(Encryption, UploadKeys)
 TEST(Encryption, QueryKeys)
 {
         auto alice     = std::make_shared<Client>("localhost");
-        auto alice_olm = mtx::client::crypto::olm_new_account();
+        auto alice_olm = std::make_shared<mtx::client::crypto::OlmClient>();
 
         auto bob     = std::make_shared<Client>("localhost");
-        auto bob_olm = mtx::client::crypto::olm_new_account();
+        auto bob_olm = std::make_shared<mtx::client::crypto::OlmClient>();
+
+        alice_olm->create_new_account();
+        bob_olm->create_new_account();
 
         alice->login("alice", "secret", [](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -174,9 +187,15 @@ TEST(Encryption, QueryKeys)
         while (alice->access_token().empty() || bob->access_token().empty())
                 sleep();
 
+        alice_olm->set_user_id(alice->user_id().to_string());
+        alice_olm->set_device_id(alice->device_id());
+
+        bob_olm->set_user_id(bob->user_id().to_string());
+        bob_olm->set_device_id(bob->device_id());
+
         // Create and upload keys for both users.
-        auto alice_req = ::generate_keys(alice_olm, alice->user_id(), alice->device_id());
-        auto bob_req   = ::generate_keys(bob_olm, bob->user_id(), bob->device_id());
+        auto alice_req = ::generate_keys(alice_olm);
+        auto bob_req   = ::generate_keys(bob_olm);
 
         // Validates that both upload requests are finished.
         atomic_int uploads(0);
@@ -257,7 +276,8 @@ TEST(Encryption, QueryKeys)
 TEST(Encryption, KeyChanges)
 {
         auto carl     = std::make_shared<Client>("localhost");
-        auto carl_olm = mtx::client::crypto::olm_new_account();
+        auto carl_olm = std::make_shared<mtx::client::crypto::OlmClient>();
+        carl_olm->create_new_account();
 
         carl->login("carl", "secret", [](const mtx::responses::Login &, RequestErr err) {
                 check_error(err);
@@ -266,6 +286,9 @@ TEST(Encryption, KeyChanges)
         while (carl->access_token().empty())
                 sleep();
 
+        carl_olm->set_device_id(carl->device_id());
+        carl_olm->set_user_id(carl->user_id().to_string());
+
         mtx::requests::CreateRoom req;
         carl->create_room(
           req, [carl, carl_olm](const mtx::responses::CreateRoom &, RequestErr err) {
@@ -281,8 +304,7 @@ TEST(Encryption, KeyChanges)
                             check_error(err);
                             const auto next_batch_token = res.next_batch;
 
-                            auto key_req =
-                              ::generate_keys(carl_olm, carl->user_id(), carl->device_id());
+                            auto key_req = ::generate_keys(carl_olm);
 
                             atomic_bool keys_uploaded(false);
 
diff --git a/tests/utils.cpp b/tests/utils.cpp
index db182c64466d51fb4fc2a7f66a89f8d36316bb82..bfef609fef67cefd60458a20815a0c22a24f05fd 100644
--- a/tests/utils.cpp
+++ b/tests/utils.cpp
@@ -11,8 +11,6 @@ using json = nlohmann::json;
 using namespace mtx::client::crypto;
 using namespace std;
 
-constexpr int SIGNATURE_SIZE = 64;
-
 TEST(Utilities, JsonToBuffer)
 {
         auto msg = json({{"key", "text"}});
@@ -23,33 +21,39 @@ TEST(Utilities, JsonToBuffer)
 
 TEST(Utilities, VerifySignedOneTimeKey)
 {
-        auto alice = olm_new_account();
+        auto alice = make_shared<OlmClient>();
+        alice->create_new_account();
+        alice->create_new_utility();
 
-        generate_one_time_keys(alice, 1);
-        auto keys = one_time_keys(alice);
+        alice->identity_keys();
 
-        auto first_key = keys["curve25519"].begin()->get<std::string>();
-        auto msg       = json({{"key", first_key}}).dump();
+        alice->generate_one_time_keys(1);
+        auto keys = alice->one_time_keys();
 
-        auto sig_buf = sign_message(alice, msg);
+        auto first_key = keys.curve25519.begin()->second;
+        auto msg       = json({{"key", first_key}}).dump();
 
-        olm::Utility utillity;
+        auto sig_buf = alice->sign_message(msg);
 
-        auto res = utillity.ed25519_verify(alice->identity_keys.ed25519_key.public_key,
-                                           str_to_buffer(msg)->data(),
-                                           msg.size(),
-                                           sig_buf->data(),
-                                           SIGNATURE_SIZE);
+        auto res = olm_ed25519_verify(alice->utility(),
+                                      str_to_buffer(alice->identity_keys().ed25519)->data(),
+                                      str_to_buffer(alice->identity_keys().ed25519)->size(),
+                                      str_to_buffer(msg)->data(),
+                                      str_to_buffer(msg)->size(),
+                                      sig_buf->data(),
+                                      sig_buf->size());
 
-        EXPECT_EQ(utillity.last_error, 0);
+        EXPECT_EQ(std::string(olm_utility_last_error(alice->utility())), "SUCCESS");
         EXPECT_EQ(res, 0);
 }
 
 TEST(Utilities, VerifySignedIdentityKeys)
 {
-        auto alice = olm_new_account();
+        auto alice = make_shared<OlmClient>();
+        alice->create_new_account();
+        alice->create_new_utility();
 
-        json keys = identity_keys(alice);
+        json keys = alice->identity_keys();
 
         auto msg = json({{"algorithms", {"m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"}},
                          {"device_id", "some_device"},
@@ -59,47 +63,16 @@ TEST(Utilities, VerifySignedIdentityKeys)
                            {"ed25519:some_device", keys["ed25519"]}}}})
                      .dump();
 
-        auto sig_buf = sign_message(alice, msg);
-
-        olm::Utility utillity;
+        auto sig_buf = alice->sign_message(msg);
 
-        auto res = utillity.ed25519_verify(alice->identity_keys.ed25519_key.public_key,
-                                           str_to_buffer(msg)->data(),
-                                           msg.size(),
-                                           sig_buf->data(),
-                                           SIGNATURE_SIZE);
+        auto res = olm_ed25519_verify(alice->utility(),
+                                      str_to_buffer(alice->identity_keys().ed25519)->data(),
+                                      str_to_buffer(alice->identity_keys().ed25519)->size(),
+                                      str_to_buffer(msg)->data(),
+                                      str_to_buffer(msg)->size(),
+                                      sig_buf->data(),
+                                      sig_buf->size());
 
-        EXPECT_EQ(utillity.last_error, 0);
+        EXPECT_EQ(std::string(olm_utility_last_error(alice->utility())), "SUCCESS");
         EXPECT_EQ(res, 0);
 }
-
-TEST(Utilities, OutboundGroupSession)
-{
-        auto alice = olm_new_account();
-        auto bob   = olm_new_account();
-        auto carl  = olm_new_account();
-
-        generate_one_time_keys(bob, 1);
-        generate_one_time_keys(carl, 1);
-
-        OneTimeKeys bob_otk = one_time_keys(bob);
-        IdentityKeys bob_ik = identity_keys(bob);
-
-        OneTimeKeys carl_otk = one_time_keys(carl);
-        IdentityKeys carl_ik = identity_keys(carl);
-
-        auto bob_session =
-          init_outbound_group_session(alice, bob_ik.curve25519, bob_otk.curve25519.begin()->second);
-        auto carl_session = init_outbound_group_session(
-          alice, carl_ik.curve25519, carl_otk.curve25519.begin()->second);
-
-        auto sid_1 = create_buffer(bob_session.session_id_length());
-        bob_session.session_id(sid_1->data(), sid_1->size());
-
-        EXPECT_EQ(sid_1->size(), 32);
-
-        auto sid_2 = create_buffer(carl_session.session_id_length());
-        carl_session.session_id(sid_2->data(), sid_2->size());
-
-        EXPECT_EQ(sid_2->size(), 32);
-}