diff --git a/.travis.yml b/.travis.yml
index 06a95396f81ca367f6f5fef2e907dd8e66f7e281..39a3ed54b7d846d4cc0967e1e24770d79edfe7c2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -46,7 +46,7 @@ matrix:
             - "g++-7"
 
 install:
-  - if [ $TRAVIS_OS_NAME == osx ]; then brew update && brew install boost libsodium; fi
+  - if [ $TRAVIS_OS_NAME == osx ]; then brew update && brew upgrade boost && brew install libsodium; fi
 
   - if [ $TRAVIS_OS_NAME == linux ]; then sudo add-apt-repository -y ppa:george-edison55/cmake-3.x; fi
   - if [ $TRAVIS_OS_NAME == linux ]; then sudo add-apt-repository -y ppa:chris-lea/libsodium; fi
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 411bd2074bba494d2bb1abaf28a30946850f9702..84079bdbb7d36cd25a224d142a55e2f492bde773 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -155,6 +155,9 @@ if (BUILD_LIB_TESTS)
     add_executable(e2ee tests/e2ee.cpp)
     target_link_libraries(e2ee matrix_client ${GTEST_BOTH_LIBRARIES})
 
+    add_executable(utils tests/utils.cpp)
+    target_link_libraries(utils matrix_client ${GTEST_BOTH_LIBRARIES})
+
     add_executable(connection tests/connection.cpp)
     target_link_libraries(connection matrix_client ${GTEST_BOTH_LIBRARIES})
 
@@ -163,10 +166,12 @@ if (BUILD_LIB_TESTS)
         add_dependencies(connection GTest)
         add_dependencies(media_api GTest)
         add_dependencies(e2ee GTest)
+        add_dependencies(utils GTest)
     endif()
 
     add_test(BasicConnectivity connection)
     add_test(ClientAPI client_api)
     add_test(MediaAPI media_api)
     add_test(Encryption e2ee)
+    add_test(Utilities utils)
 endif()
diff --git a/src/crypto.cpp b/src/crypto.cpp
index c8282c3133a25c39c90dc669ba9e74803ae9489c..843d41574d7ce586741b54a33727faa9da39d18c 100644
--- a/src/crypto.cpp
+++ b/src/crypto.cpp
@@ -2,11 +2,13 @@
 #include <sodium.h>
 
 #include "crypto.hpp"
-#include "olm/base64.hh"
+#include <olm/base64.hh>
 
 using json = nlohmann::json;
 using namespace mtx::client::crypto;
 
+constexpr std::size_t SIGNATURE_SIZE = 64;
+
 std::unique_ptr<uint8_t[]>
 mtx::client::crypto::create_buffer(std::size_t nbytes)
 {
@@ -81,21 +83,23 @@ mtx::client::crypto::sign_one_time_key(std::shared_ptr<olm::Account> account,
         json j{{"key", key}};
         auto str_json = j.dump();
 
-        constexpr std::size_t SIGNATURE_SIZE = 64;
+        auto signature_buf = sign_message(account, j.dump());
 
-        // Message
-        std::vector<std::uint8_t> tmp(str_json.begin(), str_json.end());
-        std::uint8_t *buf  = &tmp[0];
-        std::size_t nbytes = str_json.size();
+        return encode_base64(signature_buf.get(), SIGNATURE_SIZE);
+}
 
-        // Signature
-        auto signature_buf = create_buffer(SIGNATURE_SIZE);
-        account->sign(buf, nbytes, signature_buf.get(), SIGNATURE_SIZE);
+std::unique_ptr<uint8_t[]>
+mtx::client::crypto::sign_message(std::shared_ptr<olm::Account> account, const std::string &msg)
+{
+        // Message buffer
+        auto buf           = str_to_buffer(msg);
+        std::size_t nbytes = msg.size();
 
-        auto encoded_buf = create_buffer(SIGNATURE_SIZE);
-        olm::encode_base64(signature_buf.get(), SIGNATURE_SIZE, encoded_buf.get());
+        // Signature buffer
+        auto signature_buf = create_buffer(SIGNATURE_SIZE);
+        account->sign(buf.get(), nbytes, signature_buf.get(), SIGNATURE_SIZE);
 
-        return std::string(encoded_buf.get(), encoded_buf.get() + SIGNATURE_SIZE);
+        return signature_buf;
 }
 
 json
@@ -107,3 +111,52 @@ mtx::client::crypto::signed_one_time_key_json(const mtx::identifiers::User &user
         return json{{"key", key},
                     {"signatures", {{user_id.to_string(), {{"ed25519:" + device_id, signature}}}}}};
 }
+
+std::unique_ptr<uint8_t[]>
+mtx::client::crypto::str_to_buffer(const std::string &data)
+{
+        auto str_pointer  = reinterpret_cast<const uint8_t *>(&data[0]);
+        const auto nbytes = data.size();
+
+        auto buf = create_buffer(nbytes);
+        memcpy(buf.get(), str_pointer, nbytes);
+
+        return buf;
+}
+
+std::unique_ptr<uint8_t[]>
+mtx::client::crypto::decode_base64(const std::string &data)
+{
+        const auto nbytes       = data.size();
+        const int output_nbytes = olm::decode_base64_length(nbytes);
+
+        if (output_nbytes == -1)
+                throw std::runtime_error("invalid base64 input length");
+
+        auto output_buf = create_buffer(output_nbytes);
+        auto input_buf  = str_to_buffer(data);
+
+        olm::decode_base64(input_buf.get(), nbytes, output_buf.get());
+
+        return output_buf;
+}
+
+std::string
+mtx::client::crypto::encode_base64(const uint8_t *data, std::size_t len)
+{
+        const int output_nbytes = olm::encode_base64_length(len);
+
+        if (output_nbytes == -1)
+                throw std::runtime_error("invalid base64 input length");
+
+        auto output_buf = create_buffer(output_nbytes);
+        olm::encode_base64(data, output_nbytes, output_buf.get());
+
+        return std::string(output_buf.get(), output_buf.get() + output_nbytes);
+}
+
+std::unique_ptr<uint8_t[]>
+mtx::client::crypto::json_to_buffer(const nlohmann::json &obj)
+{
+        return str_to_buffer(obj.dump());
+}
diff --git a/src/crypto.hpp b/src/crypto.hpp
index 68d930b336d52aea8fa83f6a6268329b70bc787a..3c961daeb809efc9900533b011b0a1f4d71be910 100644
--- a/src/crypto.hpp
+++ b/src/crypto.hpp
@@ -50,16 +50,36 @@ one_time_keys(std::shared_ptr<olm::Account> user);
 std::unique_ptr<uint8_t[]>
 create_buffer(std::size_t nbytes);
 
-//! Sign the given one time keys.
+//! 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);
 
+//! Sign the given message.
+std::unique_ptr<uint8_t[]>
+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);
+
+std::string
+encode_base64(const uint8_t *data, std::size_t len);
+
+//! Decode the given base64 string
+std::unique_ptr<uint8_t[]>
+decode_base64(const std::string &data);
+
+//! Convert the given string to an uint8_t buffer.
+std::unique_ptr<uint8_t[]>
+str_to_buffer(const std::string &data);
+
+//! Convert the given json struct to an uint8_t buffer.
+std::unique_ptr<uint8_t[]>
+json_to_buffer(const nlohmann::json &obj);
+
 } // namespace crypto
 } // namespace client
 } // namespace mtx
