From 09d4125ff164f5ca686d12ccb0790c35ce721a6b Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 27 Jun 2015 01:15:23 +0200 Subject: Rename axolotlpp as olm to avoid confusion with Axolotl-the-spec and Axolotl-the-OWS-libraries at moxie's request --- README.rst | 23 +- axolotl.py | 436 ---------------------------------- build_shared_library.py | 2 +- docs/Axolotl.svg | 6 +- include/axolotl/account.hh | 84 ------- include/axolotl/axolotl.hh | 295 ----------------------- include/axolotl/base64.hh | 49 ---- include/axolotl/cipher.hh | 128 ---------- include/axolotl/crypto.hh | 148 ------------ include/axolotl/error.hh | 34 --- include/axolotl/list.hh | 119 ---------- include/axolotl/memory.hh | 38 --- include/axolotl/message.hh | 126 ---------- include/axolotl/pickle.hh | 178 -------------- include/axolotl/ratchet.hh | 177 -------------- include/axolotl/session.hh | 114 --------- include/olm/account.hh | 84 +++++++ include/olm/base64.hh | 49 ++++ include/olm/cipher.hh | 128 ++++++++++ include/olm/crypto.hh | 148 ++++++++++++ include/olm/error.hh | 34 +++ include/olm/list.hh | 119 ++++++++++ include/olm/memory.hh | 38 +++ include/olm/message.hh | 126 ++++++++++ include/olm/olm.hh | 295 +++++++++++++++++++++++ include/olm/pickle.hh | 178 ++++++++++++++ include/olm/ratchet.hh | 177 ++++++++++++++ include/olm/session.hh | 114 +++++++++ javascript/axolotl_post.js | 248 ------------------- javascript/axolotl_pre.js | 1 - javascript/build.py | 6 +- javascript/demo.html | 10 +- javascript/olm_post.js | 248 +++++++++++++++++++ javascript/olm_pre.js | 1 + olm.py | 436 ++++++++++++++++++++++++++++++++++ src/account.cpp | 88 +++---- src/axolotl.cpp | 580 --------------------------------------------- src/base64.cpp | 10 +- src/cipher.cpp | 46 ++-- src/crypto.cpp | 70 +++--- src/memory.cpp | 6 +- src/message.cpp | 20 +- src/olm.cpp | 580 +++++++++++++++++++++++++++++++++++++++++++++ src/pickle.cpp | 38 +-- src/ratchet.cpp | 258 ++++++++++---------- src/session.cpp | 170 ++++++------- tests/test_axolotl.cpp | 269 --------------------- tests/test_base64.cpp | 10 +- tests/test_crypto.cpp | 44 ++-- tests/test_list.cpp | 6 +- tests/test_message.cpp | 12 +- tests/test_olm.cpp | 269 +++++++++++++++++++++ tests/test_ratchet.cpp | 36 +-- 53 files changed, 3458 insertions(+), 3451 deletions(-) delete mode 100755 axolotl.py delete mode 100644 include/axolotl/account.hh delete mode 100644 include/axolotl/axolotl.hh delete mode 100644 include/axolotl/base64.hh delete mode 100644 include/axolotl/cipher.hh delete mode 100644 include/axolotl/crypto.hh delete mode 100644 include/axolotl/error.hh delete mode 100644 include/axolotl/list.hh delete mode 100644 include/axolotl/memory.hh delete mode 100644 include/axolotl/message.hh delete mode 100644 include/axolotl/pickle.hh delete mode 100644 include/axolotl/ratchet.hh delete mode 100644 include/axolotl/session.hh create mode 100644 include/olm/account.hh create mode 100644 include/olm/base64.hh create mode 100644 include/olm/cipher.hh create mode 100644 include/olm/crypto.hh create mode 100644 include/olm/error.hh create mode 100644 include/olm/list.hh create mode 100644 include/olm/memory.hh create mode 100644 include/olm/message.hh create mode 100644 include/olm/olm.hh create mode 100644 include/olm/pickle.hh create mode 100644 include/olm/ratchet.hh create mode 100644 include/olm/session.hh delete mode 100644 javascript/axolotl_post.js delete mode 100644 javascript/axolotl_pre.js create mode 100644 javascript/olm_post.js create mode 100644 javascript/olm_pre.js create mode 100755 olm.py delete mode 100644 src/axolotl.cpp create mode 100644 src/olm.cpp delete mode 100644 tests/test_axolotl.cpp create mode 100644 tests/test_olm.cpp diff --git a/README.rst b/README.rst index c5b7ba8..3a382ab 100644 --- a/README.rst +++ b/README.rst @@ -1,5 +1,5 @@ -Axolotlpp -========= +Olm +=== An implementation of the axolotl ratchet as described by https://github.com/trevp/axolotl/wiki, written in C++11 and exposed as a C API @@ -7,7 +7,7 @@ https://github.com/trevp/axolotl/wiki, written in C++11 and exposed as a C API Building -------- -To build axolotlpp as a shared library run: +To build olm as a shared library run: .. code:: bash @@ -29,19 +29,19 @@ To build the javascript bindings run: Design ------ -Axolotlpp is designed to be easy port to different platforms and to be easy +Olm is designed to be easy port to different platforms and to be easy to write bindings for. Error Handling ~~~~~~~~~~~~~~ -All C functions in the API for axolotlpp return ``axolotl_error()`` on error. +All C functions in the API for olm return ``olm_error()`` on error. This makes it easy to check for error conditions within the language bindings. Random Numbers ~~~~~~~~~~~~~~ -Axolotlpp doesn't generate random numbers itself. Instead the caller must +Olm doesn't generate random numbers itself. Instead the caller must provide the random data. This makes it easier to port the library to different platforms since the caller can use whatever cryptographic random number generator their platform provides. @@ -49,7 +49,7 @@ generator their platform provides. Memory ~~~~~~ -Axolotlpp avoids calling malloc or allocating memory on the heap itself. +Olm avoids calling malloc or allocating memory on the heap itself. Instead the library calculates how much memory will be needed to hold the output and the caller supplies a buffer of the appropriate size. @@ -62,6 +62,13 @@ strings will find it easier to handle the output. Dependencies ~~~~~~~~~~~~ -Axolotlpp uses pure C implementations of the cryptographic primitives used by +Olm uses pure C implementations of the cryptographic primitives used by the ratchet. While this decreases the performance it makes it much easier to compile the library for different architectures. + +What's an olm? +~~~~~~~~~~~~~~ + +It's a really cool species of European troglodytic salamander. +Matthew once tried to climb into a pool full of them in Postojnska Jama. +http://www.postojnska-jama.eu/en/about-the-cave/meet-the-dragon-s-offspring/ diff --git a/axolotl.py b/axolotl.py deleted file mode 100755 index 239a6ce..0000000 --- a/axolotl.py +++ /dev/null @@ -1,436 +0,0 @@ -#! /usr/bin/python -from ctypes import * -import json - -lib = cdll.LoadLibrary("build/libaxolotl.so") - - -lib.axolotl_error.argtypes = [] -lib.axolotl_error.restypes = c_size_t - -ERR = lib.axolotl_error() - -class AxolotlError(Exception): - pass - - -lib.axolotl_account_size.argtypes = [] -lib.axolotl_account_size.restype = c_size_t - -lib.axolotl_account.argtypes = [c_void_p] -lib.axolotl_account.restype = c_void_p - -lib.axolotl_account_last_error.argtypes = [c_void_p] -lib.axolotl_account_last_error.restype = c_char_p - -def account_errcheck(res, func, args): - if res == ERR: - raise AxolotlError("%s: %s" % ( - func.__name__, lib.axolotl_account_last_error(args[0]) - )) - return res - - -def account_function(func, *types): - func.argtypes = (c_void_p,) + types - func.restypes = c_size_t - func.errcheck = account_errcheck - - -account_function( - lib.axolotl_pickle_account, c_void_p, c_size_t, c_void_p, c_size_t -) -account_function( - lib.axolotl_unpickle_account, c_void_p, c_size_t, c_void_p, c_size_t -) -account_function(lib.axolotl_create_account_random_length) -account_function(lib.axolotl_create_account, c_void_p, c_size_t) -account_function(lib.axolotl_account_identity_keys_length) -account_function(lib.axolotl_account_identity_keys, c_void_p, c_size_t) -account_function(lib.axolotl_account_one_time_keys_length) -account_function(lib.axolotl_account_one_time_keys, c_void_p, c_size_t) - -def read_random(n): - with open("/dev/urandom", "rb") as f: - return f.read(n) - -class Account(object): - def __init__(self): - self.buf = create_string_buffer(lib.axolotl_account_size()) - self.ptr = lib.axolotl_account(self.buf) - - def create(self): - random_length = lib.axolotl_create_account_random_length(self.ptr) - random = read_random(random_length) - random_buffer = create_string_buffer(random) - lib.axolotl_create_account(self.ptr, random_buffer, random_length) - - def pickle(self, key): - key_buffer = create_string_buffer(key) - pickle_length = lib.axolotl_pickle_account_length(self.ptr) - pickle_buffer = create_string_buffer(pickle_length) - lib.axolotl_pickle_account( - self.ptr, key_buffer, len(key), pickle_buffer, pickle_length - ) - return pickle_buffer.raw - - def unpickle(self, key, pickle): - key_buffer = create_string_buffer(key) - pickle_buffer = create_string_buffer(pickle) - lib.axolotl_unpickle_account( - self.ptr, key_buffer, len(key), pickle_buffer, len(pickle) - ) - - def identity_keys(self): - out_length = lib.axolotl_account_identity_keys_length(self.ptr) - out_buffer = create_string_buffer(out_length) - lib.axolotl_account_identity_keys(self.ptr, out_buffer, out_length) - return json.loads(out_buffer.raw) - - def one_time_keys(self): - out_length = lib.axolotl_account_one_time_keys_length(self.ptr) - out_buffer = create_string_buffer(out_length) - lib.axolotl_account_one_time_keys(self.ptr, out_buffer, out_length) - return json.loads(out_buffer.raw) - - def clear(self): - pass - - -lib.axolotl_session_size.argtypes = [] -lib.axolotl_session_size.restype = c_size_t - -lib.axolotl_session.argtypes = [c_void_p] -lib.axolotl_session.restype = c_void_p - -lib.axolotl_session_last_error.argtypes = [c_void_p] -lib.axolotl_session_last_error.restype = c_char_p - - -def session_errcheck(res, func, args): - if res == ERR: - raise AxolotlError("%s: %s" % ( - func.__name__, lib.axolotl_session_last_error(args[0]) - )) - return res - - -def session_function(func, *types): - func.argtypes = (c_void_p,) + types - func.restypes = c_size_t - func.errcheck = session_errcheck - -session_function(lib.axolotl_session_last_error) -session_function( - lib.axolotl_pickle_session, c_void_p, c_size_t, c_void_p, c_size_t -) -session_function( - lib.axolotl_unpickle_session, c_void_p, c_size_t, c_void_p, c_size_t -) -session_function(lib.axolotl_create_outbound_session_random_length) -session_function( - lib.axolotl_create_outbound_session, - c_void_p, # Account - c_void_p, c_size_t, # Identity Key - c_uint, # One Time Key Id - c_void_p, c_size_t, # One Time Key - c_void_p, c_size_t, # Random -) -session_function( - lib.axolotl_create_inbound_session, - c_void_p, # Account - c_void_p, c_size_t, # Pre Key Message -) -session_function(lib.axolotl_matches_inbound_session, c_void_p, c_size_t) -session_function(lib.axolotl_encrypt_message_type) -session_function(lib.axolotl_encrypt_random_length) -session_function(lib.axolotl_encrypt_message_length, c_size_t) -session_function( - lib.axolotl_encrypt, - c_void_p, c_size_t, # Plaintext - c_void_p, c_size_t, # Random - c_void_p, c_size_t, # Message -); -session_function( - lib.axolotl_decrypt_max_plaintext_length, - c_size_t, # Message Type - c_void_p, c_size_t, # Message -) -session_function( - lib.axolotl_decrypt, - c_size_t, # Message Type - c_void_p, c_size_t, # Message - c_void_p, c_size_t, # Plaintext -) - -class Session(object): - def __init__(self): - self.buf = create_string_buffer(lib.axolotl_session_size()) - self.ptr = lib.axolotl_session(self.buf) - - def pickle(self, key): - key_buffer = create_string_buffer(key) - pickle_length = lib.axolotl_pickle_session_length(self.ptr) - pickle_buffer = create_string_buffer(pickle_length) - lib.axolotl_pickle_session( - self.ptr, key_buffer, len(key), pickle_buffer, pickle_length - ) - return pickle_buffer.raw - - def unpickle(self, key, pickle): - key_buffer = create_string_buffer(key) - pickle_buffer = create_string_buffer(pickle) - lib.axolotl_unpickle_session( - self.ptr, key_buffer, len(key), pickle_buffer, len(pickle) - ) - - def create_outbound(self, account, identity_key, one_time_key_id, - one_time_key): - r_length = lib.axolotl_create_outbound_session_random_length(self.ptr) - random = read_random(r_length) - random_buffer = create_string_buffer(random) - identity_key_buffer = create_string_buffer(identity_key) - one_time_key_buffer = create_string_buffer(one_time_key) - lib.axolotl_create_outbound_session( - self.ptr, - account.ptr, - identity_key_buffer, len(identity_key), - one_time_key_id, - one_time_key_buffer, len(one_time_key), - random_buffer, r_length - ) - - def create_inbound(self, account, one_time_key_message): - one_time_key_message_buffer = create_string_buffer(one_time_key_message) - lib.axolotl_create_inbound_session( - self.ptr, - account.ptr, - one_time_key_message_buffer, len(one_time_key_message) - ) - - def matches_inbound(self, one_time_key_message): - one_time_key_message_buffer = create_string_buffer(one_time_key_message) - return bool(lib.axolotl_create_inbound_session( - self.ptr, - one_time_key_message_buffer, len(one_time_key_message) - )) - - def encrypt(self, plaintext): - r_length = lib.axolotl_encrypt_random_length(self.ptr) - random = read_random(r_length) - random_buffer = create_string_buffer(random) - - message_type = lib.axolotl_encrypt_message_type(self.ptr) - message_length = lib.axolotl_encrypt_message_length( - self.ptr, len(plaintext) - ) - message_buffer = create_string_buffer(message_length) - - plaintext_buffer = create_string_buffer(plaintext) - - lib.axolotl_encrypt( - self.ptr, - plaintext_buffer, len(plaintext), - random_buffer, r_length, - message_buffer, message_length, - ) - return message_type, message_buffer.raw - - def decrypt(self, message_type, message): - message_buffer = create_string_buffer(message) - max_plaintext_length = lib.axolotl_decrypt_max_plaintext_length( - self.ptr, message_type, message_buffer, len(message) - ) - plaintext_buffer = create_string_buffer(max_plaintext_length) - message_buffer = create_string_buffer(message) - plaintext_length = lib.axolotl_decrypt( - self.ptr, message_type, message_buffer, len(message), - plaintext_buffer, max_plaintext_length - ) - return plaintext_buffer.raw[:plaintext_length] - - def clear(self): - pass - - -if __name__ == '__main__': - import argparse - import sys - import os - import yaml - - parser = argparse.ArgumentParser() - parser.add_argument("--key", help="Account encryption key", default="") - commands = parser.add_subparsers() - - create_account = commands.add_parser("create_account", help="Create a new account") - create_account.add_argument("account_file", help="Local account file") - - def do_create_account(args): - if os.path.exists(args.account_file): - sys.stderr.write("Account %r file already exists" % ( - args.account_file, - )) - sys.exit(1) - account = Account() - account.create() - with open(args.account_file, "wb") as f: - f.write(account.pickle(args.key)) - - create_account.set_defaults(func=do_create_account) - - keys = commands.add_parser("keys", help="List public keys for an account") - keys.add_argument("account_file", help="Local account_file") - - def do_keys(args): - account = Account() - with open(args.account_file, "rb") as f: - account.unpickle(args.key, f.read()) - (r_id, id_key), (signed_id, signed_key) = account.identity_keys() - ot_keys = account.one_time_keys() - result1 = { - "identityKey": str(id_key), - "signedKey": { - "keyId": signed_id, - "publicKey": str(signed_key), - }, - "lastResortKey": { - "keyId": ot_keys[0][0], - "publicKey": str(ot_keys[0][1]), - }, - } - result2 = { - "oneTimeKeys": [{ - "keyId": k[0], - "publicKey": str(k[1]), - } for k in ot_keys[1:]] - } - try: - yaml.dump(result1, sys.stdout, default_flow_style=False) - yaml.dump(result2, sys.stdout, default_flow_style=False) - except: - pass - - keys.set_defaults(func=do_keys) - - outbound = commands.add_parser("outbound", help="Create an outbound session") - outbound.add_argument("account_file", help="Local account file") - outbound.add_argument("session_file", help="Local session file") - outbound.add_argument("identity_key", help="Remote identity key") - outbound.add_argument("signed_key_id", help="Remote signed key id", - type=int) - outbound.add_argument("signed_key", help="Remote signed key") - outbound.add_argument("one_time_key_id", help="Remote one time key id", - type=int) - outbound.add_argument("one_time_key", help="Remote one time key") - - def do_outbound(args): - if os.path.exists(args.session_file): - sys.stderr.write("Session %r file already exists" % ( - args.account_file, - )) - sys.exit(1) - account = Account() - with open(args.account_file, "rb") as f: - account.unpickle(args.key, f.read()) - session = Session() - session.create_outbound( - account, args.identity_key, args.signed_key_id, args.signed_key, - args.one_time_key_id, args.one_time_key - ) - with open(args.session_file, "wb") as f: - f.write(session.pickle(args.key)) - - outbound.set_defaults(func=do_outbound) - - def open_in(path): - if path == "-": - return sys.stdin - else: - return open(path, "rb") - - def open_out(path): - if path == "-": - return sys.stdout - else: - return open(path, "wb") - - inbound = commands.add_parser("inbound", help="Create an inbound session") - inbound.add_argument("account_file", help="Local account file") - inbound.add_argument("session_file", help="Local session file") - inbound.add_argument("message_file", help="Message", default="-") - inbound.add_argument("plaintext_file", help="Plaintext", default="-") - - def do_inbound(args): - if os.path.exists(args.session_file): - sys.stderr.write("Session %r file already exists" % ( - args.account_file, - )) - sys.exit(1) - account = Account() - with open(args.account_file, "rb") as f: - account.unpickle(args.key, f.read()) - with open_in(args.message_file) as f: - message_type = f.read(8) - message = f.read() - if message_type != "PRE_KEY ": - sys.stderr.write("Expecting a PRE_KEY message") - sys.exit(1) - session = Session() - session.create_inbound(account, message) - plaintext = session.decrypt(0, message) - with open(args.session_file, "wb") as f: - f.write(session.pickle(args.key)) - with open_out(args.plaintext_file) as f: - f.write(plaintext) - - inbound.set_defaults(func=do_inbound) - - encrypt = commands.add_parser("encrypt", help="Encrypt a message") - encrypt.add_argument("session_file", help="Local session file") - encrypt.add_argument("plaintext_file", help="Plaintext", default="-") - encrypt.add_argument("message_file", help="Message", default="-") - - def do_encrypt(args): - session = Session() - with open(args.session_file, "rb") as f: - session.unpickle(args.key, f.read()) - with open_in(args.plaintext_file) as f: - plaintext = f.read() - message_type, message = session.encrypt(plaintext) - with open(args.session_file, "wb") as f: - f.write(session.pickle(args.key)) - with open_out(args.message_file) as f: - f.write(["PRE_KEY ", "MESSAGE "][message_type]) - f.write(message) - - encrypt.set_defaults(func=do_encrypt) - - decrypt = commands.add_parser("decrypt", help="Decrypt a message") - decrypt.add_argument("session_file", help="Local session file") - decrypt.add_argument("plaintext_file", help="Plaintext", default="-") - decrypt.add_argument("message_file", help="Message", default="-") - - def do_decrypt(args): - session = Session() - with open(args.session_file, "rb") as f: - session.unpickle(args.key, f.read()) - with open_in(args.message_file) as f: - message_type = f.read(8) - message = f.read() - if message_type not in {"PRE_KEY ", "MESSAGE "}: - sys.stderr.write("Expecting a PRE_KEY or MESSAGE message") - sys.exit(1) - message_type = 1 if message_type == "MESSAGE " else 0 - plaintext = session.decrypt(message_type, message) - with open(args.session_file, "wb") as f: - f.write(session.pickle(args.key)) - with open_out(args.plaintext_file) as f: - f.write(plaintext) - - decrypt.set_defaults(func=do_decrypt) - - args = parser.parse_args() - args.func(args) - - diff --git a/build_shared_library.py b/build_shared_library.py index 1081846..6dbafcd 100755 --- a/build_shared_library.py +++ b/build_shared_library.py @@ -25,7 +25,7 @@ source_files = glob.glob("src/*.cpp") compile_args = "g++ -O0 -g -Iinclude -Ilib --std=c++11 --shared -fPIC".split() compile_args += source_files -library = "build/libaxolotl.so" +library = "build/libolm.so" def run(args): print " ".join(args) diff --git a/docs/Axolotl.svg b/docs/Axolotl.svg index 934f3ab..26ee8c3 100644 --- a/docs/Axolotl.svg +++ b/docs/Axolotl.svg @@ -229,7 +229,7 @@ y="-64" x="32" id="tspan4814" - sodipodi:role="line">"AXOLOTL_ROOT" + sodipodi:role="line">"OLM_ROOT" "AXOLOTL_KEYS" + y="-64">"OLM_KEYS" "AXOLOTL_RATCHET" + y="-64">"OLM_RATCHET" - -namespace axolotl { - - -struct LocalKey { - std::uint32_t id; - Curve25519KeyPair key; -}; - - -struct SignedKey : LocalKey { - std::uint8_t signature[64]; -}; - - -static std::size_t const MAX_ONE_TIME_KEYS = 100; - -struct Account { - LocalKey identity_key; - LocalKey last_resort_one_time_key; - List one_time_keys; - ErrorCode last_error; - - /** Number of random bytes needed to create a new account */ - std::size_t new_account_random_length(); - - /** Create a new account. Returns NOT_ENOUGH_RANDOM if the number of random - * bytes is too small. */ - std::size_t new_account( - uint8_t const * random, std::size_t random_length - ); - - LocalKey const * lookup_key( - std::uint32_t id - ); - - std::size_t remove_key( - std::uint32_t id - ); -}; - - -std::size_t pickle_length( - Account const & value -); - - -std::uint8_t * pickle( - std::uint8_t * pos, - Account const & value -); - - -std::uint8_t const * unpickle( - std::uint8_t const * pos, std::uint8_t const * end, - Account & value -); - - -} // namespace axolotl - -#endif /* AXOLOTL_ACCOUNT_HH_ */ diff --git a/include/axolotl/axolotl.hh b/include/axolotl/axolotl.hh deleted file mode 100644 index 90065a7..0000000 --- a/include/axolotl/axolotl.hh +++ /dev/null @@ -1,295 +0,0 @@ -/* Copyright 2015 OpenMarket Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef AXOLOTL_HH_ -#define AXOLOTL_HH_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -static const size_t AXOLOTL_MESSAGE_TYPE_PRE_KEY = 0; -static const size_t AXOLOTL_MESSAGE_TYPE_MESSAGE = 1; - -struct AxolotlAccount; -struct AxolotlSession; - -/** The size of an account object in bytes */ -size_t axolotl_account_size(); - -/** The size of a session object in bytes */ -size_t axolotl_session_size(); - -/** Initialise an account object using the supplied memory - * The supplied memory must be at least axolotl_account_size() bytes */ -AxolotlAccount * axolotl_account( - void * memory -); - -/** Initialise a session object using the supplied memory - * The supplied memory must be at least axolotl_session_size() bytes */ -AxolotlSession * axolotl_session( - void * memory -); - -/** The value that axolotl will return from a function if there was an error */ -size_t axolotl_error(); - -/** A null terminated string describing the most recent error to happen to an - * account */ -const char * axolotl_account_last_error( - AxolotlSession * account -); - -/** A null terminated string describing the most recent error to happen to a - * session */ -const char * axolotl_session_last_error( - AxolotlSession * session -); - -/** Returns the number of bytes needed to store an account */ -size_t axolotl_pickle_account_length( - AxolotlAccount * account -); - -/** Returns the number of bytes needed to store a session */ -size_t axolotl_pickle_session_length( - AxolotlSession * session -); - -/** Stores an account as a base64 string. Encrypts the account using the - * supplied key. Returns the length of the pickled account on success. - * Returns axolotl_error() on failure. If the pickle output buffer - * is smaller than axolotl_pickle_account_length() then - * axolotl_account_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */ -size_t axolotl_pickle_account( - AxolotlAccount * account, - void const * key, size_t key_length, - void * pickled, size_t pickled_length -); - -/** Stores a session as a base64 string. Encrypts the session using the - * supplied key. Returns the length of the pickled session on success. - * Returns axolotl_error() on failure. If the pickle output buffer - * is smaller than axolotl_pickle_session_length() then - * axolotl_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */ -size_t axolotl_pickle_session( - AxolotlSession * session, - void const * key, size_t key_length, - void * pickled, size_t pickled_length -); - -/** Loads an account from a pickled base64 string. Decrypts the account using - * the supplied key. Returns axolotl_error() on failure. If the key doesn't - * match the one used to encrypt the account then axolotl_account_last_error() - * will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then - * axolotl_account_last_error() will be "INVALID_BASE64". The input pickled - * buffer is destroyed */ -size_t axolotl_unpickle_account( - AxolotlAccount * account, - void const * key, size_t key_length, - void * pickled, size_t pickled_length -); - -/** Loads a session from a pickled base64 string. Decrypts the session using - * the supplied key. Returns axolotl_error() on failure. If the key doesn't - * match the one used to encrypt the account then axolotl_session_last_error() - * will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then - * axolotl_session_last_error() will be "INVALID_BASE64". The input pickled - * buffer is destroyed */ -size_t axolotl_unpickle_session( - AxolotlSession * session, - void const * key, size_t key_length, - void * pickled, size_t pickled_length -); - -/** The number of random bytes needed to create an account.*/ -size_t axolotl_create_account_random_length( - AxolotlAccount * account -); - -/** Creates a new account. Returns axolotl_error() on failure. If weren't - * enough random bytes then axolotl_account_last_error() will be - * "NOT_ENOUGH_RANDOM" */ -size_t axolotl_create_account( - AxolotlAccount * account, - void const * random, size_t random_length -); - -/** The size of the output buffer needed to hold the identity keys */ -size_t axolotl_account_identity_keys_length( - AxolotlAccount * account -); - -/** Writes the public parts of the identity keys for the account into the - * identity_keys output buffer. The output is formatted as though it was - * created with sprintf(output, "[[%10d,\"%43s\"]\n]", key_id, key_base64). - * The output can either be parsed as fixed width using the above format or by - * a JSON parser. Returns axolotl_error() on failure. If the identity_keys - * buffer was too small then axolotl_account_last_error() will be - * "OUTPUT_BUFFER_TOO_SMALL". */ -size_t axolotl_account_identity_keys( - AxolotlAccount * account, - void * identity_keys, size_t identity_key_length -); - -/** The size of the output buffer needed to hold the one time keys */ -size_t axolotl_account_one_time_keys_length( - AxolotlAccount * account -); - -/** Writes the public parts of the one time keys for the account into the - * one_time_keys output buffer. The first key will be formatted as though it was - * created with sprintf(output, "[[%10d,\"%43s\"]\n", key_id, key_base64). - * subsequent keys are formatted with ",[%10d,\"%43s\"]\n". The final byte of - * output will be "]". The output can either be parsed as fixed width using - * the above format or by a JSON parser. Returns axolotl_error() on failure. - * If the one_time_keys buffer was too small then axolotl_account_last_error() - * will be "OUTPUT_BUFFER_TOO_SMALL". */ -size_t axolotl_account_one_time_keys( - AxolotlAccount * account, - void * one_time_keys, size_t one_time_keys_length -); - -/* TODO: Add methods for marking keys as used, generating new keys, and - * tracking which keys have been uploaded to the central servers */ - -/** The number of random bytes needed to create an outbound session */ -size_t axolotl_create_outbound_session_random_length( - AxolotlSession * session -); - -/** Creates a new out-bound session for sending messages to a given identity_key - * and one_time_key. Returns axolotl_error() on failure. If the keys couldn't be - * decoded as base64 then axolotl_session_last_error() will be "INVALID_BASE64" - * If there weren't enough random bytes then axolotl_session_last_error() will - * be "NOT_ENOUGH_RANDOM". */ -size_t axolotl_create_outbound_session( - AxolotlSession * session, - AxolotlAccount * account, - void const * their_identity_key, size_t their_identity_key_length, - unsigned their_one_time_key_id, - void const * their_one_time_key, size_t their_one_time_key_length, - void const * random, size_t random_length -); - -/** Create a new in-bound session for sending/receiving messages from an - * incoming PRE_KEY message. Returns axolotl_error() on failure. If the base64 - * couldn't be decoded then axolotl_session_last_error will be "INVALID_BASE64". - * If the message was for an unsupported protocol version then - * axolotl_session_last_error() will be "BAD_MESSAGE_VERSION". If the message - * couldn't be decoded then then axolotl_session_last_error() will be - * "BAD_MESSAGE_FORMAT". If the message refers to an unknown one time - * key then axolotl_session_last_error() will be "BAD_MESSAGE_KEY_ID". */ -size_t axolotl_create_inbound_session( - AxolotlSession * session, - AxolotlAccount * account, - void * one_time_key_message, size_t message_length -); - -/** Checks if the PRE_KEY message is for this in-bound session. This can happen - * if multiple messages are sent to this account before this account sends a - * message in reply. Returns axolotl_error() on failure. If the base64 - * couldn't be decoded then axolotl_session_last_error will be "INVALID_BASE64". - * If the message was for an unsupported protocol version then - * axolotl_session_last_error() will be "BAD_MESSAGE_VERSION". If the message - * couldn't be decoded then then axolotl_session_last_error() will be - * "BAD_MESSAGE_FORMAT". */ -size_t axolotl_matches_inbound_session( - AxolotlSession * session, - void * one_time_key_message, size_t message_length -); - -/** Removes the one time keys that the session used from the account. Returns - * axolotl_error() on failure. If the account doesn't have any matching one time - * keys then axolotl_account_last_error() will be "BAD_MESSAGE_KEY_ID". */ -size_t axolotl_remove_one_time_keys( - AxolotlAccount * account, - AxolotlSession * session -); - -/** The type of the next message that axolotl_encrypt() will return. Returns - * AXOLOTL_MESSAGE_TYPE_PRE_KEY if the message will be a PRE_KEY message. - * Returns AXOLOTL_MESSAGE_TYPE_MESSAGE if the message will be a normal message. - * Returns axolotl_error on failure. */ -size_t axolotl_encrypt_message_type( - AxolotlSession * session -); - -/** The number of random bytes needed to encrypt the next message. */ -size_t axolotl_encrypt_random_length( - AxolotlSession * session -); - -/** The size of the next message in bytes for the given number of plain-text - * bytes. */ -size_t axolotl_encrypt_message_length( - AxolotlSession * session, - size_t plaintext_length -); - -/** Encrypts a message using the session. Returns the length of the message in - * bytes on success. Writes the message as base64 into the message buffer. - * Returns axolotl_error() on failure. If the message buffer is too small then - * axolotl_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". If there - * weren't enough random bytes then axolotl_session_last_error() will be - * "NOT_ENOUGH_RANDOM". */ -size_t axolotl_encrypt( - AxolotlSession * session, - void const * plaintext, size_t plaintext_length, - void const * random, size_t random_length, - void * message, size_t message_length -); - -/** The maximum number of bytes of plain-text a given message could decode to. - * The actual size could be different due to padding. The input message buffer - * is destroyed. Returns axolotl_error() on failure. If the message base64 - * couldn't be decoded then axolotl_session_last_error() will be - * "INVALID_BASE64". If the message is for an unsupported version of the - * protocol then axolotl_session_last_error() will be "BAD_MESSAGE_VERSION". - * If the message couldn't be decoded then axolotl_session_last_error() will be - * "BAD_MESSAGE_FORMAT". */ -size_t axolotl_decrypt_max_plaintext_length( - AxolotlSession * session, - size_t message_type, - void * message, size_t message_length -); - -/** Decrypts a message using the session. The input message buffer is destroyed. - * Returns the length of the plain-text on success. Returns axolotl_error() on - * failure. If the plain-text buffer is smaller than - * axolotl_decrypt_max_plaintext_length() then axolotl_session_last_error() - * will be "OUTPUT_BUFFER_TOO_SMALL". If the base64 couldn't be decoded then - * axolotl_session_last_error() will be "INVALID_BASE64". If the message is for - * an unsupported version of the protocol then axolotl_session_last_error() will - * be "BAD_MESSAGE_VERSION". If the message couldn't be decoded then - * axolotl_session_last_error() will be BAD_MESSAGE_FORMAT". - * If the MAC on the message was invalid then axolotl_session_last_error() will - * be "BAD_MESSAGE_MAC". */ -size_t axolotl_decrypt( - AxolotlSession * session, - size_t message_type, - void * message, size_t message_length, - void * plaintext, size_t max_plaintext_length -); - - - -#ifdef __cplusplus -} -#endif - -#endif /* AXOLOTL_HH_ */ diff --git a/include/axolotl/base64.hh b/include/axolotl/base64.hh deleted file mode 100644 index 361a558..0000000 --- a/include/axolotl/base64.hh +++ /dev/null @@ -1,49 +0,0 @@ -/* Copyright 2015 OpenMarket Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef AXOLOLT_BASE64_HH_ -#define AXOLOLT_BASE64_HH_ - -#include -#include - -namespace axolotl { - - -std::size_t encode_base64_length( - std::size_t input_length -); - - -void encode_base64( - std::uint8_t const * input, std::size_t input_length, - std::uint8_t * output -); - - -std::size_t decode_base64_length( - std::size_t input_length -); - - -void decode_base64( - std::uint8_t const * input, std::size_t input_length, - std::uint8_t * output -); - - -} // namespace axolotl - - -#endif /* AXOLOLT_BASE64_HH_ */ diff --git a/include/axolotl/cipher.hh b/include/axolotl/cipher.hh deleted file mode 100644 index 5a077aa..0000000 --- a/include/axolotl/cipher.hh +++ /dev/null @@ -1,128 +0,0 @@ -/* Copyright 2015 OpenMarket Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef AXOLOTL_CIPHER_HH_ -#define AXOLOTL_CIPHER_HH_ - -#include -#include - -namespace axolotl { - -class Cipher { -public: - virtual ~Cipher(); - - /** - * Returns the length of the message authentication code that will be - * appended to the output. - */ - virtual std::size_t mac_length() const = 0; - - /** - * Returns the length of cipher-text for a given length of plain-text. - */ - virtual std::size_t encrypt_ciphertext_length( - std::size_t plaintext_length - ) const = 0; - - /* - * Encrypts the plain-text into the output buffer and authenticates the - * contents of the output buffer covering both cipher-text and any other - * associated data in the output buffer. - * - * |---------------------------------------output_length-->| - * output |--ciphertext_length-->| |---mac_length-->| - * ciphertext - * - * Returns std::size_t(-1) if the length of the cipher-text or the output - * buffer is too small. Otherwise returns the length of the output buffer. - */ - virtual std::size_t encrypt( - std::uint8_t const * key, std::size_t key_length, - std::uint8_t const * plaintext, std::size_t plaintext_length, - std::uint8_t * ciphertext, std::size_t ciphertext_length, - std::uint8_t * output, std::size_t output_length - ) const = 0; - - /** - * Returns the maximum length of plain-text that a given length of - * cipher-text can contain. - */ - virtual std::size_t decrypt_max_plaintext_length( - std::size_t ciphertext_length - ) const = 0; - - /** - * Authenticates the input and decrypts the cipher-text into the plain-text - * buffer. - * - * |----------------------------------------input_length-->| - * input |--ciphertext_length-->| |---mac_length-->| - * ciphertext - * - * Returns std::size_t(-1) if the length of the plain-text buffer is too - * small or if the authentication check fails. Otherwise returns the length - * of the plain text. - */ - virtual std::size_t decrypt( - std::uint8_t const * key, std::size_t key_length, - std::uint8_t const * input, std::size_t input_length, - std::uint8_t const * ciphertext, std::size_t ciphertext_length, - std::uint8_t * plaintext, std::size_t max_plaintext_length - ) const = 0; -}; - - -class CipherAesSha256 : public Cipher { -public: - CipherAesSha256( - std::uint8_t const * kdf_info, std::size_t kdf_info_length - ); - - virtual std::size_t mac_length() const; - - virtual std::size_t encrypt_ciphertext_length( - std::size_t plaintext_length - ) const; - - virtual std::size_t encrypt( - std::uint8_t const * key, std::size_t key_length, - std::uint8_t const * plaintext, std::size_t plaintext_length, - std::uint8_t * ciphertext, std::size_t ciphertext_length, - std::uint8_t * output, std::size_t output_length - ) const; - - virtual std::size_t decrypt_max_plaintext_length( - std::size_t ciphertext_length - ) const; - - virtual std::size_t decrypt( - std::uint8_t const * key, std::size_t key_length, - std::uint8_t const * input, std::size_t input_length, - std::uint8_t const * ciphertext, std::size_t ciphertext_length, - std::uint8_t * plaintext, std::size_t max_plaintext_length - ) const; - -private: - std::uint8_t const * kdf_info; - std::size_t kdf_info_length; -}; - - -} // namespace - - -#endif /* AXOLOTL_CIPHER_HH_ */ diff --git a/include/axolotl/crypto.hh b/include/axolotl/crypto.hh deleted file mode 100644 index 7564e8f..0000000 --- a/include/axolotl/crypto.hh +++ /dev/null @@ -1,148 +0,0 @@ -/* Copyright 2015 OpenMarket Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef AXOLOTL_CRYPTO_HH_ -#define AXOLOTL_CRYPTO_HH_ - -#include -#include - -namespace axolotl { - - -struct Curve25519PublicKey { - static const int LENGTH = 32; - std::uint8_t public_key[32]; -}; - - -struct Curve25519KeyPair : public Curve25519PublicKey { - static const int LENGTH = 64; - std::uint8_t private_key[32]; -}; - - -/** Generate a curve25519 key pair from 32 random bytes. */ -void generate_key( - std::uint8_t const * random_32_bytes, - Curve25519KeyPair & key_pair -); - - -const std::size_t CURVE25519_SHARED_SECRET_LENGTH = 32; - - -/** Create a shared secret using our private key and their public key. - * The output buffer must be at least 32 bytes long. */ -void curve25519_shared_secret( - Curve25519KeyPair const & our_key, - Curve25519PublicKey const & their_key, - std::uint8_t * output -); - - -/** Signs the message using our private key. - * The output buffer must be at least 64 bytes long. */ -void curve25519_sign( - Curve25519KeyPair const & our_key, - std::uint8_t const * message, std::size_t message_length, - std::uint8_t * output -); - - -/** Verify thei message using their public key. - * The signature input buffer must be 64 bytes long. - * Returns true if the signature is valid. */ -bool curve25519_verify( - Curve25519PublicKey const & their_key, - std::uint8_t const * message, std::size_t message_length, - std::uint8_t const * signature -); - - -struct Aes256Key { - static const int LENGTH = 32; - std::uint8_t key[32]; -}; - - -struct Aes256Iv { - static const int LENGTH = 16; - std::uint8_t iv[16]; -}; - - -/** The length of output the aes_encrypt_cbc function will write */ -std::size_t aes_encrypt_cbc_length( - std::size_t input_length -); - - -/** Encrypts the input using AES256 in CBC mode with PKCS#7 padding. - * The output buffer must be big enough to hold the output including padding */ -void aes_encrypt_cbc( - Aes256Key const & key, - Aes256Iv const & iv, - std::uint8_t const * input, std::size_t input_length, - std::uint8_t * output -); - - -/** Decrypts the input using AES256 in CBC mode. The output buffer must be at - * least the same size as the input buffer. Returns the length of the plaintext - * without padding on success or std::size_t(-1) if the padding is invalid. - */ -std::size_t aes_decrypt_cbc( - Aes256Key const & key, - Aes256Iv const & iv, - std::uint8_t const * input, std::size_t input_length, - std::uint8_t * output -); - - -/** Computes SHA-256 of the input. The output buffer must be a least 32 - * bytes long. */ -void sha256( - std::uint8_t const * input, std::size_t input_length, - std::uint8_t * output -); - - -const std::size_t HMAC_SHA256_OUTPUT_LENGTH = 32; - - -/** HMAC: Keyed-Hashing for Message Authentication - * http://tools.ietf.org/html/rfc2104 - * Computes HMAC-SHA-256 of the input for the key. The output buffer must - * be at least 32 bytes long. */ -void hmac_sha256( - std::uint8_t const * key, std::size_t key_length, - std::uint8_t const * input, std::size_t input_length, - std::uint8_t * output -); - - -/** HMAC-based Key Derivation Function (HKDF) - * https://tools.ietf.org/html/rfc5869 - * Derives key material from the input bytes. */ -void hkdf_sha256( - std::uint8_t const * input, std::size_t input_length, - std::uint8_t const * info, std::size_t info_length, - std::uint8_t const * salt, std::size_t salt_length, - std::uint8_t * output, std::size_t output_length -); - -} // namespace axolotl - -#endif /* AXOLOTL_CRYPTO_HH_ */ diff --git a/include/axolotl/error.hh b/include/axolotl/error.hh deleted file mode 100644 index 781705e..0000000 --- a/include/axolotl/error.hh +++ /dev/null @@ -1,34 +0,0 @@ -/* Copyright 2015 OpenMarket Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef ERROR_HH_ -#define ERROR_HH_ - -namespace axolotl { - -enum struct ErrorCode { - SUCCESS = 0, /*!< There wasn't an error */ - NOT_ENOUGH_RANDOM = 1, /*!< Not enough entropy was supplied */ - OUTPUT_BUFFER_TOO_SMALL = 2, /*!< Supplied output buffer is too small */ - BAD_MESSAGE_VERSION = 3, /*!< The message version is unsupported */ - BAD_MESSAGE_FORMAT = 4, /*!< The message couldn't be decoded */ - BAD_MESSAGE_MAC = 5, /*!< The message couldn't be decrypted */ - BAD_MESSAGE_KEY_ID = 6, /*!< The message references an unknown key id */ - INVALID_BASE64 = 7, /*!< The input base64 was invalid */ - BAD_ACCOUNT_KEY = 8, /*!< The supplied account key is invalid */ -}; - -} // namespace axolotl - -#endif /* ERROR_HH_ */ diff --git a/include/axolotl/list.hh b/include/axolotl/list.hh deleted file mode 100644 index 604f00f..0000000 --- a/include/axolotl/list.hh +++ /dev/null @@ -1,119 +0,0 @@ -/* Copyright 2015 OpenMarket Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef AXOLOTL_LIST_HH_ -#define AXOLOTL_LIST_HH_ - -#include - -namespace axolotl { - -template -class List { -public: - List() : _end(_data) {} - - typedef T * iterator; - typedef T const * const_iterator; - - T * begin() { return _data; } - T * end() { return _end; } - T const * begin() const { return _data; } - T const * end() const { return _end; } - - /** - * Is the list empty? - */ - bool empty() const { return _end == _data; } - - /** - * The number of items in the list. - */ - std::size_t size() const { return _end - _data; } - - T & operator[](std::size_t index) { return _data[index]; } - - T const & operator[](std::size_t index) const { return _data[index]; } - - /** - * Erase the item from the list at the given position. - */ - void erase(T * pos) { - --_end; - while (pos != _end) { - *pos = *(pos + 1); - ++pos; - } - } - - /** - * Make space for an item in the list at a given position. - * If inserting the item makes the list longer than max_size then - * the end of the list is discarded. - * Returns the where the item is inserted. - */ - T * insert(T * pos) { - if (_end != _data + max_size) { - ++_end; - } else if (pos == _end) { - --pos; - } - T * tmp = pos; - while (tmp != _end - 1) { - *(tmp + 1) = *tmp; - ++tmp; - } - return pos; - } - - /** - * Make space for an item in the list at the start of the list - */ - T * insert() { return insert(begin()); } - - /** - * Insert an item into the list at a given position. - * If inserting the item makes the list longer than max_size then - * the end of the list is discarded. - * Returns the where the item is inserted. - */ - T * insert(T * pos, T const & value) { - pos = insert(pos); - *pos = value; - return pos; - } - - List & operator=(List const & other) { - if (this == &other) { - return *this; - } - T * this_pos = _data; - T * const other_pos = other._data; - while (other_pos != other._end) { - *this_pos = *other; - ++this_pos; - ++other_pos; - } - _end = this_pos; - return *this; - } - -private: - T * _end; - T _data[max_size]; -}; - -} // namespace axolotl - -#endif /* AXOLOTL_LIST_HH_ */ diff --git a/include/axolotl/memory.hh b/include/axolotl/memory.hh deleted file mode 100644 index 68b577d..0000000 --- a/include/axolotl/memory.hh +++ /dev/null @@ -1,38 +0,0 @@ -/* Copyright 2015 OpenMarket Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include - -namespace axolotl { - -/** Clear the memory held in the buffer */ -void unset( - void volatile * buffer, std::size_t buffer_length -); - -/** Clear the memory backing an object */ -template -void unset(T & value) { - unset(reinterpret_cast(&value), sizeof(T)); -} - -/** Check if two buffers are equal in constant time. */ -bool is_equal( - std::uint8_t const * buffer_a, - std::uint8_t const * buffer_b, - std::size_t length -); - -} // namespace axolotl diff --git a/include/axolotl/message.hh b/include/axolotl/message.hh deleted file mode 100644 index 4d7a1c7..0000000 --- a/include/axolotl/message.hh +++ /dev/null @@ -1,126 +0,0 @@ -/* Copyright 2015 OpenMarket Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include - - -namespace axolotl { - -/** - * The length of the buffer needed to hold a message. - */ -std::size_t encode_message_length( - std::uint32_t counter, - std::size_t ratchet_key_length, - std::size_t ciphertext_length, - std::size_t mac_length -); - - -struct MessageWriter { - std::uint8_t * ratchet_key; - std::uint8_t * ciphertext; -}; - - -struct MessageReader { - std::uint8_t version; - bool has_counter; - std::uint32_t counter; - std::uint8_t const * input; std::size_t input_length; - std::uint8_t const * ratchet_key; std::size_t ratchet_key_length; - std::uint8_t const * ciphertext; std::size_t ciphertext_length; -}; - - -/** - * Writes the message headers into the output buffer. - * Populates the writer struct with pointers into the output buffer. - */ -void encode_message( - MessageWriter & writer, - std::uint8_t version, - std::uint32_t counter, - std::size_t ratchet_key_length, - std::size_t ciphertext_length, - std::uint8_t * output -); - - -/** - * Reads the message headers from the input buffer. - * Populates the reader struct with pointers into the input buffer. - */ -void decode_message( - MessageReader & reader, - std::uint8_t const * input, std::size_t input_length, - std::size_t mac_length -); - - -struct PreKeyMessageWriter { - std::uint8_t * identity_key; - std::uint8_t * base_key; - std::uint8_t * message; -}; - - -struct PreKeyMessageReader { - std::uint8_t version; - bool has_one_time_key_id; - std::uint32_t one_time_key_id; - std::uint8_t const * identity_key; std::size_t identity_key_length; - std::uint8_t const * base_key; std::size_t base_key_length; - std::uint8_t const * message; std::size_t message_length; -}; - - -/** - * The length of the buffer needed to hold a message. - */ -std::size_t encode_one_time_key_message_length( - std::uint32_t one_time_key_id, - std::size_t identity_key_length, - std::size_t base_key_length, - std::size_t message_length -); - - -/** - * Writes the message headers into the output buffer. - * Populates the writer struct with pointers into the output buffer. - */ -void encode_one_time_key_message( - PreKeyMessageWriter & writer, - std::uint8_t version, - std::uint32_t one_time_key_id, - std::size_t identity_key_length, - std::size_t base_key_length, - std::size_t message_length, - std::uint8_t * output -); - - -/** - * Reads the message headers from the input buffer. - * Populates the reader struct with pointers into the input buffer. - */ -void decode_one_time_key_message( - PreKeyMessageReader & reader, - std::uint8_t const * input, std::size_t input_length -); - - -} // namespace axolotl diff --git a/include/axolotl/pickle.hh b/include/axolotl/pickle.hh deleted file mode 100644 index 8134971..0000000 --- a/include/axolotl/pickle.hh +++ /dev/null @@ -1,178 +0,0 @@ -/* Copyright 2015 OpenMarket Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef AXOLOTL_PICKLE_HH_ -#define AXOLOTL_PICKLE_HH_ - -#include "axolotl/list.hh" -#include "axolotl/crypto.hh" - -#include -#include - -namespace axolotl { - -static std::size_t pickle_length( - const std::uint32_t & value -) { - return 4; -} - - -static std::uint8_t * pickle( - std::uint8_t * pos, - std::uint32_t value -) { - pos += 4; - for (unsigned i = 4; i--;) { *(--pos) = value; value >>= 8; } - return pos + 4; -} - - -static std::uint8_t const * unpickle( - std::uint8_t const * pos, std::uint8_t const * end, - std::uint32_t & value -) { - value = 0; - if (end - pos < 4) return end; - for (unsigned i = 4; i--;) { value <<= 8; value |= *(pos++); } - return pos; -} - -static std::size_t pickle_length( - const bool & value -) { - return 1; -} - - -static std::uint8_t * pickle( - std::uint8_t * pos, - bool value -) { - *(pos++) = value ? 1 : 0; - return pos; -} - - -static std::uint8_t const * unpickle( - std::uint8_t const * pos, std::uint8_t const * end, - bool & value -) { - if (pos == end) return end; - value = *(pos++); - return pos; -} - - - -template -std::size_t pickle_length( - axolotl::List const & list -) { - std::size_t length = pickle_length(std::uint32_t(list.size())); - for (auto const & value : list) { - length += pickle_length(value); - } - return length; -} - - -template -std::uint8_t * pickle( - std::uint8_t * pos, - axolotl::List const & list -) { - pos = pickle(pos, std::uint32_t(list.size())); - for (auto const & value : list) { - pos = pickle(pos, value); - } - return pos; -} - - -template -std::uint8_t const * unpickle( - std::uint8_t const * pos, std::uint8_t const * end, - axolotl::List & list -) { - std::uint32_t size; - pos = unpickle(pos, end, size); - while (size--) { - T * value = list.insert(list.end()); - pos = unpickle(pos, end, *value); - } - return pos; -} - - -static std::uint8_t * pickle_bytes( - std::uint8_t * pos, - std::uint8_t const * bytes, std::size_t bytes_length -) { - std::memcpy(pos, bytes, bytes_length); - return pos + bytes_length; -} - - -static std::uint8_t const * unpickle_bytes( - std::uint8_t const * pos, std::uint8_t const * end, - std::uint8_t * bytes, std::size_t bytes_length -) { - if (end - pos < bytes_length) return end; - std::memcpy(bytes, pos, bytes_length); - return pos + bytes_length; -} - - -std::size_t pickle_length( - const Curve25519PublicKey & value -); - - -std::uint8_t * pickle( - std::uint8_t * pos, - const Curve25519PublicKey & value -); - - -std::uint8_t const * unpickle( - std::uint8_t const * pos, std::uint8_t const * end, - Curve25519PublicKey & value -); - - -std::size_t pickle_length( - const Curve25519KeyPair & value -); - - -std::uint8_t * pickle( - std::uint8_t * pos, - const Curve25519KeyPair & value -); - - -std::uint8_t const * unpickle( - std::uint8_t const * pos, std::uint8_t const * end, - Curve25519KeyPair & value -); - - -} // namespace axolotl - - - - -#endif /* AXOLOTL_PICKLE_HH */ diff --git a/include/axolotl/ratchet.hh b/include/axolotl/ratchet.hh deleted file mode 100644 index 0874cf0..0000000 --- a/include/axolotl/ratchet.hh +++ /dev/null @@ -1,177 +0,0 @@ -/* Copyright 2015 OpenMarket Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "axolotl/crypto.hh" -#include "axolotl/list.hh" -#include "axolotl/error.hh" - -namespace axolotl { - -class Cipher; - -typedef std::uint8_t SharedKey[32]; - - -struct ChainKey { - std::uint32_t index; - SharedKey key; -}; - - -struct MessageKey { - std::uint32_t index; - SharedKey key; -}; - - -struct SenderChain { - Curve25519KeyPair ratchet_key; - ChainKey chain_key; -}; - - -struct ReceiverChain { - Curve25519PublicKey ratchet_key; - ChainKey chain_key; -}; - - -struct SkippedMessageKey { - Curve25519PublicKey ratchet_key; - MessageKey message_key; -}; - - -static std::size_t const MAX_RECEIVER_CHAINS = 5; -static std::size_t const MAX_SKIPPED_MESSAGE_KEYS = 40; - - -struct KdfInfo { - std::uint8_t const * root_info; - std::size_t root_info_length; - std::uint8_t const * ratchet_info; - std::size_t ratchet_info_length; -}; - - -struct Ratchet { - - Ratchet( - KdfInfo const & kdf_info, - Cipher const & ratchet_cipher - ); - - /** A some strings identifying the application to feed into the KDF. */ - KdfInfo const & kdf_info; - - /** The AEAD cipher to use for encrypting messages. */ - Cipher const & ratchet_cipher; - - /** The last error that happened encrypting or decrypting a message. */ - ErrorCode last_error; - - /** The root key is used to generate chain keys from the ephemeral keys. - * A new root_key derived each time a chain key is derived. */ - SharedKey root_key; - - /** The sender chain is used to send messages. Each time a new ephemeral - * key is received from the remote server we generate a new sender chain - * with a new empheral key when we next send a message. */ - List sender_chain; - - /** The receiver chain is used to decrypt received messages. We store the - * last few chains so we can decrypt any out of order messages we haven't - * received yet. */ - List receiver_chains; - - /** List of message keys we've skipped over when advancing the receiver - * chain. */ - List skipped_message_keys; - - /** Initialise the session using a shared secret and the public part of the - * remote's first ratchet key */ - void initialise_as_bob( - std::uint8_t const * shared_secret, std::size_t shared_secret_length, - Curve25519PublicKey const & their_ratchet_key - ); - - /** Initialise the session using a shared secret and the public/private key - * pair for the first ratchet key */ - void initialise_as_alice( - std::uint8_t const * shared_secret, std::size_t shared_secret_length, - Curve25519KeyPair const & our_ratchet_key - ); - - /** The number of bytes of output the encrypt method will write for - * a given message length. */ - std::size_t encrypt_output_length( - std::size_t plaintext_length - ); - - /** The number of bytes of random data the encrypt method will need to - * encrypt a message. This will be 32 bytes if the session needs to - * generate a new ephemeral key, or will be 0 bytes otherwise.*/ - std::size_t encrypt_random_length(); - - /** Encrypt some plain-text. Returns the length of the encrypted message - * or std::size_t(-1) on failure. On failure last_error will be set with - * an error code. The last_error will be NOT_ENOUGH_RANDOM if the number - * of random bytes is too small. The last_error will be - * OUTPUT_BUFFER_TOO_SMALL if the output buffer is too small. */ - std::size_t encrypt( - std::uint8_t const * plaintext, std::size_t plaintext_length, - std::uint8_t const * random, std::size_t random_length, - std::uint8_t * output, std::size_t max_output_length - ); - - /** An upper bound on the number of bytes of plain-text the decrypt method - * will write for a given input message length. */ - std::size_t decrypt_max_plaintext_length( - std::uint8_t const * input, std::size_t input_length - ); - - /** Decrypt a message. Returns the length of the decrypted plain-text or - * std::size_t(-1) on failure. On failure last_error will be set with an - * error code. The last_error will be OUTPUT_BUFFER_TOO_SMALL if the - * plain-text buffer is too small. The last_error will be - * BAD_MESSAGE_VERSION if the message was encrypted with an unsupported - * version of the protocol. The last_error will be BAD_MESSAGE_FORMAT if - * the message headers could not be decoded. The last_error will be - * BAD_MESSAGE_MAC if the message could not be verified */ - std::size_t decrypt( - std::uint8_t const * input, std::size_t input_length, - std::uint8_t * plaintext, std::size_t max_plaintext_length - ); -}; - - -std::size_t pickle_length( - Ratchet const & value -); - - -std::uint8_t * pickle( - std::uint8_t * pos, - Ratchet const & value -); - - -std::uint8_t const * unpickle( - std::uint8_t const * pos, std::uint8_t const * end, - Ratchet & value -); - - -} // namespace axolotl diff --git a/include/axolotl/session.hh b/include/axolotl/session.hh deleted file mode 100644 index 17f1653..0000000 --- a/include/axolotl/session.hh +++ /dev/null @@ -1,114 +0,0 @@ -/* Copyright 2015 OpenMarket Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef AXOLOTL_SESSION_HH_ -#define AXOLOTL_SESSION_HH_ - -#include "axolotl/ratchet.hh" - -namespace axolotl { - -class Account; - -struct RemoteKey { - std::uint32_t id; - Curve25519PublicKey key; -}; - - -enum struct MessageType { - PRE_KEY = 0, - MESSAGE = 1, -}; - - -struct Session { - - Session(); - - Ratchet ratchet; - ErrorCode last_error; - - bool received_message; - - RemoteKey alice_identity_key; - Curve25519PublicKey alice_base_key; - std::uint32_t bob_one_time_key_id; - - - std::size_t new_outbound_session_random_length(); - - std::size_t new_outbound_session( - Account const & local_account, - Curve25519PublicKey const & identity_key, - RemoteKey const & one_time_key, - std::uint8_t const * random, std::size_t random_length - ); - - std::size_t new_inbound_session( - Account & local_account, - std::uint8_t const * one_time_key_message, std::size_t message_length - ); - - bool matches_inbound_session( - std::uint8_t const * one_time_key_message, std::size_t message_length - ); - - MessageType encrypt_message_type(); - - std::size_t encrypt_message_length( - std::size_t plaintext_length - ); - - std::size_t encrypt_random_length(); - - std::size_t encrypt( - std::uint8_t const * plaintext, std::size_t plaintext_length, - std::uint8_t const * random, std::size_t random_length, - std::uint8_t * message, std::size_t message_length - ); - - std::size_t decrypt_max_plaintext_length( - MessageType message_type, - std::uint8_t const * message, std::size_t message_length - ); - - std::size_t decrypt( - MessageType message_type, - std::uint8_t const * message, std::size_t message_length, - std::uint8_t * plaintext, std::size_t max_plaintext_length - ); -}; - - -std::size_t pickle_length( - Session const & value -); - - -std::uint8_t * pickle( - std::uint8_t * pos, - Session const & value -); - - -std::uint8_t const * unpickle( - std::uint8_t const * pos, std::uint8_t const * end, - Session & value -); - - -} // namespace axolotl - -#endif /* AXOLOTL_SESSION_HH_ */ diff --git a/include/olm/account.hh b/include/olm/account.hh new file mode 100644 index 0000000..8094a25 --- /dev/null +++ b/include/olm/account.hh @@ -0,0 +1,84 @@ +/* Copyright 2015 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef OLM_ACCOUNT_HH_ +#define OLM_ACCOUNT_HH_ + +#include "olm/list.hh" +#include "olm/crypto.hh" +#include "olm/error.hh" + +#include + +namespace olm { + + +struct LocalKey { + std::uint32_t id; + Curve25519KeyPair key; +}; + + +struct SignedKey : LocalKey { + std::uint8_t signature[64]; +}; + + +static std::size_t const MAX_ONE_TIME_KEYS = 100; + +struct Account { + LocalKey identity_key; + LocalKey last_resort_one_time_key; + List one_time_keys; + ErrorCode last_error; + + /** Number of random bytes needed to create a new account */ + std::size_t new_account_random_length(); + + /** Create a new account. Returns NOT_ENOUGH_RANDOM if the number of random + * bytes is too small. */ + std::size_t new_account( + uint8_t const * random, std::size_t random_length + ); + + LocalKey const * lookup_key( + std::uint32_t id + ); + + std::size_t remove_key( + std::uint32_t id + ); +}; + + +std::size_t pickle_length( + Account const & value +); + + +std::uint8_t * pickle( + std::uint8_t * pos, + Account const & value +); + + +std::uint8_t const * unpickle( + std::uint8_t const * pos, std::uint8_t const * end, + Account & value +); + + +} // namespace olm + +#endif /* OLM_ACCOUNT_HH_ */ diff --git a/include/olm/base64.hh b/include/olm/base64.hh new file mode 100644 index 0000000..0a7435b --- /dev/null +++ b/include/olm/base64.hh @@ -0,0 +1,49 @@ +/* Copyright 2015 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef AXOLOLT_BASE64_HH_ +#define AXOLOLT_BASE64_HH_ + +#include +#include + +namespace olm { + + +std::size_t encode_base64_length( + std::size_t input_length +); + + +void encode_base64( + std::uint8_t const * input, std::size_t input_length, + std::uint8_t * output +); + + +std::size_t decode_base64_length( + std::size_t input_length +); + + +void decode_base64( + std::uint8_t const * input, std::size_t input_length, + std::uint8_t * output +); + + +} // namespace olm + + +#endif /* AXOLOLT_BASE64_HH_ */ diff --git a/include/olm/cipher.hh b/include/olm/cipher.hh new file mode 100644 index 0000000..f71b3af --- /dev/null +++ b/include/olm/cipher.hh @@ -0,0 +1,128 @@ +/* Copyright 2015 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OLM_CIPHER_HH_ +#define OLM_CIPHER_HH_ + +#include +#include + +namespace olm { + +class Cipher { +public: + virtual ~Cipher(); + + /** + * Returns the length of the message authentication code that will be + * appended to the output. + */ + virtual std::size_t mac_length() const = 0; + + /** + * Returns the length of cipher-text for a given length of plain-text. + */ + virtual std::size_t encrypt_ciphertext_length( + std::size_t plaintext_length + ) const = 0; + + /* + * Encrypts the plain-text into the output buffer and authenticates the + * contents of the output buffer covering both cipher-text and any other + * associated data in the output buffer. + * + * |---------------------------------------output_length-->| + * output |--ciphertext_length-->| |---mac_length-->| + * ciphertext + * + * Returns std::size_t(-1) if the length of the cipher-text or the output + * buffer is too small. Otherwise returns the length of the output buffer. + */ + virtual std::size_t encrypt( + std::uint8_t const * key, std::size_t key_length, + std::uint8_t const * plaintext, std::size_t plaintext_length, + std::uint8_t * ciphertext, std::size_t ciphertext_length, + std::uint8_t * output, std::size_t output_length + ) const = 0; + + /** + * Returns the maximum length of plain-text that a given length of + * cipher-text can contain. + */ + virtual std::size_t decrypt_max_plaintext_length( + std::size_t ciphertext_length + ) const = 0; + + /** + * Authenticates the input and decrypts the cipher-text into the plain-text + * buffer. + * + * |----------------------------------------input_length-->| + * input |--ciphertext_length-->| |---mac_length-->| + * ciphertext + * + * Returns std::size_t(-1) if the length of the plain-text buffer is too + * small or if the authentication check fails. Otherwise returns the length + * of the plain text. + */ + virtual std::size_t decrypt( + std::uint8_t const * key, std::size_t key_length, + std::uint8_t const * input, std::size_t input_length, + std::uint8_t const * ciphertext, std::size_t ciphertext_length, + std::uint8_t * plaintext, std::size_t max_plaintext_length + ) const = 0; +}; + + +class CipherAesSha256 : public Cipher { +public: + CipherAesSha256( + std::uint8_t const * kdf_info, std::size_t kdf_info_length + ); + + virtual std::size_t mac_length() const; + + virtual std::size_t encrypt_ciphertext_length( + std::size_t plaintext_length + ) const; + + virtual std::size_t encrypt( + std::uint8_t const * key, std::size_t key_length, + std::uint8_t const * plaintext, std::size_t plaintext_length, + std::uint8_t * ciphertext, std::size_t ciphertext_length, + std::uint8_t * output, std::size_t output_length + ) const; + + virtual std::size_t decrypt_max_plaintext_length( + std::size_t ciphertext_length + ) const; + + virtual std::size_t decrypt( + std::uint8_t const * key, std::size_t key_length, + std::uint8_t const * input, std::size_t input_length, + std::uint8_t const * ciphertext, std::size_t ciphertext_length, + std::uint8_t * plaintext, std::size_t max_plaintext_length + ) const; + +private: + std::uint8_t const * kdf_info; + std::size_t kdf_info_length; +}; + + +} // namespace + + +#endif /* OLM_CIPHER_HH_ */ diff --git a/include/olm/crypto.hh b/include/olm/crypto.hh new file mode 100644 index 0000000..b299e12 --- /dev/null +++ b/include/olm/crypto.hh @@ -0,0 +1,148 @@ +/* Copyright 2015 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef OLM_CRYPTO_HH_ +#define OLM_CRYPTO_HH_ + +#include +#include + +namespace olm { + + +struct Curve25519PublicKey { + static const int LENGTH = 32; + std::uint8_t public_key[32]; +}; + + +struct Curve25519KeyPair : public Curve25519PublicKey { + static const int LENGTH = 64; + std::uint8_t private_key[32]; +}; + + +/** Generate a curve25519 key pair from 32 random bytes. */ +void generate_key( + std::uint8_t const * random_32_bytes, + Curve25519KeyPair & key_pair +); + + +const std::size_t CURVE25519_SHARED_SECRET_LENGTH = 32; + + +/** Create a shared secret using our private key and their public key. + * The output buffer must be at least 32 bytes long. */ +void curve25519_shared_secret( + Curve25519KeyPair const & our_key, + Curve25519PublicKey const & their_key, + std::uint8_t * output +); + + +/** Signs the message using our private key. + * The output buffer must be at least 64 bytes long. */ +void curve25519_sign( + Curve25519KeyPair const & our_key, + std::uint8_t const * message, std::size_t message_length, + std::uint8_t * output +); + + +/** Verify thei message using their public key. + * The signature input buffer must be 64 bytes long. + * Returns true if the signature is valid. */ +bool curve25519_verify( + Curve25519PublicKey const & their_key, + std::uint8_t const * message, std::size_t message_length, + std::uint8_t const * signature +); + + +struct Aes256Key { + static const int LENGTH = 32; + std::uint8_t key[32]; +}; + + +struct Aes256Iv { + static const int LENGTH = 16; + std::uint8_t iv[16]; +}; + + +/** The length of output the aes_encrypt_cbc function will write */ +std::size_t aes_encrypt_cbc_length( + std::size_t input_length +); + + +/** Encrypts the input using AES256 in CBC mode with PKCS#7 padding. + * The output buffer must be big enough to hold the output including padding */ +void aes_encrypt_cbc( + Aes256Key const & key, + Aes256Iv const & iv, + std::uint8_t const * input, std::size_t input_length, + std::uint8_t * output +); + + +/** Decrypts the input using AES256 in CBC mode. The output buffer must be at + * least the same size as the input buffer. Returns the length of the plaintext + * without padding on success or std::size_t(-1) if the padding is invalid. + */ +std::size_t aes_decrypt_cbc( + Aes256Key const & key, + Aes256Iv const & iv, + std::uint8_t const * input, std::size_t input_length, + std::uint8_t * output +); + + +/** Computes SHA-256 of the input. The output buffer must be a least 32 + * bytes long. */ +void sha256( + std::uint8_t const * input, std::size_t input_length, + std::uint8_t * output +); + + +const std::size_t HMAC_SHA256_OUTPUT_LENGTH = 32; + + +/** HMAC: Keyed-Hashing for Message Authentication + * http://tools.ietf.org/html/rfc2104 + * Computes HMAC-SHA-256 of the input for the key. The output buffer must + * be at least 32 bytes long. */ +void hmac_sha256( + std::uint8_t const * key, std::size_t key_length, + std::uint8_t const * input, std::size_t input_length, + std::uint8_t * output +); + + +/** HMAC-based Key Derivation Function (HKDF) + * https://tools.ietf.org/html/rfc5869 + * Derives key material from the input bytes. */ +void hkdf_sha256( + std::uint8_t const * input, std::size_t input_length, + std::uint8_t const * info, std::size_t info_length, + std::uint8_t const * salt, std::size_t salt_length, + std::uint8_t * output, std::size_t output_length +); + +} // namespace olm + +#endif /* OLM_CRYPTO_HH_ */ diff --git a/include/olm/error.hh b/include/olm/error.hh new file mode 100644 index 0000000..960de72 --- /dev/null +++ b/include/olm/error.hh @@ -0,0 +1,34 @@ +/* Copyright 2015 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef ERROR_HH_ +#define ERROR_HH_ + +namespace olm { + +enum struct ErrorCode { + SUCCESS = 0, /*!< There wasn't an error */ + NOT_ENOUGH_RANDOM = 1, /*!< Not enough entropy was supplied */ + OUTPUT_BUFFER_TOO_SMALL = 2, /*!< Supplied output buffer is too small */ + BAD_MESSAGE_VERSION = 3, /*!< The message version is unsupported */ + BAD_MESSAGE_FORMAT = 4, /*!< The message couldn't be decoded */ + BAD_MESSAGE_MAC = 5, /*!< The message couldn't be decrypted */ + BAD_MESSAGE_KEY_ID = 6, /*!< The message references an unknown key id */ + INVALID_BASE64 = 7, /*!< The input base64 was invalid */ + BAD_ACCOUNT_KEY = 8, /*!< The supplied account key is invalid */ +}; + +} // namespace olm + +#endif /* ERROR_HH_ */ diff --git a/include/olm/list.hh b/include/olm/list.hh new file mode 100644 index 0000000..e4bf951 --- /dev/null +++ b/include/olm/list.hh @@ -0,0 +1,119 @@ +/* Copyright 2015 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef OLM_LIST_HH_ +#define OLM_LIST_HH_ + +#include + +namespace olm { + +template +class List { +public: + List() : _end(_data) {} + + typedef T * iterator; + typedef T const * const_iterator; + + T * begin() { return _data; } + T * end() { return _end; } + T const * begin() const { return _data; } + T const * end() const { return _end; } + + /** + * Is the list empty? + */ + bool empty() const { return _end == _data; } + + /** + * The number of items in the list. + */ + std::size_t size() const { return _end - _data; } + + T & operator[](std::size_t index) { return _data[index]; } + + T const & operator[](std::size_t index) const { return _data[index]; } + + /** + * Erase the item from the list at the given position. + */ + void erase(T * pos) { + --_end; + while (pos != _end) { + *pos = *(pos + 1); + ++pos; + } + } + + /** + * Make space for an item in the list at a given position. + * If inserting the item makes the list longer than max_size then + * the end of the list is discarded. + * Returns the where the item is inserted. + */ + T * insert(T * pos) { + if (_end != _data + max_size) { + ++_end; + } else if (pos == _end) { + --pos; + } + T * tmp = pos; + while (tmp != _end - 1) { + *(tmp + 1) = *tmp; + ++tmp; + } + return pos; + } + + /** + * Make space for an item in the list at the start of the list + */ + T * insert() { return insert(begin()); } + + /** + * Insert an item into the list at a given position. + * If inserting the item makes the list longer than max_size then + * the end of the list is discarded. + * Returns the where the item is inserted. + */ + T * insert(T * pos, T const & value) { + pos = insert(pos); + *pos = value; + return pos; + } + + List & operator=(List const & other) { + if (this == &other) { + return *this; + } + T * this_pos = _data; + T * const other_pos = other._data; + while (other_pos != other._end) { + *this_pos = *other; + ++this_pos; + ++other_pos; + } + _end = this_pos; + return *this; + } + +private: + T * _end; + T _data[max_size]; +}; + +} // namespace olm + +#endif /* OLM_LIST_HH_ */ diff --git a/include/olm/memory.hh b/include/olm/memory.hh new file mode 100644 index 0000000..b19c74b --- /dev/null +++ b/include/olm/memory.hh @@ -0,0 +1,38 @@ +/* Copyright 2015 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +namespace olm { + +/** Clear the memory held in the buffer */ +void unset( + void volatile * buffer, std::size_t buffer_length +); + +/** Clear the memory backing an object */ +template +void unset(T & value) { + unset(reinterpret_cast(&value), sizeof(T)); +} + +/** Check if two buffers are equal in constant time. */ +bool is_equal( + std::uint8_t const * buffer_a, + std::uint8_t const * buffer_b, + std::size_t length +); + +} // namespace olm diff --git a/include/olm/message.hh b/include/olm/message.hh new file mode 100644 index 0000000..fefdd20 --- /dev/null +++ b/include/olm/message.hh @@ -0,0 +1,126 @@ +/* Copyright 2015 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + + +namespace olm { + +/** + * The length of the buffer needed to hold a message. + */ +std::size_t encode_message_length( + std::uint32_t counter, + std::size_t ratchet_key_length, + std::size_t ciphertext_length, + std::size_t mac_length +); + + +struct MessageWriter { + std::uint8_t * ratchet_key; + std::uint8_t * ciphertext; +}; + + +struct MessageReader { + std::uint8_t version; + bool has_counter; + std::uint32_t counter; + std::uint8_t const * input; std::size_t input_length; + std::uint8_t const * ratchet_key; std::size_t ratchet_key_length; + std::uint8_t const * ciphertext; std::size_t ciphertext_length; +}; + + +/** + * Writes the message headers into the output buffer. + * Populates the writer struct with pointers into the output buffer. + */ +void encode_message( + MessageWriter & writer, + std::uint8_t version, + std::uint32_t counter, + std::size_t ratchet_key_length, + std::size_t ciphertext_length, + std::uint8_t * output +); + + +/** + * Reads the message headers from the input buffer. + * Populates the reader struct with pointers into the input buffer. + */ +void decode_message( + MessageReader & reader, + std::uint8_t const * input, std::size_t input_length, + std::size_t mac_length +); + + +struct PreKeyMessageWriter { + std::uint8_t * identity_key; + std::uint8_t * base_key; + std::uint8_t * message; +}; + + +struct PreKeyMessageReader { + std::uint8_t version; + bool has_one_time_key_id; + std::uint32_t one_time_key_id; + std::uint8_t const * identity_key; std::size_t identity_key_length; + std::uint8_t const * base_key; std::size_t base_key_length; + std::uint8_t const * message; std::size_t message_length; +}; + + +/** + * The length of the buffer needed to hold a message. + */ +std::size_t encode_one_time_key_message_length( + std::uint32_t one_time_key_id, + std::size_t identity_key_length, + std::size_t base_key_length, + std::size_t message_length +); + + +/** + * Writes the message headers into the output buffer. + * Populates the writer struct with pointers into the output buffer. + */ +void encode_one_time_key_message( + PreKeyMessageWriter & writer, + std::uint8_t version, + std::uint32_t one_time_key_id, + std::size_t identity_key_length, + std::size_t base_key_length, + std::size_t message_length, + std::uint8_t * output +); + + +/** + * Reads the message headers from the input buffer. + * Populates the reader struct with pointers into the input buffer. + */ +void decode_one_time_key_message( + PreKeyMessageReader & reader, + std::uint8_t const * input, std::size_t input_length +); + + +} // namespace olm diff --git a/include/olm/olm.hh b/include/olm/olm.hh new file mode 100644 index 0000000..fca35c4 --- /dev/null +++ b/include/olm/olm.hh @@ -0,0 +1,295 @@ +/* Copyright 2015 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef OLM_HH_ +#define OLM_HH_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static const size_t OLM_MESSAGE_TYPE_PRE_KEY = 0; +static const size_t OLM_MESSAGE_TYPE_MESSAGE = 1; + +struct OlmAccount; +struct OlmSession; + +/** The size of an account object in bytes */ +size_t olm_account_size(); + +/** The size of a session object in bytes */ +size_t olm_session_size(); + +/** Initialise an account object using the supplied memory + * The supplied memory must be at least olm_account_size() bytes */ +OlmAccount * olm_account( + void * memory +); + +/** Initialise a session object using the supplied memory + * The supplied memory must be at least olm_session_size() bytes */ +OlmSession * olm_session( + void * memory +); + +/** The value that olm will return from a function if there was an error */ +size_t olm_error(); + +/** A null terminated string describing the most recent error to happen to an + * account */ +const char * olm_account_last_error( + OlmSession * account +); + +/** A null terminated string describing the most recent error to happen to a + * session */ +const char * olm_session_last_error( + OlmSession * session +); + +/** Returns the number of bytes needed to store an account */ +size_t olm_pickle_account_length( + OlmAccount * account +); + +/** Returns the number of bytes needed to store a session */ +size_t olm_pickle_session_length( + OlmSession * session +); + +/** Stores an account as a base64 string. Encrypts the account using the + * supplied key. Returns the length of the pickled account on success. + * Returns olm_error() on failure. If the pickle output buffer + * is smaller than olm_pickle_account_length() then + * olm_account_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */ +size_t olm_pickle_account( + OlmAccount * account, + void const * key, size_t key_length, + void * pickled, size_t pickled_length +); + +/** Stores a session as a base64 string. Encrypts the session using the + * supplied key. Returns the length of the pickled session on success. + * Returns olm_error() on failure. If the pickle output buffer + * is smaller than olm_pickle_session_length() then + * olm_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */ +size_t olm_pickle_session( + OlmSession * session, + void const * key, size_t key_length, + void * pickled, size_t pickled_length +); + +/** Loads an account from a pickled base64 string. Decrypts the account using + * the supplied key. Returns olm_error() on failure. If the key doesn't + * match the one used to encrypt the account then olm_account_last_error() + * will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then + * olm_account_last_error() will be "INVALID_BASE64". The input pickled + * buffer is destroyed */ +size_t olm_unpickle_account( + OlmAccount * account, + void const * key, size_t key_length, + void * pickled, size_t pickled_length +); + +/** Loads a session from a pickled base64 string. Decrypts the session using + * the supplied key. Returns olm_error() on failure. If the key doesn't + * match the one used to encrypt the account then olm_session_last_error() + * will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then + * olm_session_last_error() will be "INVALID_BASE64". The input pickled + * buffer is destroyed */ +size_t olm_unpickle_session( + OlmSession * session, + void const * key, size_t key_length, + void * pickled, size_t pickled_length +); + +/** The number of random bytes needed to create an account.*/ +size_t olm_create_account_random_length( + OlmAccount * account +); + +/** Creates a new account. Returns olm_error() on failure. If weren't + * enough random bytes then olm_account_last_error() will be + * "NOT_ENOUGH_RANDOM" */ +size_t olm_create_account( + OlmAccount * account, + void const * random, size_t random_length +); + +/** The size of the output buffer needed to hold the identity keys */ +size_t olm_account_identity_keys_length( + OlmAccount * account +); + +/** Writes the public parts of the identity keys for the account into the + * identity_keys output buffer. The output is formatted as though it was + * created with sprintf(output, "[[%10d,\"%43s\"]\n]", key_id, key_base64). + * The output can either be parsed as fixed width using the above format or by + * a JSON parser. Returns olm_error() on failure. If the identity_keys + * buffer was too small then olm_account_last_error() will be + * "OUTPUT_BUFFER_TOO_SMALL". */ +size_t olm_account_identity_keys( + OlmAccount * account, + void * identity_keys, size_t identity_key_length +); + +/** The size of the output buffer needed to hold the one time keys */ +size_t olm_account_one_time_keys_length( + OlmAccount * account +); + +/** Writes the public parts of the one time keys for the account into the + * one_time_keys output buffer. The first key will be formatted as though it was + * created with sprintf(output, "[[%10d,\"%43s\"]\n", key_id, key_base64). + * subsequent keys are formatted with ",[%10d,\"%43s\"]\n". The final byte of + * output will be "]". The output can either be parsed as fixed width using + * the above format or by a JSON parser. Returns olm_error() on failure. + * If the one_time_keys buffer was too small then olm_account_last_error() + * will be "OUTPUT_BUFFER_TOO_SMALL". */ +size_t olm_account_one_time_keys( + OlmAccount * account, + void * one_time_keys, size_t one_time_keys_length +); + +/* TODO: Add methods for marking keys as used, generating new keys, and + * tracking which keys have been uploaded to the central servers */ + +/** The number of random bytes needed to create an outbound session */ +size_t olm_create_outbound_session_random_length( + OlmSession * session +); + +/** Creates a new out-bound session for sending messages to a given identity_key + * and one_time_key. Returns olm_error() on failure. If the keys couldn't be + * decoded as base64 then olm_session_last_error() will be "INVALID_BASE64" + * If there weren't enough random bytes then olm_session_last_error() will + * be "NOT_ENOUGH_RANDOM". */ +size_t olm_create_outbound_session( + OlmSession * session, + OlmAccount * account, + void const * their_identity_key, size_t their_identity_key_length, + unsigned their_one_time_key_id, + void const * their_one_time_key, size_t their_one_time_key_length, + void const * random, size_t random_length +); + +/** Create a new in-bound session for sending/receiving messages from an + * incoming PRE_KEY message. Returns olm_error() on failure. If the base64 + * couldn't be decoded then olm_session_last_error will be "INVALID_BASE64". + * If the message was for an unsupported protocol version then + * olm_session_last_error() will be "BAD_MESSAGE_VERSION". If the message + * couldn't be decoded then then olm_session_last_error() will be + * "BAD_MESSAGE_FORMAT". If the message refers to an unknown one time + * key then olm_session_last_error() will be "BAD_MESSAGE_KEY_ID". */ +size_t olm_create_inbound_session( + OlmSession * session, + OlmAccount * account, + void * one_time_key_message, size_t message_length +); + +/** Checks if the PRE_KEY message is for this in-bound session. This can happen + * if multiple messages are sent to this account before this account sends a + * message in reply. Returns olm_error() on failure. If the base64 + * couldn't be decoded then olm_session_last_error will be "INVALID_BASE64". + * If the message was for an unsupported protocol version then + * olm_session_last_error() will be "BAD_MESSAGE_VERSION". If the message + * couldn't be decoded then then olm_session_last_error() will be + * "BAD_MESSAGE_FORMAT". */ +size_t olm_matches_inbound_session( + OlmSession * session, + void * one_time_key_message, size_t message_length +); + +/** Removes the one time keys that the session used from the account. Returns + * olm_error() on failure. If the account doesn't have any matching one time + * keys then olm_account_last_error() will be "BAD_MESSAGE_KEY_ID". */ +size_t olm_remove_one_time_keys( + OlmAccount * account, + OlmSession * session +); + +/** The type of the next message that olm_encrypt() will return. Returns + * OLM_MESSAGE_TYPE_PRE_KEY if the message will be a PRE_KEY message. + * Returns OLM_MESSAGE_TYPE_MESSAGE if the message will be a normal message. + * Returns olm_error on failure. */ +size_t olm_encrypt_message_type( + OlmSession * session +); + +/** The number of random bytes needed to encrypt the next message. */ +size_t olm_encrypt_random_length( + OlmSession * session +); + +/** The size of the next message in bytes for the given number of plain-text + * bytes. */ +size_t olm_encrypt_message_length( + OlmSession * session, + size_t plaintext_length +); + +/** Encrypts a message using the session. Returns the length of the message in + * bytes on success. Writes the message as base64 into the message buffer. + * Returns olm_error() on failure. If the message buffer is too small then + * olm_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". If there + * weren't enough random bytes then olm_session_last_error() will be + * "NOT_ENOUGH_RANDOM". */ +size_t olm_encrypt( + OlmSession * session, + void const * plaintext, size_t plaintext_length, + void const * random, size_t random_length, + void * message, size_t message_length +); + +/** The maximum number of bytes of plain-text a given message could decode to. + * The actual size could be different due to padding. The input message buffer + * is destroyed. Returns olm_error() on failure. If the message base64 + * couldn't be decoded then olm_session_last_error() will be + * "INVALID_BASE64". If the message is for an unsupported version of the + * protocol then olm_session_last_error() will be "BAD_MESSAGE_VERSION". + * If the message couldn't be decoded then olm_session_last_error() will be + * "BAD_MESSAGE_FORMAT". */ +size_t olm_decrypt_max_plaintext_length( + OlmSession * session, + size_t message_type, + void * message, size_t message_length +); + +/** Decrypts a message using the session. The input message buffer is destroyed. + * Returns the length of the plain-text on success. Returns olm_error() on + * failure. If the plain-text buffer is smaller than + * olm_decrypt_max_plaintext_length() then olm_session_last_error() + * will be "OUTPUT_BUFFER_TOO_SMALL". If the base64 couldn't be decoded then + * olm_session_last_error() will be "INVALID_BASE64". If the message is for + * an unsupported version of the protocol then olm_session_last_error() will + * be "BAD_MESSAGE_VERSION". If the message couldn't be decoded then + * olm_session_last_error() will be BAD_MESSAGE_FORMAT". + * If the MAC on the message was invalid then olm_session_last_error() will + * be "BAD_MESSAGE_MAC". */ +size_t olm_decrypt( + OlmSession * session, + size_t message_type, + void * message, size_t message_length, + void * plaintext, size_t max_plaintext_length +); + + + +#ifdef __cplusplus +} +#endif + +#endif /* OLM_HH_ */ diff --git a/include/olm/pickle.hh b/include/olm/pickle.hh new file mode 100644 index 0000000..1676e23 --- /dev/null +++ b/include/olm/pickle.hh @@ -0,0 +1,178 @@ +/* Copyright 2015 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef OLM_PICKLE_HH_ +#define OLM_PICKLE_HH_ + +#include "olm/list.hh" +#include "olm/crypto.hh" + +#include +#include + +namespace olm { + +static std::size_t pickle_length( + const std::uint32_t & value +) { + return 4; +} + + +static std::uint8_t * pickle( + std::uint8_t * pos, + std::uint32_t value +) { + pos += 4; + for (unsigned i = 4; i--;) { *(--pos) = value; value >>= 8; } + return pos + 4; +} + + +static std::uint8_t const * unpickle( + std::uint8_t const * pos, std::uint8_t const * end, + std::uint32_t & value +) { + value = 0; + if (end - pos < 4) return end; + for (unsigned i = 4; i--;) { value <<= 8; value |= *(pos++); } + return pos; +} + +static std::size_t pickle_length( + const bool & value +) { + return 1; +} + + +static std::uint8_t * pickle( + std::uint8_t * pos, + bool value +) { + *(pos++) = value ? 1 : 0; + return pos; +} + + +static std::uint8_t const * unpickle( + std::uint8_t const * pos, std::uint8_t const * end, + bool & value +) { + if (pos == end) return end; + value = *(pos++); + return pos; +} + + + +template +std::size_t pickle_length( + olm::List const & list +) { + std::size_t length = pickle_length(std::uint32_t(list.size())); + for (auto const & value : list) { + length += pickle_length(value); + } + return length; +} + + +template +std::uint8_t * pickle( + std::uint8_t * pos, + olm::List const & list +) { + pos = pickle(pos, std::uint32_t(list.size())); + for (auto const & value : list) { + pos = pickle(pos, value); + } + return pos; +} + + +template +std::uint8_t const * unpickle( + std::uint8_t const * pos, std::uint8_t const * end, + olm::List & list +) { + std::uint32_t size; + pos = unpickle(pos, end, size); + while (size--) { + T * value = list.insert(list.end()); + pos = unpickle(pos, end, *value); + } + return pos; +} + + +static std::uint8_t * pickle_bytes( + std::uint8_t * pos, + std::uint8_t const * bytes, std::size_t bytes_length +) { + std::memcpy(pos, bytes, bytes_length); + return pos + bytes_length; +} + + +static std::uint8_t const * unpickle_bytes( + std::uint8_t const * pos, std::uint8_t const * end, + std::uint8_t * bytes, std::size_t bytes_length +) { + if (end - pos < bytes_length) return end; + std::memcpy(bytes, pos, bytes_length); + return pos + bytes_length; +} + + +std::size_t pickle_length( + const Curve25519PublicKey & value +); + + +std::uint8_t * pickle( + std::uint8_t * pos, + const Curve25519PublicKey & value +); + + +std::uint8_t const * unpickle( + std::uint8_t const * pos, std::uint8_t const * end, + Curve25519PublicKey & value +); + + +std::size_t pickle_length( + const Curve25519KeyPair & value +); + + +std::uint8_t * pickle( + std::uint8_t * pos, + const Curve25519KeyPair & value +); + + +std::uint8_t const * unpickle( + std::uint8_t const * pos, std::uint8_t const * end, + Curve25519KeyPair & value +); + + +} // namespace olm + + + + +#endif /* OLM_PICKLE_HH */ diff --git a/include/olm/ratchet.hh b/include/olm/ratchet.hh new file mode 100644 index 0000000..7274255 --- /dev/null +++ b/include/olm/ratchet.hh @@ -0,0 +1,177 @@ +/* Copyright 2015 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "olm/crypto.hh" +#include "olm/list.hh" +#include "olm/error.hh" + +namespace olm { + +class Cipher; + +typedef std::uint8_t SharedKey[32]; + + +struct ChainKey { + std::uint32_t index; + SharedKey key; +}; + + +struct MessageKey { + std::uint32_t index; + SharedKey key; +}; + + +struct SenderChain { + Curve25519KeyPair ratchet_key; + ChainKey chain_key; +}; + + +struct ReceiverChain { + Curve25519PublicKey ratchet_key; + ChainKey chain_key; +}; + + +struct SkippedMessageKey { + Curve25519PublicKey ratchet_key; + MessageKey message_key; +}; + + +static std::size_t const MAX_RECEIVER_CHAINS = 5; +static std::size_t const MAX_SKIPPED_MESSAGE_KEYS = 40; + + +struct KdfInfo { + std::uint8_t const * root_info; + std::size_t root_info_length; + std::uint8_t const * ratchet_info; + std::size_t ratchet_info_length; +}; + + +struct Ratchet { + + Ratchet( + KdfInfo const & kdf_info, + Cipher const & ratchet_cipher + ); + + /** A some strings identifying the application to feed into the KDF. */ + KdfInfo const & kdf_info; + + /** The AEAD cipher to use for encrypting messages. */ + Cipher const & ratchet_cipher; + + /** The last error that happened encrypting or decrypting a message. */ + ErrorCode last_error; + + /** The root key is used to generate chain keys from the ephemeral keys. + * A new root_key derived each time a chain key is derived. */ + SharedKey root_key; + + /** The sender chain is used to send messages. Each time a new ephemeral + * key is received from the remote server we generate a new sender chain + * with a new empheral key when we next send a message. */ + List sender_chain; + + /** The receiver chain is used to decrypt received messages. We store the + * last few chains so we can decrypt any out of order messages we haven't + * received yet. */ + List receiver_chains; + + /** List of message keys we've skipped over when advancing the receiver + * chain. */ + List skipped_message_keys; + + /** Initialise the session using a shared secret and the public part of the + * remote's first ratchet key */ + void initialise_as_bob( + std::uint8_t const * shared_secret, std::size_t shared_secret_length, + Curve25519PublicKey const & their_ratchet_key + ); + + /** Initialise the session using a shared secret and the public/private key + * pair for the first ratchet key */ + void initialise_as_alice( + std::uint8_t const * shared_secret, std::size_t shared_secret_length, + Curve25519KeyPair const & our_ratchet_key + ); + + /** The number of bytes of output the encrypt method will write for + * a given message length. */ + std::size_t encrypt_output_length( + std::size_t plaintext_length + ); + + /** The number of bytes of random data the encrypt method will need to + * encrypt a message. This will be 32 bytes if the session needs to + * generate a new ephemeral key, or will be 0 bytes otherwise.*/ + std::size_t encrypt_random_length(); + + /** Encrypt some plain-text. Returns the length of the encrypted message + * or std::size_t(-1) on failure. On failure last_error will be set with + * an error code. The last_error will be NOT_ENOUGH_RANDOM if the number + * of random bytes is too small. The last_error will be + * OUTPUT_BUFFER_TOO_SMALL if the output buffer is too small. */ + std::size_t encrypt( + std::uint8_t const * plaintext, std::size_t plaintext_length, + std::uint8_t const * random, std::size_t random_length, + std::uint8_t * output, std::size_t max_output_length + ); + + /** An upper bound on the number of bytes of plain-text the decrypt method + * will write for a given input message length. */ + std::size_t decrypt_max_plaintext_length( + std::uint8_t const * input, std::size_t input_length + ); + + /** Decrypt a message. Returns the length of the decrypted plain-text or + * std::size_t(-1) on failure. On failure last_error will be set with an + * error code. The last_error will be OUTPUT_BUFFER_TOO_SMALL if the + * plain-text buffer is too small. The last_error will be + * BAD_MESSAGE_VERSION if the message was encrypted with an unsupported + * version of the protocol. The last_error will be BAD_MESSAGE_FORMAT if + * the message headers could not be decoded. The last_error will be + * BAD_MESSAGE_MAC if the message could not be verified */ + std::size_t decrypt( + std::uint8_t const * input, std::size_t input_length, + std::uint8_t * plaintext, std::size_t max_plaintext_length + ); +}; + + +std::size_t pickle_length( + Ratchet const & value +); + + +std::uint8_t * pickle( + std::uint8_t * pos, + Ratchet const & value +); + + +std::uint8_t const * unpickle( + std::uint8_t const * pos, std::uint8_t const * end, + Ratchet & value +); + + +} // namespace olm diff --git a/include/olm/session.hh b/include/olm/session.hh new file mode 100644 index 0000000..a0aff08 --- /dev/null +++ b/include/olm/session.hh @@ -0,0 +1,114 @@ +/* Copyright 2015 OpenMarket Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef OLM_SESSION_HH_ +#define OLM_SESSION_HH_ + +#include "olm/ratchet.hh" + +namespace olm { + +class Account; + +struct RemoteKey { + std::uint32_t id; + Curve25519PublicKey key; +}; + + +enum struct MessageType { + PRE_KEY = 0, + MESSAGE = 1, +}; + + +struct Session { + + Session(); + + Ratchet ratchet; + ErrorCode last_error; + + bool received_message; + + RemoteKey alice_identity_key; + Curve25519PublicKey alice_base_key; + std::uint32_t bob_one_time_key_id; + + + std::size_t new_outbound_session_random_length(); + + std::size_t new_outbound_session( + Account const & local_account, + Curve25519PublicKey const & identity_key, + RemoteKey const & one_time_key, + std::uint8_t const * random, std::size_t random_length + ); + + std::size_t new_inbound_session( + Account & local_account, + std::uint8_t const * one_time_key_message, std::size_t message_length + ); + + bool matches_inbound_session( + std::uint8_t const * one_time_key_message, std::size_t message_length + ); + + MessageType encrypt_message_type(); + + std::size_t encrypt_message_length( + std::size_t plaintext_length + ); + + std::size_t encrypt_random_length(); + + std::size_t encrypt( + std::uint8_t const * plaintext, std::size_t plaintext_length, + std::uint8_t const * random, std::size_t random_length, + std::uint8_t * message, std::size_t message_length + ); + + std::size_t decrypt_max_plaintext_length( + MessageType message_type, + std::uint8_t const * message, std::size_t message_length + ); + + std::size_t decrypt( + MessageType message_type, + std::uint8_t const * message, std::size_t message_length, + std::uint8_t * plaintext, std::size_t max_plaintext_length + ); +}; + + +std::size_t pickle_length( + Session const & value +); + + +std::uint8_t * pickle( + std::uint8_t * pos, + Session const & value +); + + +std::uint8_t const * unpickle( + std::uint8_t const * pos, std::uint8_t const * end, + Session & value +); + + +} // namespace olm + +#endif /* OLM_SESSION_HH_ */ diff --git a/javascript/axolotl_post.js b/javascript/axolotl_post.js deleted file mode 100644 index 570c66f..0000000 --- a/javascript/axolotl_post.js +++ /dev/null @@ -1,248 +0,0 @@ -var runtime = Module['Runtime']; -var malloc = Module['_malloc']; -var free = Module['_free']; -var Pointer_stringify = Module['Pointer_stringify']; -var AXOLOTL_ERROR = Module['_axolotl_error'](); - -function stack(size_or_array) { - return Module['allocate'](size_or_array, 'i8', Module['ALLOC_STACK']); -} - -function array_from_string(string) { - return Module['intArrayFromString'](string, true); -} - -function random_stack(size) { - var ptr = stack(size); - var array = new Uint8Array(Module['HEAPU8'].buffer, ptr, size); - window.crypto.getRandomValues(array); - return ptr; -} - -function restore_stack(wrapped) { - return function() { - var sp = runtime.stackSave(); - try { - return wrapped.apply(this, arguments); - } finally { - runtime.stackRestore(sp); - } - } -} - -function Account() { - var size = Module['_axolotl_account_size'](); - this.buf = malloc(size); - this.ptr = Module['_axolotl_account'](this.buf); -} - -function account_method(wrapped) { - return function() { - var result = wrapped.apply(this, arguments); - if (result === AXOLOTL_ERROR) { - var message = Pointer_stringify( - Module['_axolotl_account_last_error'](arguments[0]) - ); - throw new Error("AXOLOTL." + message); - } - return result; - } -} - -Account.prototype['free'] = function() { - free(this.ptr); -} - -Account.prototype['create'] = restore_stack(function() { - var random_length = account_method( - Module['_axolotl_create_account_random_length'] - )(this.ptr); - var random = random_stack(random_length); - account_method(Module['_axolotl_create_account'])( - this.ptr, random, random_length - ); -}); - -Account.prototype['identity_keys'] = restore_stack(function() { - var keys_length = account_method( - Module['_axolotl_account_identity_keys_length'] - )(this.ptr); - var keys = stack(keys_length); - account_method(Module['_axolotl_account_identity_keys'])( - this.ptr, keys, keys_length - ); - return Pointer_stringify(keys, keys_length); -}); - -Account.prototype['one_time_keys'] = restore_stack(function() { - var keys_length = account_method( - Module['_axolotl_account_one_time_keys_length'] - )(this.ptr); - var keys = stack(keys_length); - account_method(Module['_axolotl_account_one_time_keys'])( - this.ptr, keys, keys_length - ); - return Pointer_stringify(keys, keys_length); -}); - -Account.prototype['pickle'] = restore_stack(function(key) { - var key_array = array_from_string(key); - var pickle_length = account_method( - Module['_axolotl_pickle_account_length'] - )(this.ptr); - var key_buffer = stack(key_array); - var pickle_buffer = stack(pickle_length); - account_method(Module['_axolotl_pickle_account'])( - this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length - ); - return Pointer_stringify(pickle_buffer, pickle_length); -}); - -Account.prototype['unpickle'] = restore_stack(function(key, pickle) { - var key_array = array_from_string(key); - var key_buffer = stack(key_array); - var pickle_array = array_from_string(pickle); - var pickle_buffer = stack(pickle_length); - account_method(Module['_axolotl_unpickle_account'])( - this.ptr, key_buffer, key_array.length, pickle_buffer, - pickle_array.length - ); -}); - -function Session() { - var size = Module['_axolotl_session_size'](); - this.buf = malloc(size); - this.ptr = Module['_axolotl_session'](this.buf); -} - -function session_method(wrapped) { - return function() { - var result = wrapped.apply(this, arguments); - if (result === AXOLOTL_ERROR) { - var message = Pointer_stringify( - Module['_axolotl_session_last_error'](arguments[0]) - ); - throw new Error("AXOLOTL." + message); - } - return result; - } -} - -Session.prototype['free'] = function() { - free(this.ptr); -} - -Session.prototype['pickle'] = restore_stack(function(key) { - var key_array = array_from_string(key); - var pickle_length = session_method( - Module['_axolotl_pickle_session_length'] - )(this.ptr); - var key_buffer = stack(key_array); - var pickle_buffer = stack(pickle_length); - session_method(Module['_axolotl_pickle_session'])( - this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length - ); - return Pointer_stringify(pickle_buffer, pickle_length); -}); - -Session.prototype['unpickle'] = restore_stack(function(key, pickle) { - var key_array = array_from_string(key); - var key_buffer = stack(key_array); - var pickle_array = array_from_string(pickle); - var pickle_buffer = stack(pickle_array); - session_method(Module['_axolotl_unpickle_session'])( - this.ptr, key_buffer, key_array.length, pickle_buffer, - pickle_array.length - ); -}); - -Session.prototype['create_outbound'] = restore_stack(function( - account, their_identity_key, their_one_time_key_id, their_one_time_key -) { - var random_length = session_method( - Module['_axolotl_create_outbound_session_random_length'] - )(this.ptr); - var random = random_stack(random_length); - var identity_key_array = array_from_string(their_identity_key); - var one_time_key_array = array_from_string(their_one_time_key); - var identity_key_buffer = stack(identity_key_array); - var one_time_key_buffer = stack(one_time_key_array); - session_method(Module['_axolotl_create_outbound_session'])( - this.ptr, account.ptr, - identity_key_buffer, identity_key_array.length, - their_one_time_key_id, - one_time_key_buffer, one_time_key_array.length, - random, random_length - ); -}); - -Session.prototype['create_inbound'] = restore_stack(function( - account, one_time_key_message -) { - var message_array = array_from_string(one_time_key_message); - var message_buffer = stack(message_array); - session_method(Module['_axolotl_create_inbound_session'])( - this.ptr, account.ptr, message_buffer, message_array.length - ); -}); - -Session.prototype['matches_inbound'] = restore_stack(function( - account, one_time_key_message -) { - var message_array = array_from_string(one_time_key_message); - var message_buffer = stack(message_array); - return session_method(Module['_axolotl_matches_inbound_session'])( - this.ptr, account.ptr, message_buffer, message_array.length - ) ? true : false; -}); - -Session.prototype['encrypt'] = restore_stack(function( - plaintext -) { - var random_length = session_method( - Module['_axolotl_encrypt_random_length'] - )(this.ptr); - var message_type = session_method( - Module['_axolotl_encrypt_message_type'] - )(this.ptr); - var plaintext_array = array_from_string(plaintext); - var message_length = session_method( - Module['_axolotl_encrypt_message_length'] - )(this.ptr, plaintext_array.length); - var random = random_stack(random_length); - var plaintext_buffer = stack(plaintext_array); - var message_buffer = stack(message_length); - session_method(Module['_axolotl_encrypt'])( - this.ptr, - plaintext_buffer, plaintext_array.length, - random, random_length, - message_buffer, message_length - ); - return { - "type": message_type, - "body": Pointer_stringify(message_buffer, message_length) - }; -}); - -Session.prototype['decrypt'] = restore_stack(function( - message_type, message -) { - var message_array = array_from_string(message); - var message_buffer = stack(message_array); - var max_plaintext_length = session_method( - Module['_axolotl_decrypt_max_plaintext_length'] - )(this.ptr, message_type, message_buffer, message_array.length); - // caculating the length destroys the input buffer. - // So we copy the array to a new buffer - var message_buffer = stack(message_array); - var plaintext_buffer = stack(max_plaintext_length); - var plaintext_length = session_method(Module["_axolotl_decrypt"])( - this.ptr, message_type, - message_buffer, message.length, - plaintext_buffer, max_plaintext_length - ); - return Pointer_stringify(plaintext_buffer, plaintext_length); -}); - -return {"Account": Account, "Session": Session}; -}(); diff --git a/javascript/axolotl_pre.js b/javascript/axolotl_pre.js deleted file mode 100644 index 69df923..0000000 --- a/javascript/axolotl_pre.js +++ /dev/null @@ -1 +0,0 @@ -Axolotl = function() { diff --git a/javascript/build.py b/javascript/build.py index 08436fd..68b7e45 100755 --- a/javascript/build.py +++ b/javascript/build.py @@ -28,8 +28,8 @@ if not os.path.exists("build"): os.mkdir("build") functions = set() -RE_FUNCTION=re.compile("(axolotl_[^( ]*)\\(") -with open("include/axolotl/axolotl.hh") as header: +RE_FUNCTION=re.compile("(olm_[^( ]*)\\(") +with open("include/olm/olm.hh") as header: for line in header: match = RE_FUNCTION.search(line) if match: @@ -60,7 +60,7 @@ compile_args += ("--pre-js", pre_js) compile_args += ("--post-js", post_js) compile_args += ("-s", "EXPORTED_FUNCTIONS=@" + exported_functions) -library = "build/axolotl.js" +library = "build/olm.js" def run(args): print args diff --git a/javascript/demo.html b/javascript/demo.html index ce4bfed..cace119 100644 --- a/javascript/demo.html +++ b/javascript/demo.html @@ -1,6 +1,6 @@ - +