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 --- 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 +++++++++++++++++++ 12 files changed, 1490 insertions(+) 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 (limited to 'include/olm') 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_ */ -- cgit v1.2.3