diff --git a/tests/utils.cpp b/tests/utils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5e19bae70a4033f0f30e210d1fb53d35c68ad370
--- /dev/null
+++ b/tests/utils.cpp
@@ -0,0 +1,77 @@
+#include <gtest/gtest.h>
+
+#include "crypto.hpp"
+#include "json.hpp"
+
+#include "olm/utility.hh"
+
+using json = nlohmann::json;
+using namespace mtx::client::crypto;
+
+constexpr int SIGNATURE_SIZE = 64;
+
+TEST(Utilities, JsonToBuffer)
+{
+        auto msg = json({{"key", "text"}});
+        auto buf = json_to_buffer(msg);
+
+        auto strjson = msg.dump();
+        auto len     = msg.dump().size();
+
+        for (uint8_t i = 0; i < len; i++) {
+                if (strjson[i] != buf[i])
+                        FAIL();
+        }
+}
+
+TEST(Utilities, VerifySignedOneTimeKey)
+{
+        auto alice = olm_new_account();
+
+        generate_one_time_keys(alice, 1);
+        auto keys = one_time_keys(alice);
+
+        auto first_key = keys["curve25519"].begin()->get<std::string>();
+        auto msg       = json({{"key", first_key}}).dump();
+
+        auto sig_buf = sign_message(alice, msg);
+
+        olm::Utility utillity;
+
+        auto res = utillity.ed25519_verify(alice->identity_keys.ed25519_key.public_key,
+                                           str_to_buffer(msg).get(),
+                                           msg.size(),
+                                           sig_buf.get(),
+                                           SIGNATURE_SIZE);
+
+        EXPECT_EQ(utillity.last_error, 0);
+        EXPECT_EQ(res, 0);
+}
+
+TEST(Utilities, VerifySignedIdentityKeys)
+{
+        auto alice = olm_new_account();
+
+        json keys = identity_keys(alice);
+
+        auto msg = json({{"algorithms", {"m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"}},
+                         {"device_id", "some_device"},
+                         {"user_id", "@alice:localhost"},
+                         {"keys",
+                          {{"curve25519:some_device", keys["curve25519"]},
+                           {"ed25519:some_device", keys["ed25519"]}}}})
+                     .dump();
+
+        auto sig_buf = sign_message(alice, msg);
+
+        olm::Utility utillity;
+
+        auto res = utillity.ed25519_verify(alice->identity_keys.ed25519_key.public_key,
+                                           str_to_buffer(msg).get(),
+                                           msg.size(),
+                                           sig_buf.get(),
+                                           SIGNATURE_SIZE);
+
+        EXPECT_EQ(utillity.last_error, 0);
+        EXPECT_EQ(res, 0);
+}