diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/account.cpp | 75 | ||||
-rw-r--r-- | src/base64.cpp | 39 | ||||
-rw-r--r-- | src/cipher.cpp | 115 | ||||
-rw-r--r-- | src/crypto.cpp | 175 | ||||
-rw-r--r-- | src/ed25519.c (renamed from src/libs.cpp) | 9 | ||||
-rw-r--r-- | src/error.c | 43 | ||||
-rw-r--r-- | src/inbound_group_session.c | 386 | ||||
-rw-r--r-- | src/megolm.c | 150 | ||||
-rw-r--r-- | src/memory.cpp | 6 | ||||
-rw-r--r-- | src/message.cpp | 79 | ||||
-rw-r--r-- | src/olm.cpp | 201 | ||||
-rw-r--r-- | src/outbound_group_session.c | 361 | ||||
-rw-r--r-- | src/pickle.cpp | 162 | ||||
-rw-r--r-- | src/pickle_encoding.c | 92 | ||||
-rw-r--r-- | src/ratchet.cpp | 139 | ||||
-rw-r--r-- | src/session.cpp | 173 | ||||
-rw-r--r-- | src/utility.cpp | 22 |
17 files changed, 1694 insertions, 533 deletions
diff --git a/src/account.cpp b/src/account.cpp index 43033c8..248f3d1 100644 --- a/src/account.cpp +++ b/src/account.cpp @@ -1,4 +1,4 @@ -/* Copyright 2015 OpenMarket Ltd +/* Copyright 2015, 2016 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,20 +14,21 @@ */ #include "olm/account.hh" #include "olm/base64.hh" +#include "olm/pickle.h" #include "olm/pickle.hh" #include "olm/memory.hh" olm::Account::Account( ) : next_one_time_key_id(0), - last_error(olm::ErrorCode::SUCCESS) { + last_error(OlmErrorCode::OLM_SUCCESS) { } olm::OneTimeKey const * olm::Account::lookup_key( - olm::Curve25519PublicKey const & public_key + _olm_curve25519_public_key const & public_key ) { for (olm::OneTimeKey const & key : one_time_keys) { - if (olm::array_equal(key.key.public_key, public_key.public_key)) { + if (olm::array_equal(key.key.public_key.public_key, public_key.public_key)) { return &key; } } @@ -35,11 +36,11 @@ olm::OneTimeKey const * olm::Account::lookup_key( } std::size_t olm::Account::remove_key( - olm::Curve25519PublicKey const & public_key + _olm_curve25519_public_key const & public_key ) { OneTimeKey * i; for (i = one_time_keys.begin(); i != one_time_keys.end(); ++i) { - if (olm::array_equal(i->key.public_key, public_key.public_key)) { + if (olm::array_equal(i->key.public_key.public_key, public_key.public_key)) { std::uint32_t id = i->id; one_time_keys.erase(i); return id; @@ -49,20 +50,20 @@ std::size_t olm::Account::remove_key( } std::size_t olm::Account::new_account_random_length() { - return 2 * olm::KEY_LENGTH; + return ED25519_RANDOM_LENGTH + CURVE25519_RANDOM_LENGTH; } std::size_t olm::Account::new_account( uint8_t const * random, std::size_t random_length ) { if (random_length < new_account_random_length()) { - last_error = olm::ErrorCode::NOT_ENOUGH_RANDOM; + last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM; return std::size_t(-1); } - olm::ed25519_generate_key(random, identity_keys.ed25519_key); - random += KEY_LENGTH; - olm::curve25519_generate_key(random, identity_keys.curve25519_key); + _olm_crypto_ed25519_generate_key(random, &identity_keys.ed25519_key); + random += ED25519_RANDOM_LENGTH; + _olm_crypto_curve25519_generate_key(random, &identity_keys.curve25519_key); return 0; } @@ -110,7 +111,7 @@ std::size_t olm::Account::get_identity_json( size_t expected_length = get_identity_json_length(); if (identity_json_length < expected_length) { - last_error = olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL; + last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } @@ -118,16 +119,16 @@ std::size_t olm::Account::get_identity_json( pos = write_string(pos, KEY_JSON_CURVE25519); *(pos++) = '\"'; pos = olm::encode_base64( - identity_keys.curve25519_key.public_key, - sizeof(identity_keys.curve25519_key.public_key), + identity_keys.curve25519_key.public_key.public_key, + sizeof(identity_keys.curve25519_key.public_key.public_key), pos ); *(pos++) = '\"'; *(pos++) = ','; pos = write_string(pos, KEY_JSON_ED25519); *(pos++) = '\"'; pos = olm::encode_base64( - identity_keys.ed25519_key.public_key, - sizeof(identity_keys.ed25519_key.public_key), + identity_keys.ed25519_key.public_key.public_key, + sizeof(identity_keys.ed25519_key.public_key.public_key), pos ); *(pos++) = '\"'; *(pos++) = '}'; @@ -137,7 +138,7 @@ std::size_t olm::Account::get_identity_json( std::size_t olm::Account::signature_length( ) { - return olm::SIGNATURE_LENGTH; + return ED25519_SIGNATURE_LENGTH; } @@ -146,11 +147,11 @@ std::size_t olm::Account::sign( std::uint8_t * signature, std::size_t signature_length ) { if (signature_length < this->signature_length()) { - last_error = olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL; + last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } - olm::ed25519_sign( - identity_keys.ed25519_key, message, message_length, signature + _olm_crypto_ed25519_sign( + &identity_keys.ed25519_key, message, message_length, signature ); return this->signature_length(); } @@ -185,7 +186,7 @@ std::size_t olm::Account::get_one_time_keys_json( ) { std::uint8_t * pos = one_time_json; if (one_time_json_length < get_one_time_keys_json_length()) { - last_error = olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL; + last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } *(pos++) = '{'; @@ -202,7 +203,7 @@ std::size_t olm::Account::get_one_time_keys_json( pos = olm::encode_base64(key_id, sizeof(key_id), pos); *(pos++) = '\"'; *(pos++) = ':'; *(pos++) = '\"'; pos = olm::encode_base64( - key.key.public_key, sizeof(key.key.public_key), pos + key.key.public_key.public_key, sizeof(key.key.public_key.public_key), pos ); *(pos++) = '\"'; sep = ','; @@ -238,7 +239,7 @@ std::size_t olm::Account::max_number_of_one_time_keys( std::size_t olm::Account::generate_one_time_keys_random_length( std::size_t number_of_keys ) { - return olm::KEY_LENGTH * number_of_keys; + return CURVE25519_RANDOM_LENGTH * number_of_keys; } std::size_t olm::Account::generate_one_time_keys( @@ -246,15 +247,15 @@ std::size_t olm::Account::generate_one_time_keys( std::uint8_t const * random, std::size_t random_length ) { if (random_length < generate_one_time_keys_random_length(number_of_keys)) { - last_error = olm::ErrorCode::NOT_ENOUGH_RANDOM; + last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM; return std::size_t(-1); } for (unsigned i = 0; i < number_of_keys; ++i) { OneTimeKey & key = *one_time_keys.insert(one_time_keys.begin()); key.id = ++next_one_time_key_id; key.published = false; - olm::curve25519_generate_key(random, key.key); - random += olm::KEY_LENGTH; + _olm_crypto_curve25519_generate_key(random, &key.key); + random += CURVE25519_RANDOM_LENGTH; } return number_of_keys; } @@ -265,7 +266,7 @@ static std::size_t pickle_length( olm::IdentityKeys const & value ) { size_t length = 0; - length += olm::pickle_length(value.ed25519_key); + length += _olm_pickle_ed25519_key_pair_length(&value.ed25519_key); length += olm::pickle_length(value.curve25519_key); return length; } @@ -275,7 +276,7 @@ static std::uint8_t * pickle( std::uint8_t * pos, olm::IdentityKeys const & value ) { - pos = olm::pickle(pos, value.ed25519_key); + pos = _olm_pickle_ed25519_key_pair(pos, &value.ed25519_key); pos = olm::pickle(pos, value.curve25519_key); return pos; } @@ -285,7 +286,7 @@ static std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, olm::IdentityKeys & value ) { - pos = olm::unpickle(pos, end, value.ed25519_key); + pos = _olm_unpickle_ed25519_key_pair(pos, end, &value.ed25519_key); pos = olm::unpickle(pos, end, value.curve25519_key); return pos; } @@ -326,7 +327,9 @@ static std::uint8_t const * unpickle( } // namespace olm namespace { -static const std::uint32_t ACCOUNT_PICKLE_VERSION = 1; +// pickle version 1 used only 32 bytes for the ed25519 private key. +// Any keys thus used should be considered compromised. +static const std::uint32_t ACCOUNT_PICKLE_VERSION = 2; } @@ -360,9 +363,15 @@ std::uint8_t const * olm::unpickle( ) { uint32_t pickle_version; pos = olm::unpickle(pos, end, pickle_version); - if (pickle_version != ACCOUNT_PICKLE_VERSION) { - value.last_error = olm::ErrorCode::UNKNOWN_PICKLE_VERSION; - return end; + switch (pickle_version) { + case ACCOUNT_PICKLE_VERSION: + break; + case 1: + value.last_error = OlmErrorCode::OLM_BAD_LEGACY_ACCOUNT_PICKLE; + return end; + default: + value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION; + return end; } pos = olm::unpickle(pos, end, value.identity_keys); pos = olm::unpickle(pos, end, value.one_time_keys); diff --git a/src/base64.cpp b/src/base64.cpp index bf8492e..bbfb210 100644 --- a/src/base64.cpp +++ b/src/base64.cpp @@ -12,9 +12,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "olm/base64.h" #include "olm/base64.hh" - namespace { static const std::uint8_t ENCODE_BASE64[64] = { @@ -45,6 +45,12 @@ static const std::uint8_t DECODE_BASE64[128] = { } // namespace +std::size_t olm::encode_base64_length( + std::size_t input_length +) { + return 4 * ((input_length + 2) / 3) + (input_length + 2) % 3 - 2; +} + std::uint8_t * olm::encode_base64( std::uint8_t const * input, std::size_t input_length, std::uint8_t * output @@ -128,3 +134,34 @@ std::uint8_t const * olm::decode_base64( } return input + input_length; } + + +// implementations of base64.h + +size_t _olm_encode_base64_length( + size_t input_length +) { + return olm::encode_base64_length(input_length); +} + +size_t _olm_encode_base64( + uint8_t const * input, size_t input_length, + uint8_t * output +) { + uint8_t * r = olm::encode_base64(input, input_length, output); + return r - output; +} + +size_t _olm_decode_base64_length( + size_t input_length +) { + return olm::decode_base64_length(input_length); +} + +size_t _olm_decode_base64( + uint8_t const * input, size_t input_length, + uint8_t * output +) { + olm::decode_base64(input, input_length, output); + return olm::decode_base64_length(input_length); +} diff --git a/src/cipher.cpp b/src/cipher.cpp index 7bb11b8..6b53690 100644 --- a/src/cipher.cpp +++ b/src/cipher.cpp @@ -12,21 +12,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "olm/cipher.hh" -#include "olm/crypto.hh" +#include "olm/cipher.h" +#include "olm/crypto.h" #include "olm/memory.hh" #include <cstring> -olm::Cipher::~Cipher() { - -} +const std::size_t HMAC_KEY_LENGTH = 32; namespace { struct DerivedKeys { - olm::Aes256Key aes_key; - std::uint8_t mac_key[olm::KEY_LENGTH]; - olm::Aes256Iv aes_iv; + _olm_aes256_key aes_key; + std::uint8_t mac_key[HMAC_KEY_LENGTH]; + _olm_aes256_iv aes_iv; }; @@ -35,8 +33,10 @@ static void derive_keys( std::uint8_t const * key, std::size_t key_length, DerivedKeys & keys ) { - std::uint8_t derived_secrets[2 * olm::KEY_LENGTH + olm::IV_LENGTH]; - olm::hkdf_sha256( + std::uint8_t derived_secrets[ + AES256_KEY_LENGTH + HMAC_KEY_LENGTH + AES256_IV_LENGTH + ]; + _olm_crypto_hkdf_sha256( key, key_length, nullptr, 0, kdf_info, kdf_info_length, @@ -51,48 +51,41 @@ static void derive_keys( static const std::size_t MAC_LENGTH = 8; -} // namespace - - -olm::CipherAesSha256::CipherAesSha256( - std::uint8_t const * kdf_info, std::size_t kdf_info_length -) : kdf_info(kdf_info), kdf_info_length(kdf_info_length) { - -} - - -std::size_t olm::CipherAesSha256::mac_length() const { +size_t aes_sha_256_cipher_mac_length(const struct _olm_cipher *cipher) { return MAC_LENGTH; } - -std::size_t olm::CipherAesSha256::encrypt_ciphertext_length( - std::size_t plaintext_length -) const { - return olm::aes_encrypt_cbc_length(plaintext_length); +size_t aes_sha_256_cipher_encrypt_ciphertext_length( + const struct _olm_cipher *cipher, size_t plaintext_length +) { + return _olm_crypto_aes_encrypt_cbc_length(plaintext_length); } +size_t aes_sha_256_cipher_encrypt( + const struct _olm_cipher *cipher, + uint8_t const * key, size_t key_length, + uint8_t const * plaintext, size_t plaintext_length, + uint8_t * ciphertext, size_t ciphertext_length, + uint8_t * output, size_t output_length +) { + auto *c = reinterpret_cast<const _olm_cipher_aes_sha_256 *>(cipher); -std::size_t olm::CipherAesSha256::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 { - if (encrypt_ciphertext_length(plaintext_length) < ciphertext_length) { + if (aes_sha_256_cipher_encrypt_ciphertext_length(cipher, plaintext_length) + < ciphertext_length) { return std::size_t(-1); } + struct DerivedKeys keys; - std::uint8_t mac[olm::SHA256_OUTPUT_LENGTH]; + std::uint8_t mac[SHA256_OUTPUT_LENGTH]; - derive_keys(kdf_info, kdf_info_length, key, key_length, keys); + derive_keys(c->kdf_info, c->kdf_info_length, key, key_length, keys); - olm::aes_encrypt_cbc( - keys.aes_key, keys.aes_iv, plaintext, plaintext_length, ciphertext + _olm_crypto_aes_encrypt_cbc( + &keys.aes_key, &keys.aes_iv, plaintext, plaintext_length, ciphertext ); - olm::hmac_sha256( - keys.mac_key, olm::KEY_LENGTH, output, output_length - MAC_LENGTH, mac + _olm_crypto_hmac_sha256( + keys.mac_key, HMAC_KEY_LENGTH, output, output_length - MAC_LENGTH, mac ); std::memcpy(output + output_length - MAC_LENGTH, mac, MAC_LENGTH); @@ -102,25 +95,29 @@ std::size_t olm::CipherAesSha256::encrypt( } -std::size_t olm::CipherAesSha256::decrypt_max_plaintext_length( - std::size_t ciphertext_length -) const { +size_t aes_sha_256_cipher_decrypt_max_plaintext_length( + const struct _olm_cipher *cipher, + size_t ciphertext_length +) { return ciphertext_length; } -std::size_t olm::CipherAesSha256::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 { +size_t aes_sha_256_cipher_decrypt( + const struct _olm_cipher *cipher, + uint8_t const * key, size_t key_length, + uint8_t const * input, size_t input_length, + uint8_t const * ciphertext, size_t ciphertext_length, + uint8_t * plaintext, size_t max_plaintext_length +) { + auto *c = reinterpret_cast<const _olm_cipher_aes_sha_256 *>(cipher); + DerivedKeys keys; - std::uint8_t mac[olm::SHA256_OUTPUT_LENGTH]; + std::uint8_t mac[SHA256_OUTPUT_LENGTH]; - derive_keys(kdf_info, kdf_info_length, key, key_length, keys); + derive_keys(c->kdf_info, c->kdf_info_length, key, key_length, keys); - olm::hmac_sha256( - keys.mac_key, olm::KEY_LENGTH, input, input_length - MAC_LENGTH, mac + _olm_crypto_hmac_sha256( + keys.mac_key, HMAC_KEY_LENGTH, input, input_length - MAC_LENGTH, mac ); std::uint8_t const * input_mac = input + input_length - MAC_LENGTH; @@ -129,10 +126,20 @@ std::size_t olm::CipherAesSha256::decrypt( return std::size_t(-1); } - std::size_t plaintext_length = olm::aes_decrypt_cbc( - keys.aes_key, keys.aes_iv, ciphertext, ciphertext_length, plaintext + std::size_t plaintext_length = _olm_crypto_aes_decrypt_cbc( + &keys.aes_key, &keys.aes_iv, ciphertext, ciphertext_length, plaintext ); olm::unset(keys); return plaintext_length; } + +} // namespace + +const struct _olm_cipher_ops _olm_cipher_aes_sha_256_ops = { + aes_sha_256_cipher_mac_length, + aes_sha_256_cipher_encrypt_ciphertext_length, + aes_sha_256_cipher_encrypt, + aes_sha_256_cipher_decrypt_max_plaintext_length, + aes_sha_256_cipher_decrypt, +}; diff --git a/src/crypto.cpp b/src/crypto.cpp index 8024355..5095c79 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -12,62 +12,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "olm/crypto.hh" +#include "olm/crypto.h" #include "olm/memory.hh" #include <cstring> extern "C" { -int curve25519_donna( - uint8_t * output, - const uint8_t * secret, - const uint8_t * basepoint -); - #include "crypto-algorithms/aes.h" #include "crypto-algorithms/sha256.h" -int ed25519_sign( - unsigned char *signature, - const unsigned char *message, size_t message_len, - const unsigned char *public_key, - const unsigned char *private_key -); - - -int ed25519_verify( - const unsigned char *signature, - const unsigned char *message, size_t message_len, - const unsigned char *public_key -); - - -void convert_curve25519_to_ed25519( - unsigned char * public_key, - unsigned char * signature -); - - -void convert_ed25519_to_curve25519( - unsigned char const * public_key, - unsigned char * signature -); - - -void ed25519_keypair( - unsigned char * private_key, - unsigned char * public_key -); - } +#include "ed25519/src/ed25519.h" +#include "curve25519-donna.h" namespace { static const std::uint8_t CURVE25519_BASEPOINT[32] = {9}; static const std::size_t AES_KEY_SCHEDULE_LENGTH = 60; -static const std::size_t AES_KEY_BITS = 8 * olm::KEY_LENGTH; +static const std::size_t AES_KEY_BITS = 8 * AES256_KEY_LENGTH; static const std::size_t AES_BLOCK_LENGTH = 16; static const std::size_t SHA256_BLOCK_LENGTH = 64; static const std::uint8_t HKDF_DEFAULT_SALT[32] = {}; @@ -120,7 +84,7 @@ inline static void hmac_sha256_final( std::uint8_t const * hmac_key, std::uint8_t * output ) { - std::uint8_t o_pad[SHA256_BLOCK_LENGTH + olm::SHA256_OUTPUT_LENGTH]; + std::uint8_t o_pad[SHA256_BLOCK_LENGTH + SHA256_OUTPUT_LENGTH]; std::memcpy(o_pad, hmac_key, SHA256_BLOCK_LENGTH); for (std::size_t i = 0; i < SHA256_BLOCK_LENGTH; ++i) { o_pad[i] ^= 0x5C; @@ -136,115 +100,86 @@ inline static void hmac_sha256_final( } // namespace - -void olm::curve25519_generate_key( - std::uint8_t const * random_32_bytes, - olm::Curve25519KeyPair & key_pair +void _olm_crypto_curve25519_generate_key( + uint8_t const * random_32_bytes, + struct _olm_curve25519_key_pair *key_pair ) { - std::memcpy(key_pair.private_key, random_32_bytes, KEY_LENGTH); + std::memcpy( + key_pair->private_key.private_key, random_32_bytes, + CURVE25519_KEY_LENGTH + ); ::curve25519_donna( - key_pair.public_key, key_pair.private_key, CURVE25519_BASEPOINT + key_pair->public_key.public_key, + key_pair->private_key.private_key, + CURVE25519_BASEPOINT ); } -void olm::curve25519_shared_secret( - olm::Curve25519KeyPair const & our_key, - olm::Curve25519PublicKey const & their_key, - std::uint8_t * output -) { - ::curve25519_donna(output, our_key.private_key, their_key.public_key); -} - - -void olm::curve25519_sign( - olm::Curve25519KeyPair const & our_key, - std::uint8_t const * message, std::size_t message_length, +void _olm_crypto_curve25519_shared_secret( + const struct _olm_curve25519_key_pair *our_key, + const struct _olm_curve25519_public_key * their_key, std::uint8_t * output ) { - std::uint8_t private_key[KEY_LENGTH]; - std::uint8_t public_key[KEY_LENGTH]; - std::memcpy(private_key, our_key.private_key, KEY_LENGTH); - ::ed25519_keypair(private_key, public_key); - ::ed25519_sign( - output, - message, message_length, - public_key, private_key - ); - ::convert_ed25519_to_curve25519(public_key, output); + ::curve25519_donna(output, our_key->private_key.private_key, their_key->public_key); } -bool olm::curve25519_verify( - olm::Curve25519PublicKey const & their_key, - std::uint8_t const * message, std::size_t message_length, - std::uint8_t const * signature -) { - std::uint8_t public_key[KEY_LENGTH]; - std::uint8_t signature_buffer[SIGNATURE_LENGTH]; - std::memcpy(public_key, their_key.public_key, KEY_LENGTH); - std::memcpy(signature_buffer, signature, SIGNATURE_LENGTH); - ::convert_curve25519_to_ed25519(public_key, signature_buffer); - return 0 != ::ed25519_verify( - signature, - message, message_length, - public_key - ); -} - - -void olm::ed25519_generate_key( +void _olm_crypto_ed25519_generate_key( std::uint8_t const * random_32_bytes, - olm::Ed25519KeyPair & key_pair + struct _olm_ed25519_key_pair *key_pair ) { - std::memcpy(key_pair.private_key, random_32_bytes, KEY_LENGTH); - ::ed25519_keypair(key_pair.private_key, key_pair.public_key); + ::ed25519_create_keypair( + key_pair->public_key.public_key, key_pair->private_key.private_key, + random_32_bytes + ); } -void olm::ed25519_sign( - olm::Ed25519KeyPair const & our_key, +void _olm_crypto_ed25519_sign( + const struct _olm_ed25519_key_pair *our_key, std::uint8_t const * message, std::size_t message_length, std::uint8_t * output ) { ::ed25519_sign( output, message, message_length, - our_key.public_key, our_key.private_key + our_key->public_key.public_key, + our_key->private_key.private_key ); } -bool olm::ed25519_verify( - olm::Ed25519PublicKey const & their_key, +int _olm_crypto_ed25519_verify( + const struct _olm_ed25519_public_key *their_key, std::uint8_t const * message, std::size_t message_length, std::uint8_t const * signature ) { return 0 != ::ed25519_verify( signature, message, message_length, - their_key.public_key + their_key->public_key ); } -std::size_t olm::aes_encrypt_cbc_length( +std::size_t _olm_crypto_aes_encrypt_cbc_length( std::size_t input_length ) { return input_length + AES_BLOCK_LENGTH - input_length % AES_BLOCK_LENGTH; } -void olm::aes_encrypt_cbc( - olm::Aes256Key const & key, - olm::Aes256Iv const & iv, +void _olm_crypto_aes_encrypt_cbc( + _olm_aes256_key const *key, + _olm_aes256_iv const *iv, std::uint8_t const * input, std::size_t input_length, std::uint8_t * output ) { std::uint32_t key_schedule[AES_KEY_SCHEDULE_LENGTH]; - ::aes_key_setup(key.key, key_schedule, AES_KEY_BITS); + ::aes_key_setup(key->key, key_schedule, AES_KEY_BITS); std::uint8_t input_block[AES_BLOCK_LENGTH]; - std::memcpy(input_block, iv.iv, AES_BLOCK_LENGTH); + std::memcpy(input_block, iv->iv, AES_BLOCK_LENGTH); while (input_length >= AES_BLOCK_LENGTH) { xor_block<AES_BLOCK_LENGTH>(input_block, input); ::aes_encrypt(input_block, output, key_schedule, AES_KEY_BITS); @@ -266,17 +201,17 @@ void olm::aes_encrypt_cbc( } -std::size_t olm::aes_decrypt_cbc( - olm::Aes256Key const & key, - olm::Aes256Iv const & iv, +std::size_t _olm_crypto_aes_decrypt_cbc( + _olm_aes256_key const *key, + _olm_aes256_iv const *iv, std::uint8_t const * input, std::size_t input_length, std::uint8_t * output ) { std::uint32_t key_schedule[AES_KEY_SCHEDULE_LENGTH]; - ::aes_key_setup(key.key, key_schedule, AES_KEY_BITS); + ::aes_key_setup(key->key, key_schedule, AES_KEY_BITS); std::uint8_t block1[AES_BLOCK_LENGTH]; std::uint8_t block2[AES_BLOCK_LENGTH]; - std::memcpy(block1, iv.iv, AES_BLOCK_LENGTH); + std::memcpy(block1, iv->iv, AES_BLOCK_LENGTH); for (std::size_t i = 0; i < input_length; i += AES_BLOCK_LENGTH) { std::memcpy(block2, &input[i], AES_BLOCK_LENGTH); ::aes_decrypt(&input[i], &output[i], key_schedule, AES_KEY_BITS); @@ -291,7 +226,7 @@ std::size_t olm::aes_decrypt_cbc( } -void olm::sha256( +void _olm_crypto_sha256( std::uint8_t const * input, std::size_t input_length, std::uint8_t * output ) { @@ -303,7 +238,7 @@ void olm::sha256( } -void olm::hmac_sha256( +void _olm_crypto_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 @@ -319,7 +254,7 @@ void olm::hmac_sha256( } -void olm::hkdf_sha256( +void _olm_crypto_hkdf_sha256( std::uint8_t const * input, std::size_t input_length, std::uint8_t const * salt, std::size_t salt_length, std::uint8_t const * info, std::size_t info_length, @@ -327,32 +262,32 @@ void olm::hkdf_sha256( ) { ::SHA256_CTX context; std::uint8_t hmac_key[SHA256_BLOCK_LENGTH]; - std::uint8_t step_result[olm::SHA256_OUTPUT_LENGTH]; + std::uint8_t step_result[SHA256_OUTPUT_LENGTH]; std::size_t bytes_remaining = output_length; std::uint8_t iteration = 1; if (!salt) { salt = HKDF_DEFAULT_SALT; salt_length = sizeof(HKDF_DEFAULT_SALT); } - /* Expand */ + /* Extract */ hmac_sha256_key(salt, salt_length, hmac_key); hmac_sha256_init(&context, hmac_key); ::sha256_update(&context, input, input_length); hmac_sha256_final(&context, hmac_key, step_result); - hmac_sha256_key(step_result, olm::SHA256_OUTPUT_LENGTH, hmac_key); + hmac_sha256_key(step_result, SHA256_OUTPUT_LENGTH, hmac_key); - /* Extract */ + /* Expand */ hmac_sha256_init(&context, hmac_key); ::sha256_update(&context, info, info_length); ::sha256_update(&context, &iteration, 1); hmac_sha256_final(&context, hmac_key, step_result); - while (bytes_remaining > olm::SHA256_OUTPUT_LENGTH) { - std::memcpy(output, step_result, olm::SHA256_OUTPUT_LENGTH); - output += olm::SHA256_OUTPUT_LENGTH; - bytes_remaining -= olm::SHA256_OUTPUT_LENGTH; + while (bytes_remaining > SHA256_OUTPUT_LENGTH) { + std::memcpy(output, step_result, SHA256_OUTPUT_LENGTH); + output += SHA256_OUTPUT_LENGTH; + bytes_remaining -= SHA256_OUTPUT_LENGTH; iteration ++; hmac_sha256_init(&context, hmac_key); - ::sha256_update(&context, step_result, olm::SHA256_OUTPUT_LENGTH); + ::sha256_update(&context, step_result, SHA256_OUTPUT_LENGTH); ::sha256_update(&context, info, info_length); ::sha256_update(&context, &iteration, 1); hmac_sha256_final(&context, hmac_key, step_result); diff --git a/src/libs.cpp b/src/ed25519.c index 6757574..c7a1a8e 100644 --- a/src/libs.cpp +++ b/src/ed25519.c @@ -1,4 +1,4 @@ -/* Copyright 2015 OpenMarket Ltd +/* Copyright 2015-6 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,16 +12,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -extern "C" { -#include "crypto-algorithms/sha256.c" -#include "crypto-algorithms/aes.c" -#include "curve25519-donna/curve25519-donna.c" #define select ed25519_select #include "ed25519/src/fe.c" #include "ed25519/src/sc.c" #include "ed25519/src/ge.c" +#include "ed25519/src/keypair.c" #include "ed25519/src/sha512.c" #include "ed25519/src/verify.c" #include "ed25519/src/sign.c" -#include "ed25519_additions.c" -} diff --git a/src/error.c b/src/error.c new file mode 100644 index 0000000..f541a93 --- /dev/null +++ b/src/error.c @@ -0,0 +1,43 @@ +/* Copyright 2016 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/error.h" + +static const char * ERRORS[] = { + "SUCCESS", + "NOT_ENOUGH_RANDOM", + "OUTPUT_BUFFER_TOO_SMALL", + "BAD_MESSAGE_VERSION", + "BAD_MESSAGE_FORMAT", + "BAD_MESSAGE_MAC", + "BAD_MESSAGE_KEY_ID", + "INVALID_BASE64", + "BAD_ACCOUNT_KEY", + "UNKNOWN_PICKLE_VERSION", + "CORRUPTED_PICKLE", + "BAD_SESSION_KEY", + "UNKNOWN_MESSAGE_INDEX", + "BAD_LEGACY_ACCOUNT_PICKLE", + "BAD_SIGNATURE", +}; + +const char * _olm_error_to_string(enum OlmErrorCode error) +{ + if (error < (sizeof(ERRORS)/sizeof(ERRORS[0]))) { + return ERRORS[error]; + } else { + return "UNKNOWN_ERROR"; + } +} diff --git a/src/inbound_group_session.c b/src/inbound_group_session.c new file mode 100644 index 0000000..bf00008 --- /dev/null +++ b/src/inbound_group_session.c @@ -0,0 +1,386 @@ +/* Copyright 2016 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/inbound_group_session.h" + +#include <string.h> + +#include "olm/base64.h" +#include "olm/cipher.h" +#include "olm/crypto.h" +#include "olm/error.h" +#include "olm/megolm.h" +#include "olm/memory.h" +#include "olm/message.h" +#include "olm/pickle.h" +#include "olm/pickle_encoding.h" + + +#define OLM_PROTOCOL_VERSION 3 +#define GROUP_SESSION_ID_LENGTH ED25519_PUBLIC_KEY_LENGTH +#define PICKLE_VERSION 1 +#define SESSION_KEY_VERSION 2 + +struct OlmInboundGroupSession { + /** our earliest known ratchet value */ + Megolm initial_ratchet; + + /** The most recent ratchet value */ + Megolm latest_ratchet; + + /** The ed25519 signing key */ + struct _olm_ed25519_public_key signing_key; + + enum OlmErrorCode last_error; +}; + +size_t olm_inbound_group_session_size() { + return sizeof(OlmInboundGroupSession); +} + +OlmInboundGroupSession * olm_inbound_group_session( + void *memory +) { + OlmInboundGroupSession *session = memory; + olm_clear_inbound_group_session(session); + return session; +} + +const char *olm_inbound_group_session_last_error( + const OlmInboundGroupSession *session +) { + return _olm_error_to_string(session->last_error); +} + +size_t olm_clear_inbound_group_session( + OlmInboundGroupSession *session +) { + _olm_unset(session, sizeof(OlmInboundGroupSession)); + return sizeof(OlmInboundGroupSession); +} + +#define SESSION_KEY_RAW_LENGTH \ + (1 + 4 + MEGOLM_RATCHET_LENGTH + ED25519_PUBLIC_KEY_LENGTH\ + + ED25519_SIGNATURE_LENGTH) + +/** init the session keys from the un-base64-ed session keys */ +static size_t _init_group_session_keys( + OlmInboundGroupSession *session, + const uint8_t *key_buf +) { + const uint8_t *ptr = key_buf; + size_t version = *ptr++; + + if (version != SESSION_KEY_VERSION) { + session->last_error = OLM_BAD_SESSION_KEY; + return (size_t)-1; + } + + uint32_t counter = 0; + // Decode counter as a big endian 32-bit number. + for (unsigned i = 0; i < 4; i++) { + counter <<= 8; counter |= *ptr++; + } + + megolm_init(&session->initial_ratchet, ptr, counter); + megolm_init(&session->latest_ratchet, ptr, counter); + + ptr += MEGOLM_RATCHET_LENGTH; + memcpy( + session->signing_key.public_key, ptr, ED25519_PUBLIC_KEY_LENGTH + ); + ptr += ED25519_PUBLIC_KEY_LENGTH; + + if (!_olm_crypto_ed25519_verify( + &session->signing_key, key_buf, ptr - key_buf, ptr + )) { + session->last_error = OLM_BAD_SIGNATURE; + return (size_t)-1; + } + return 0; +} + +size_t olm_init_inbound_group_session( + OlmInboundGroupSession *session, + const uint8_t * session_key, size_t session_key_length +) { + uint8_t key_buf[SESSION_KEY_RAW_LENGTH]; + size_t raw_length = _olm_decode_base64_length(session_key_length); + size_t result; + + if (raw_length == (size_t)-1) { + session->last_error = OLM_INVALID_BASE64; + return (size_t)-1; + } + + if (raw_length != SESSION_KEY_RAW_LENGTH) { + session->last_error = OLM_BAD_SESSION_KEY; + return (size_t)-1; + } + + _olm_decode_base64(session_key, session_key_length, key_buf); + result = _init_group_session_keys(session, key_buf); + _olm_unset(key_buf, SESSION_KEY_RAW_LENGTH); + return result; +} + +static size_t raw_pickle_length( + const OlmInboundGroupSession *session +) { + size_t length = 0; + length += _olm_pickle_uint32_length(PICKLE_VERSION); + length += megolm_pickle_length(&session->initial_ratchet); + length += megolm_pickle_length(&session->latest_ratchet); + length += _olm_pickle_ed25519_public_key_length(&session->signing_key); + return length; +} + +size_t olm_pickle_inbound_group_session_length( + const OlmInboundGroupSession *session +) { + return _olm_enc_output_length(raw_pickle_length(session)); +} + +size_t olm_pickle_inbound_group_session( + OlmInboundGroupSession *session, + void const * key, size_t key_length, + void * pickled, size_t pickled_length +) { + size_t raw_length = raw_pickle_length(session); + uint8_t *pos; + + if (pickled_length < _olm_enc_output_length(raw_length)) { + session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL; + return (size_t)-1; + } + + pos = _olm_enc_output_pos(pickled, raw_length); + pos = _olm_pickle_uint32(pos, PICKLE_VERSION); + pos = megolm_pickle(&session->initial_ratchet, pos); + pos = megolm_pickle(&session->latest_ratchet, pos); + pos = _olm_pickle_ed25519_public_key(pos, &session->signing_key); + + return _olm_enc_output(key, key_length, pickled, raw_length); +} + +size_t olm_unpickle_inbound_group_session( + OlmInboundGroupSession *session, + void const * key, size_t key_length, + void * pickled, size_t pickled_length +) { + const uint8_t *pos; + const uint8_t *end; + uint32_t pickle_version; + + size_t raw_length = _olm_enc_input( + key, key_length, pickled, pickled_length, &(session->last_error) + ); + if (raw_length == (size_t)-1) { + return raw_length; + } + + pos = pickled; + end = pos + raw_length; + pos = _olm_unpickle_uint32(pos, end, &pickle_version); + if (pickle_version != PICKLE_VERSION) { + session->last_error = OLM_UNKNOWN_PICKLE_VERSION; + return (size_t)-1; + } + pos = megolm_unpickle(&session->initial_ratchet, pos, end); + pos = megolm_unpickle(&session->latest_ratchet, pos, end); + pos = _olm_unpickle_ed25519_public_key(pos, end, &session->signing_key); + + if (end != pos) { + /* We had the wrong number of bytes in the input. */ + session->last_error = OLM_CORRUPTED_PICKLE; + return (size_t)-1; + } + + return pickled_length; +} + +/** + * get the max plaintext length in an un-base64-ed message + */ +static size_t _decrypt_max_plaintext_length( + OlmInboundGroupSession *session, + uint8_t * message, size_t message_length +) { + struct _OlmDecodeGroupMessageResults decoded_results; + + _olm_decode_group_message( + message, message_length, + megolm_cipher->ops->mac_length(megolm_cipher), + ED25519_SIGNATURE_LENGTH, + &decoded_results); + + if (decoded_results.version != OLM_PROTOCOL_VERSION) { + session->last_error = OLM_BAD_MESSAGE_VERSION; + return (size_t)-1; + } + + if (!decoded_results.ciphertext) { + session->last_error = OLM_BAD_MESSAGE_FORMAT; + return (size_t)-1; + } + + return megolm_cipher->ops->decrypt_max_plaintext_length( + megolm_cipher, decoded_results.ciphertext_length); +} + +size_t olm_group_decrypt_max_plaintext_length( + OlmInboundGroupSession *session, + uint8_t * message, size_t message_length +) { + size_t raw_length; + + raw_length = _olm_decode_base64(message, message_length, message); + if (raw_length == (size_t)-1) { + session->last_error = OLM_INVALID_BASE64; + return (size_t)-1; + } + + return _decrypt_max_plaintext_length( + session, message, raw_length + ); +} + +/** + * decrypt an un-base64-ed message + */ +static size_t _decrypt( + OlmInboundGroupSession *session, + uint8_t * message, size_t message_length, + uint8_t * plaintext, size_t max_plaintext_length +) { + struct _OlmDecodeGroupMessageResults decoded_results; + size_t max_length, r; + Megolm *megolm; + Megolm tmp_megolm; + + _olm_decode_group_message( + message, message_length, + megolm_cipher->ops->mac_length(megolm_cipher), + ED25519_SIGNATURE_LENGTH, + &decoded_results); + + if (decoded_results.version != OLM_PROTOCOL_VERSION) { + session->last_error = OLM_BAD_MESSAGE_VERSION; + return (size_t)-1; + } + + if (!decoded_results.has_message_index || !decoded_results.ciphertext) { + session->last_error = OLM_BAD_MESSAGE_FORMAT; + return (size_t)-1; + } + + /* verify the signature. We could do this before decoding the message, but + * we allow for the possibility of future protocol versions which use a + * different signing mechanism; we would rather throw "BAD_MESSAGE_VERSION" + * than "BAD_SIGNATURE" in this case. + */ + message_length -= ED25519_SIGNATURE_LENGTH; + r = _olm_crypto_ed25519_verify( + &session->signing_key, + message, message_length, + message + message_length + ); + if (!r) { + session->last_error = OLM_BAD_SIGNATURE; + return (size_t)-1; + } + + max_length = megolm_cipher->ops->decrypt_max_plaintext_length( + megolm_cipher, + decoded_results.ciphertext_length + ); + if (max_plaintext_length < max_length) { + session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL; + return (size_t)-1; + } + + /* pick a megolm instance to use. If we're at or beyond the latest ratchet + * value, use that */ + if ((decoded_results.message_index - session->latest_ratchet.counter) < (1U << 31)) { + megolm = &session->latest_ratchet; + } else if ((decoded_results.message_index - session->initial_ratchet.counter) >= (1U << 31)) { + /* the counter is before our intial ratchet - we can't decode this. */ + session->last_error = OLM_UNKNOWN_MESSAGE_INDEX; + return (size_t)-1; + } else { + /* otherwise, start from the initial megolm. Take a copy so that we + * don't overwrite the initial megolm */ + tmp_megolm = session->initial_ratchet; + megolm = &tmp_megolm; + } + + megolm_advance_to(megolm, decoded_results.message_index); + + /* now try checking the mac, and decrypting */ + r = megolm_cipher->ops->decrypt( + megolm_cipher, + megolm_get_data(megolm), MEGOLM_RATCHET_LENGTH, + message, message_length, + decoded_results.ciphertext, decoded_results.ciphertext_length, + plaintext, max_plaintext_length + ); + + _olm_unset(&tmp_megolm, sizeof(tmp_megolm)); + if (r == (size_t)-1) { + session->last_error = OLM_BAD_MESSAGE_MAC; + return r; + } + + return r; +} + +size_t olm_group_decrypt( + OlmInboundGroupSession *session, + uint8_t * message, size_t message_length, + uint8_t * plaintext, size_t max_plaintext_length +) { + size_t raw_message_length; + + raw_message_length = _olm_decode_base64(message, message_length, message); + if (raw_message_length == (size_t)-1) { + session->last_error = OLM_INVALID_BASE64; + return (size_t)-1; + } + + return _decrypt( + session, message, raw_message_length, + plaintext, max_plaintext_length + ); +} + +size_t olm_inbound_group_session_id_length( + const OlmInboundGroupSession *session +) { + return _olm_encode_base64_length(GROUP_SESSION_ID_LENGTH); +} + +size_t olm_inbound_group_session_id( + OlmInboundGroupSession *session, + uint8_t * id, size_t id_length +) { + if (id_length < olm_inbound_group_session_id_length(session)) { + session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL; + return (size_t)-1; + } + + return _olm_encode_base64( + session->signing_key.public_key, GROUP_SESSION_ID_LENGTH, id + ); +} diff --git a/src/megolm.c b/src/megolm.c new file mode 100644 index 0000000..3395449 --- /dev/null +++ b/src/megolm.c @@ -0,0 +1,150 @@ +/* Copyright 2016 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/megolm.h" + +#include <string.h> + +#include "olm/cipher.h" +#include "olm/crypto.h" +#include "olm/pickle.h" + +static const struct _olm_cipher_aes_sha_256 MEGOLM_CIPHER = + OLM_CIPHER_INIT_AES_SHA_256("MEGOLM_KEYS"); +const struct _olm_cipher *megolm_cipher = OLM_CIPHER_BASE(&MEGOLM_CIPHER); + +/* the seeds used in the HMAC-SHA-256 functions for each part of the ratchet. + */ +#define HASH_KEY_SEED_LENGTH 1 +static uint8_t HASH_KEY_SEEDS[MEGOLM_RATCHET_PARTS][HASH_KEY_SEED_LENGTH] = { + {0x00}, + {0x01}, + {0x02}, + {0x03} +}; + +static void rehash_part( + uint8_t data[MEGOLM_RATCHET_PARTS][MEGOLM_RATCHET_PART_LENGTH], + int rehash_from_part, int rehash_to_part +) { + _olm_crypto_hmac_sha256( + data[rehash_from_part], + MEGOLM_RATCHET_PART_LENGTH, + HASH_KEY_SEEDS[rehash_to_part], HASH_KEY_SEED_LENGTH, + data[rehash_to_part] + ); +} + + + +void megolm_init(Megolm *megolm, uint8_t const *random_data, uint32_t counter) { + megolm->counter = counter; + memcpy(megolm->data, random_data, MEGOLM_RATCHET_LENGTH); +} + +size_t megolm_pickle_length(const Megolm *megolm) { + size_t length = 0; + length += _olm_pickle_bytes_length(megolm_get_data(megolm), MEGOLM_RATCHET_LENGTH); + length += _olm_pickle_uint32_length(megolm->counter); + return length; + +} + +uint8_t * megolm_pickle(const Megolm *megolm, uint8_t *pos) { + pos = _olm_pickle_bytes(pos, megolm_get_data(megolm), MEGOLM_RATCHET_LENGTH); + pos = _olm_pickle_uint32(pos, megolm->counter); + return pos; +} + +const uint8_t * megolm_unpickle(Megolm *megolm, const uint8_t *pos, + const uint8_t *end) { + pos = _olm_unpickle_bytes(pos, end, (uint8_t *)(megolm->data), + MEGOLM_RATCHET_LENGTH); + pos = _olm_unpickle_uint32(pos, end, &megolm->counter); + return pos; +} + +/* simplistic implementation for a single step */ +void megolm_advance(Megolm *megolm) { + uint32_t mask = 0x00FFFFFF; + int h = 0; + int i; + + megolm->counter++; + + /* figure out how much we need to rekey */ + while (h < (int)MEGOLM_RATCHET_PARTS) { + if (!(megolm->counter & mask)) + break; + h++; + mask >>= 8; + } + + /* now update R(h)...R(3) based on R(h) */ + for (i = MEGOLM_RATCHET_PARTS-1; i >= h; i--) { + rehash_part(megolm->data, h, i); + } +} + +void megolm_advance_to(Megolm *megolm, uint32_t advance_to) { + int j; + + /* starting with R0, see if we need to update each part of the hash */ + for (j = 0; j < (int)MEGOLM_RATCHET_PARTS; j++) { + int shift = (MEGOLM_RATCHET_PARTS-j-1) * 8; + uint32_t mask = (~(uint32_t)0) << shift; + int k; + + /* how many times do we need to rehash this part? + * + * '& 0xff' ensures we handle integer wraparound correctly + */ + unsigned int steps = + ((advance_to >> shift) - (megolm->counter >> shift)) & 0xff; + + if (steps == 0) { + /* deal with the edge case where megolm->counter is slightly larger + * than advance_to. This should only happen for R(0), and implies + * that advance_to has wrapped around and we need to advance R(0) + * 256 times. + */ + if (advance_to < megolm->counter) { + steps = 0x100; + } else { + continue; + } + } + + /* for all but the last step, we can just bump R(j) without regard + * to R(j+1)...R(3). + */ + while (steps > 1) { + rehash_part(megolm->data, j, j); + steps --; + } + + /* on the last step we also need to bump R(j+1)...R(3). + * + * (Theoretically, we could skip bumping R(j+2) if we're going to bump + * R(j+1) again, but the code to figure that out is a bit baroque and + * doesn't save us much). + */ + for (k = 3; k >= j; k--) { + rehash_part(megolm->data, j, k); + } + megolm->counter = advance_to & mask; + } +} diff --git a/src/memory.cpp b/src/memory.cpp index 16a4683..20e0683 100644 --- a/src/memory.cpp +++ b/src/memory.cpp @@ -13,7 +13,13 @@ * limitations under the License. */ #include "olm/memory.hh" +#include "olm/memory.h" +void _olm_unset( + void volatile * buffer, size_t buffer_length +) { + olm::unset(buffer, buffer_length); +} void olm::unset( void volatile * buffer, std::size_t buffer_length diff --git a/src/message.cpp b/src/message.cpp index 05f707f..05fe2c7 100644 --- a/src/message.cpp +++ b/src/message.cpp @@ -1,4 +1,4 @@ -/* Copyright 2015 OpenMarket Ltd +/* Copyright 2015-2016 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +14,8 @@ */ #include "olm/message.hh" +#include "olm/memory.hh" + namespace { template<typename T> @@ -131,7 +133,7 @@ static std::uint8_t const * decode( std::uint8_t const * len_start = pos; pos = varint_skip(pos, end); std::size_t len = varint_decode<std::size_t>(len_start, pos); - if (len > end - pos) return end; + if (len > std::size_t(end - pos)) return end; value = pos; value_length = len; pos += len; @@ -152,7 +154,7 @@ static std::uint8_t const * skip_unknown( std::uint8_t const * len_start = pos; pos = varint_skip(pos, end); std::size_t len = varint_decode<std::size_t>(len_start, pos); - if (len > end - pos) return end; + if (len > std::size_t(end - pos)) return end; pos += len; } else { return end; @@ -323,3 +325,74 @@ void olm::decode_one_time_key_message( unknown = pos; } } + + + +static const std::uint8_t GROUP_MESSAGE_INDEX_TAG = 010; +static const std::uint8_t GROUP_CIPHERTEXT_TAG = 022; + +size_t _olm_encode_group_message_length( + uint32_t message_index, + size_t ciphertext_length, + size_t mac_length, + size_t signature_length +) { + size_t length = VERSION_LENGTH; + length += 1 + varint_length(message_index); + length += 1 + varstring_length(ciphertext_length); + length += mac_length; + length += signature_length; + return length; +} + + +size_t _olm_encode_group_message( + uint8_t version, + uint32_t message_index, + size_t ciphertext_length, + uint8_t *output, + uint8_t **ciphertext_ptr +) { + std::uint8_t * pos = output; + + *(pos++) = version; + pos = encode(pos, GROUP_MESSAGE_INDEX_TAG, message_index); + pos = encode(pos, GROUP_CIPHERTEXT_TAG, *ciphertext_ptr, ciphertext_length); + return pos-output; +} + +void _olm_decode_group_message( + const uint8_t *input, size_t input_length, + size_t mac_length, size_t signature_length, + struct _OlmDecodeGroupMessageResults *results +) { + std::uint8_t const * pos = input; + std::size_t trailer_length = mac_length + signature_length; + std::uint8_t const * end = input + input_length - trailer_length; + std::uint8_t const * unknown = nullptr; + + bool has_message_index = false; + results->message_index = 0; + results->ciphertext = nullptr; + results->ciphertext_length = 0; + + if (input_length < trailer_length) return; + results->version = *(pos++); + + while (pos != end) { + pos = decode( + pos, end, GROUP_MESSAGE_INDEX_TAG, + results->message_index, has_message_index + ); + pos = decode( + pos, end, GROUP_CIPHERTEXT_TAG, + results->ciphertext, results->ciphertext_length + ); + if (unknown == pos) { + pos = skip_unknown(pos, end); + } + unknown = pos; + } + + results->has_message_index = (int)has_message_index; +} diff --git a/src/olm.cpp b/src/olm.cpp index 63f3d83..d3af19c 100644 --- a/src/olm.cpp +++ b/src/olm.cpp @@ -12,12 +12,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "olm/olm.hh" +#include "olm/olm.h" #include "olm/session.hh" #include "olm/account.hh" +#include "olm/cipher.h" +#include "olm/pickle_encoding.h" #include "olm/utility.hh" #include "olm/base64.hh" -#include "olm/cipher.hh" #include "olm/memory.hh" #include <new> @@ -57,75 +58,6 @@ static std::uint8_t const * from_c(void const * bytes) { return reinterpret_cast<std::uint8_t const *>(bytes); } -static const std::uint8_t CIPHER_KDF_INFO[] = "Pickle"; - -static const olm::CipherAesSha256 PICKLE_CIPHER( - CIPHER_KDF_INFO, sizeof(CIPHER_KDF_INFO) -1 -); - -std::size_t enc_output_length( - size_t raw_length -) { - std::size_t length = PICKLE_CIPHER.encrypt_ciphertext_length(raw_length); - length += PICKLE_CIPHER.mac_length(); - return olm::encode_base64_length(length); -} - - -std::uint8_t * enc_output_pos( - std::uint8_t * output, - size_t raw_length -) { - std::size_t length = PICKLE_CIPHER.encrypt_ciphertext_length(raw_length); - length += PICKLE_CIPHER.mac_length(); - return output + olm::encode_base64_length(length) - length; -} - -std::size_t enc_output( - std::uint8_t const * key, std::size_t key_length, - std::uint8_t * output, size_t raw_length -) { - std::size_t ciphertext_length = PICKLE_CIPHER.encrypt_ciphertext_length( - raw_length - ); - std::size_t length = ciphertext_length + PICKLE_CIPHER.mac_length(); - std::size_t base64_length = olm::encode_base64_length(length); - std::uint8_t * raw_output = output + base64_length - length; - PICKLE_CIPHER.encrypt( - key, key_length, - raw_output, raw_length, - raw_output, ciphertext_length, - raw_output, length - ); - olm::encode_base64(raw_output, length, output); - return raw_length; -} - -std::size_t enc_input( - std::uint8_t const * key, std::size_t key_length, - std::uint8_t * input, size_t b64_length, - olm::ErrorCode & last_error -) { - std::size_t enc_length = olm::decode_base64_length(b64_length); - if (enc_length == std::size_t(-1)) { - last_error = olm::ErrorCode::INVALID_BASE64; - return std::size_t(-1); - } - olm::decode_base64(input, b64_length, input); - std::size_t raw_length = enc_length - PICKLE_CIPHER.mac_length(); - std::size_t result = PICKLE_CIPHER.decrypt( - key, key_length, - input, enc_length, - input, raw_length, - input, raw_length - ); - if (result == std::size_t(-1)) { - last_error = olm::ErrorCode::BAD_ACCOUNT_KEY; - } - return result; -} - - std::size_t b64_output_length( size_t raw_length ) { @@ -150,36 +82,27 @@ std::size_t b64_output( std::size_t b64_input( std::uint8_t * input, size_t b64_length, - olm::ErrorCode & last_error + OlmErrorCode & last_error ) { std::size_t raw_length = olm::decode_base64_length(b64_length); if (raw_length == std::size_t(-1)) { - last_error = olm::ErrorCode::INVALID_BASE64; + last_error = OlmErrorCode::OLM_INVALID_BASE64; return std::size_t(-1); } olm::decode_base64(input, b64_length, input); return raw_length; } -static const char * ERRORS[11] { - "SUCCESS", - "NOT_ENOUGH_RANDOM", - "OUTPUT_BUFFER_TOO_SMALL", - "BAD_MESSAGE_VERSION", - "BAD_MESSAGE_FORMAT", - "BAD_MESSAGE_MAC", - "BAD_MESSAGE_KEY_ID", - "INVALID_BASE64", - "BAD_ACCOUNT_KEY", - "UNKNOWN_PICKLE_VERSION", - "CORRUPTED_PICKLE", -}; - } // namespace extern "C" { +void olm_get_library_version(uint8_t *major, uint8_t *minor, uint8_t *patch) { + if (major != NULL) *major = OLMLIB_VERSION_MAJOR; + if (minor != NULL) *minor = OLMLIB_VERSION_MINOR; + if (patch != NULL) *patch = OLMLIB_VERSION_PATCH; +} size_t olm_error() { return std::size_t(-1); @@ -189,35 +112,23 @@ size_t olm_error() { const char * olm_account_last_error( OlmAccount * account ) { - unsigned error = unsigned(from_c(account)->last_error); - if (error < sizeof(ERRORS)) { - return ERRORS[error]; - } else { - return "UNKNOWN_ERROR"; - } + auto error = from_c(account)->last_error; + return _olm_error_to_string(error); } const char * olm_session_last_error( OlmSession * session ) { - unsigned error = unsigned(from_c(session)->last_error); - if (error < sizeof(ERRORS)) { - return ERRORS[error]; - } else { - return "UNKNOWN_ERROR"; - } + auto error = from_c(session)->last_error; + return _olm_error_to_string(error); } const char * olm_utility_last_error( OlmUtility * utility ) { - unsigned error = unsigned(from_c(utility)->last_error); - if (error < sizeof(ERRORS)) { - return ERRORS[error]; - } else { - return "UNKNOWN_ERROR"; - } + auto error = from_c(utility)->last_error; + return _olm_error_to_string(error); } size_t olm_account_size() { @@ -293,14 +204,14 @@ size_t olm_clear_utility( size_t olm_pickle_account_length( OlmAccount * account ) { - return enc_output_length(pickle_length(*from_c(account))); + return _olm_enc_output_length(pickle_length(*from_c(account))); } size_t olm_pickle_session_length( OlmSession * session ) { - return enc_output_length(pickle_length(*from_c(session))); + return _olm_enc_output_length(pickle_length(*from_c(session))); } @@ -311,12 +222,12 @@ size_t olm_pickle_account( ) { olm::Account & object = *from_c(account); std::size_t raw_length = pickle_length(object); - if (pickled_length < enc_output_length(raw_length)) { - object.last_error = olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL; + if (pickled_length < _olm_enc_output_length(raw_length)) { + object.last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return size_t(-1); } - pickle(enc_output_pos(from_c(pickled), raw_length), object); - return enc_output(from_c(key), key_length, from_c(pickled), raw_length); + pickle(_olm_enc_output_pos(from_c(pickled), raw_length), object); + return _olm_enc_output(from_c(key), key_length, from_c(pickled), raw_length); } @@ -327,12 +238,12 @@ size_t olm_pickle_session( ) { olm::Session & object = *from_c(session); std::size_t raw_length = pickle_length(object); - if (pickled_length < enc_output_length(raw_length)) { - object.last_error = olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL; + if (pickled_length < _olm_enc_output_length(raw_length)) { + object.last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return size_t(-1); } - pickle(enc_output_pos(from_c(pickled), raw_length), object); - return enc_output(from_c(key), key_length, from_c(pickled), raw_length); + pickle(_olm_enc_output_pos(from_c(pickled), raw_length), object); + return _olm_enc_output(from_c(key), key_length, from_c(pickled), raw_length); } @@ -343,8 +254,8 @@ size_t olm_unpickle_account( ) { olm::Account & object = *from_c(account); std::uint8_t * const pos = from_c(pickled); - std::size_t raw_length = enc_input( - from_c(key), key_length, pos, pickled_length, object.last_error + std::size_t raw_length = _olm_enc_input( + from_c(key), key_length, pos, pickled_length, &object.last_error ); if (raw_length == std::size_t(-1)) { return std::size_t(-1); @@ -355,8 +266,8 @@ size_t olm_unpickle_account( * (pos + raw_length). On error unpickle will return (pos + raw_length + 1). */ if (end != unpickle(pos, end + 1, object)) { - if (object.last_error == olm::ErrorCode::SUCCESS) { - object.last_error = olm::ErrorCode::CORRUPTED_PICKLE; + if (object.last_error == OlmErrorCode::OLM_SUCCESS) { + object.last_error = OlmErrorCode::OLM_CORRUPTED_PICKLE; } return std::size_t(-1); } @@ -371,8 +282,8 @@ size_t olm_unpickle_session( ) { olm::Session & object = *from_c(session); std::uint8_t * const pos = from_c(pickled); - std::size_t raw_length = enc_input( - from_c(key), key_length, pos, pickled_length, object.last_error + std::size_t raw_length = _olm_enc_input( + from_c(key), key_length, pos, pickled_length, &object.last_error ); if (raw_length == std::size_t(-1)) { return std::size_t(-1); @@ -384,8 +295,8 @@ size_t olm_unpickle_session( * (pos + raw_length). On error unpickle will return (pos + raw_length + 1). */ if (end != unpickle(pos, end + 1, object)) { - if (object.last_error == olm::ErrorCode::SUCCESS) { - object.last_error = olm::ErrorCode::CORRUPTED_PICKLE; + if (object.last_error == OlmErrorCode::OLM_SUCCESS) { + object.last_error = OlmErrorCode::OLM_CORRUPTED_PICKLE; } return std::size_t(-1); } @@ -442,7 +353,7 @@ size_t olm_account_sign( std::size_t raw_length = from_c(account)->signature_length(); if (signature_length < b64_output_length(raw_length)) { from_c(account)->last_error = - olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL; + OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } from_c(account)->sign( @@ -525,14 +436,14 @@ size_t olm_create_outbound_session( std::size_t id_key_length = their_identity_key_length; std::size_t ot_key_length = their_one_time_key_length; - if (olm::decode_base64_length(id_key_length) != olm::KEY_LENGTH - || olm::decode_base64_length(ot_key_length) != olm::KEY_LENGTH + if (olm::decode_base64_length(id_key_length) != CURVE25519_KEY_LENGTH + || olm::decode_base64_length(ot_key_length) != CURVE25519_KEY_LENGTH ) { - from_c(session)->last_error = olm::ErrorCode::INVALID_BASE64; + from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64; return std::size_t(-1); } - olm::Curve25519PublicKey identity_key; - olm::Curve25519PublicKey one_time_key; + _olm_curve25519_public_key identity_key; + _olm_curve25519_public_key one_time_key; olm::decode_base64(id_key, id_key_length, identity_key.public_key); olm::decode_base64(ot_key, ot_key_length, one_time_key.public_key); @@ -572,11 +483,11 @@ size_t olm_create_inbound_session_from( std::uint8_t const * id_key = from_c(their_identity_key); std::size_t id_key_length = their_identity_key_length; - if (olm::decode_base64_length(id_key_length) != olm::KEY_LENGTH) { - from_c(session)->last_error = olm::ErrorCode::INVALID_BASE64; + if (olm::decode_base64_length(id_key_length) != CURVE25519_KEY_LENGTH) { + from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64; return std::size_t(-1); } - olm::Curve25519PublicKey identity_key; + _olm_curve25519_public_key identity_key; olm::decode_base64(id_key, id_key_length, identity_key.public_key); std::size_t raw_length = b64_input( @@ -605,7 +516,7 @@ size_t olm_session_id( std::size_t raw_length = from_c(session)->session_id_length(); if (id_length < b64_output_length(raw_length)) { from_c(session)->last_error = - olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL; + OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } std::size_t result = from_c(session)->session_id( @@ -618,6 +529,12 @@ size_t olm_session_id( } +int olm_session_has_received_message( + OlmSession * session +) { + return from_c(session)->received_message; +} + size_t olm_matches_inbound_session( OlmSession * session, void * one_time_key_message, size_t message_length @@ -643,11 +560,11 @@ size_t olm_matches_inbound_session_from( std::uint8_t const * id_key = from_c(their_identity_key); std::size_t id_key_length = their_identity_key_length; - if (olm::decode_base64_length(id_key_length) != olm::KEY_LENGTH) { - from_c(session)->last_error = olm::ErrorCode::INVALID_BASE64; + if (olm::decode_base64_length(id_key_length) != CURVE25519_KEY_LENGTH) { + from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64; return std::size_t(-1); } - olm::Curve25519PublicKey identity_key; + _olm_curve25519_public_key identity_key; olm::decode_base64(id_key, id_key_length, identity_key.public_key); std::size_t raw_length = b64_input( @@ -671,7 +588,7 @@ size_t olm_remove_one_time_keys( from_c(session)->bob_one_time_key ); if (result == std::size_t(-1)) { - from_c(account)->last_error = olm::ErrorCode::BAD_MESSAGE_KEY_ID; + from_c(account)->last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID; } return result; } @@ -712,7 +629,7 @@ size_t olm_encrypt( ); if (message_length < b64_output_length(raw_length)) { from_c(session)->last_error = - olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL; + OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } std::size_t result = from_c(session)->encrypt( @@ -779,7 +696,7 @@ size_t olm_sha256( std::size_t raw_length = from_c(utility)->sha256_length(); if (output_length < b64_output_length(raw_length)) { from_c(utility)->last_error = - olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL; + OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } std::size_t result = from_c(utility)->sha256( @@ -799,11 +716,11 @@ size_t olm_ed25519_verify( void const * message, size_t message_length, void * signature, size_t signature_length ) { - if (olm::decode_base64_length(key_length) != olm::KEY_LENGTH) { - from_c(utility)->last_error = olm::ErrorCode::INVALID_BASE64; + if (olm::decode_base64_length(key_length) != CURVE25519_KEY_LENGTH) { + from_c(utility)->last_error = OlmErrorCode::OLM_INVALID_BASE64; return std::size_t(-1); } - olm::Ed25519PublicKey verify_key; + _olm_ed25519_public_key verify_key; olm::decode_base64(from_c(key), key_length, verify_key.public_key); std::size_t raw_signature_length = b64_input( from_c(signature), signature_length, from_c(utility)->last_error diff --git a/src/outbound_group_session.c b/src/outbound_group_session.c new file mode 100644 index 0000000..4e4561a --- /dev/null +++ b/src/outbound_group_session.c @@ -0,0 +1,361 @@ +/* Copyright 2016 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/outbound_group_session.h" + +#include <string.h> +#include <sys/time.h> + +#include "olm/base64.h" +#include "olm/cipher.h" +#include "olm/crypto.h" +#include "olm/error.h" +#include "olm/megolm.h" +#include "olm/memory.h" +#include "olm/message.h" +#include "olm/pickle.h" +#include "olm/pickle_encoding.h" + +#define OLM_PROTOCOL_VERSION 3 +#define GROUP_SESSION_ID_LENGTH ED25519_PUBLIC_KEY_LENGTH +#define PICKLE_VERSION 1 +#define SESSION_KEY_VERSION 2 + +struct OlmOutboundGroupSession { + /** the Megolm ratchet providing the encryption keys */ + Megolm ratchet; + + /** The ed25519 keypair used for signing the messages */ + struct _olm_ed25519_key_pair signing_key; + + enum OlmErrorCode last_error; +}; + + +size_t olm_outbound_group_session_size() { + return sizeof(OlmOutboundGroupSession); +} + +OlmOutboundGroupSession * olm_outbound_group_session( + void *memory +) { + OlmOutboundGroupSession *session = memory; + olm_clear_outbound_group_session(session); + return session; +} + +const char *olm_outbound_group_session_last_error( + const OlmOutboundGroupSession *session +) { + return _olm_error_to_string(session->last_error); +} + +size_t olm_clear_outbound_group_session( + OlmOutboundGroupSession *session +) { + _olm_unset(session, sizeof(OlmOutboundGroupSession)); + return sizeof(OlmOutboundGroupSession); +} + +static size_t raw_pickle_length( + const OlmOutboundGroupSession *session +) { + size_t length = 0; + length += _olm_pickle_uint32_length(PICKLE_VERSION); + length += megolm_pickle_length(&(session->ratchet)); + length += _olm_pickle_ed25519_key_pair_length(&(session->signing_key)); + return length; +} + +size_t olm_pickle_outbound_group_session_length( + const OlmOutboundGroupSession *session +) { + return _olm_enc_output_length(raw_pickle_length(session)); +} + +size_t olm_pickle_outbound_group_session( + OlmOutboundGroupSession *session, + void const * key, size_t key_length, + void * pickled, size_t pickled_length +) { + size_t raw_length = raw_pickle_length(session); + uint8_t *pos; + + if (pickled_length < _olm_enc_output_length(raw_length)) { + session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL; + return (size_t)-1; + } + + pos = _olm_enc_output_pos(pickled, raw_length); + pos = _olm_pickle_uint32(pos, PICKLE_VERSION); + pos = megolm_pickle(&(session->ratchet), pos); + pos = _olm_pickle_ed25519_key_pair(pos, &(session->signing_key)); + + return _olm_enc_output(key, key_length, pickled, raw_length); +} + +size_t olm_unpickle_outbound_group_session( + OlmOutboundGroupSession *session, + void const * key, size_t key_length, + void * pickled, size_t pickled_length +) { + const uint8_t *pos; + const uint8_t *end; + uint32_t pickle_version; + + size_t raw_length = _olm_enc_input( + key, key_length, pickled, pickled_length, &(session->last_error) + ); + if (raw_length == (size_t)-1) { + return raw_length; + } + + pos = pickled; + end = pos + raw_length; + pos = _olm_unpickle_uint32(pos, end, &pickle_version); + if (pickle_version != PICKLE_VERSION) { + session->last_error = OLM_UNKNOWN_PICKLE_VERSION; + return (size_t)-1; + } + pos = megolm_unpickle(&(session->ratchet), pos, end); + pos = _olm_unpickle_ed25519_key_pair(pos, end, &(session->signing_key)); + + if (end != pos) { + /* We had the wrong number of bytes in the input. */ + session->last_error = OLM_CORRUPTED_PICKLE; + return (size_t)-1; + } + + return pickled_length; +} + + +size_t olm_init_outbound_group_session_random_length( + const OlmOutboundGroupSession *session +) { + /* we need data to initialize the megolm ratchet, plus some more for the + * session id. + */ + return MEGOLM_RATCHET_LENGTH + + ED25519_RANDOM_LENGTH; +} + +size_t olm_init_outbound_group_session( + OlmOutboundGroupSession *session, + uint8_t const * random, size_t random_length +) { + if (random_length < olm_init_outbound_group_session_random_length(session)) { + /* Insufficient random data for new session */ + session->last_error = OLM_NOT_ENOUGH_RANDOM; + return (size_t)-1; + } + + megolm_init(&(session->ratchet), random, 0); + random += MEGOLM_RATCHET_LENGTH; + + _olm_crypto_ed25519_generate_key(random, &(session->signing_key)); + random += ED25519_RANDOM_LENGTH; + + return 0; +} + +static size_t raw_message_length( + OlmOutboundGroupSession *session, + size_t plaintext_length) +{ + size_t ciphertext_length, mac_length; + + ciphertext_length = megolm_cipher->ops->encrypt_ciphertext_length( + megolm_cipher, plaintext_length + ); + + mac_length = megolm_cipher->ops->mac_length(megolm_cipher); + + return _olm_encode_group_message_length( + session->ratchet.counter, + ciphertext_length, mac_length, ED25519_SIGNATURE_LENGTH + ); +} + +size_t olm_group_encrypt_message_length( + OlmOutboundGroupSession *session, + size_t plaintext_length +) { + size_t message_length = raw_message_length(session, plaintext_length); + return _olm_encode_base64_length(message_length); +} + +/** write an un-base64-ed message to the buffer */ +static size_t _encrypt( + OlmOutboundGroupSession *session, uint8_t const * plaintext, size_t plaintext_length, + uint8_t * buffer +) { + size_t ciphertext_length, mac_length, message_length; + size_t result; + uint8_t *ciphertext_ptr; + + ciphertext_length = megolm_cipher->ops->encrypt_ciphertext_length( + megolm_cipher, + plaintext_length + ); + + mac_length = megolm_cipher->ops->mac_length(megolm_cipher); + + /* first we build the message structure, then we encrypt + * the plaintext into it. + */ + message_length = _olm_encode_group_message( + OLM_PROTOCOL_VERSION, + session->ratchet.counter, + ciphertext_length, + buffer, + &ciphertext_ptr); + + message_length += mac_length; + + result = megolm_cipher->ops->encrypt( + megolm_cipher, + megolm_get_data(&(session->ratchet)), MEGOLM_RATCHET_LENGTH, + plaintext, plaintext_length, + ciphertext_ptr, ciphertext_length, + buffer, message_length + ); + + if (result == (size_t)-1) { + return result; + } + + megolm_advance(&(session->ratchet)); + + /* sign the whole thing with the ed25519 key. */ + _olm_crypto_ed25519_sign( + &(session->signing_key), + buffer, message_length, + buffer + message_length + ); + + return result; +} + +size_t olm_group_encrypt( + OlmOutboundGroupSession *session, + uint8_t const * plaintext, size_t plaintext_length, + uint8_t * message, size_t max_message_length +) { + size_t rawmsglen; + size_t result; + uint8_t *message_pos; + + rawmsglen = raw_message_length(session, plaintext_length); + + if (max_message_length < _olm_encode_base64_length(rawmsglen)) { + session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL; + return (size_t)-1; + } + + /* we construct the message at the end of the buffer, so that + * we have room to base64-encode it once we're done. + */ + message_pos = message + _olm_encode_base64_length(rawmsglen) - rawmsglen; + + /* write the message, and encrypt it, at message_pos */ + result = _encrypt(session, plaintext, plaintext_length, message_pos); + if (result == (size_t)-1) { + return result; + } + + /* bas64-encode it */ + return _olm_encode_base64( + message_pos, rawmsglen, message + ); +} + + +size_t olm_outbound_group_session_id_length( + const OlmOutboundGroupSession *session +) { + return _olm_encode_base64_length(GROUP_SESSION_ID_LENGTH); +} + +size_t olm_outbound_group_session_id( + OlmOutboundGroupSession *session, + uint8_t * id, size_t id_length +) { + if (id_length < olm_outbound_group_session_id_length(session)) { + session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL; + return (size_t)-1; + } + + return _olm_encode_base64( + session->signing_key.public_key.public_key, GROUP_SESSION_ID_LENGTH, id + ); +} + +uint32_t olm_outbound_group_session_message_index( + OlmOutboundGroupSession *session +) { + return session->ratchet.counter; +} + +#define SESSION_KEY_RAW_LENGTH \ + (1 + 4 + MEGOLM_RATCHET_LENGTH + ED25519_PUBLIC_KEY_LENGTH\ + + ED25519_SIGNATURE_LENGTH) + +size_t olm_outbound_group_session_key_length( + const OlmOutboundGroupSession *session +) { + return _olm_encode_base64_length(SESSION_KEY_RAW_LENGTH); +} + +size_t olm_outbound_group_session_key( + OlmOutboundGroupSession *session, + uint8_t * key, size_t key_length +) { + uint8_t *raw; + uint8_t *ptr; + size_t encoded_length = olm_outbound_group_session_key_length(session); + + if (key_length < encoded_length) { + session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL; + return (size_t)-1; + } + + /* put the raw data at the end of the output buffer. */ + raw = ptr = key + encoded_length - SESSION_KEY_RAW_LENGTH; + *ptr++ = SESSION_KEY_VERSION; + + uint32_t counter = session->ratchet.counter; + // Encode counter as a big endian 32-bit number. + for (unsigned i = 0; i < 4; i++) { + *ptr++ = 0xFF & (counter >> 24); counter <<= 8; + } + + memcpy(ptr, megolm_get_data(&session->ratchet), MEGOLM_RATCHET_LENGTH); + ptr += MEGOLM_RATCHET_LENGTH; + + memcpy( + ptr, session->signing_key.public_key.public_key, + ED25519_PUBLIC_KEY_LENGTH + ); + ptr += ED25519_PUBLIC_KEY_LENGTH; + + /* sign the whole thing with the ed25519 key. */ + _olm_crypto_ed25519_sign( + &(session->signing_key), + raw, ptr - raw, ptr + ); + + return _olm_encode_base64(raw, SESSION_KEY_RAW_LENGTH, key); +} diff --git a/src/pickle.cpp b/src/pickle.cpp index 25580d2..b6ee4c5 100644 --- a/src/pickle.cpp +++ b/src/pickle.cpp @@ -13,10 +13,65 @@ * limitations under the License. */ #include "olm/pickle.hh" +#include "olm/pickle.h" + +std::uint8_t * olm::pickle( + std::uint8_t * pos, + std::uint32_t value +) { + pos += 4; + for (unsigned i = 4; i--;) { *(--pos) = value; value >>= 8; } + return pos + 4; +} + + +std::uint8_t const * olm::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; +} + +std::uint8_t * olm::pickle( + std::uint8_t * pos, + bool value +) { + *(pos++) = value ? 1 : 0; + return pos; +} + +std::uint8_t const * olm::unpickle( + std::uint8_t const * pos, std::uint8_t const * end, + bool & value +) { + if (pos == end) return end; + value = *(pos++); + return pos; +} + +std::uint8_t * olm::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; +} + +std::uint8_t const * olm::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 olm::pickle_length( - const olm::Curve25519PublicKey & value + const _olm_curve25519_public_key & value ) { return sizeof(value.public_key); } @@ -24,7 +79,7 @@ std::size_t olm::pickle_length( std::uint8_t * olm::pickle( std::uint8_t * pos, - const olm::Curve25519PublicKey & value + const _olm_curve25519_public_key & value ) { pos = olm::pickle_bytes( pos, value.public_key, sizeof(value.public_key) @@ -35,7 +90,7 @@ std::uint8_t * olm::pickle( std::uint8_t const * olm::unpickle( std::uint8_t const * pos, std::uint8_t const * end, - olm::Curve25519PublicKey & value + _olm_curve25519_public_key & value ) { pos = olm::unpickle_bytes( pos, end, value.public_key, sizeof(value.public_key) @@ -46,21 +101,24 @@ std::uint8_t const * olm::unpickle( std::size_t olm::pickle_length( - const olm::Curve25519KeyPair & value + const _olm_curve25519_key_pair & value ) { - return sizeof(value.public_key) + sizeof(value.private_key); + return sizeof(value.public_key.public_key) + + sizeof(value.private_key.private_key); } std::uint8_t * olm::pickle( std::uint8_t * pos, - const olm::Curve25519KeyPair & value + const _olm_curve25519_key_pair & value ) { pos = olm::pickle_bytes( - pos, value.public_key, sizeof(value.public_key) + pos, value.public_key.public_key, + sizeof(value.public_key.public_key) ); pos = olm::pickle_bytes( - pos, value.private_key, sizeof(value.private_key) + pos, value.private_key.private_key, + sizeof(value.private_key.private_key) ); return pos; } @@ -68,77 +126,117 @@ std::uint8_t * olm::pickle( std::uint8_t const * olm::unpickle( std::uint8_t const * pos, std::uint8_t const * end, - olm::Curve25519KeyPair & value + _olm_curve25519_key_pair & value ) { pos = olm::unpickle_bytes( - pos, end, value.public_key, sizeof(value.public_key) + pos, end, value.public_key.public_key, + sizeof(value.public_key.public_key) ); pos = olm::unpickle_bytes( - pos, end, value.private_key, sizeof(value.private_key) + pos, end, value.private_key.private_key, + sizeof(value.private_key.private_key) ); return pos; } -std::size_t olm::pickle_length( - const olm::Ed25519PublicKey & value +////// pickle.h implementations + +std::size_t _olm_pickle_ed25519_public_key_length( + const _olm_ed25519_public_key * value ) { - return sizeof(value.public_key); + return sizeof(value->public_key); } -std::uint8_t * olm::pickle( +std::uint8_t * _olm_pickle_ed25519_public_key( std::uint8_t * pos, - const olm::Ed25519PublicKey & value + const _olm_ed25519_public_key *value ) { pos = olm::pickle_bytes( - pos, value.public_key, sizeof(value.public_key) + pos, value->public_key, sizeof(value->public_key) ); return pos; } -std::uint8_t const * olm::unpickle( +std::uint8_t const * _olm_unpickle_ed25519_public_key( std::uint8_t const * pos, std::uint8_t const * end, - olm::Ed25519PublicKey & value + _olm_ed25519_public_key * value ) { pos = olm::unpickle_bytes( - pos, end, value.public_key, sizeof(value.public_key) + pos, end, value->public_key, sizeof(value->public_key) ); return pos; - } -std::size_t olm::pickle_length( - const olm::Ed25519KeyPair & value +std::size_t _olm_pickle_ed25519_key_pair_length( + const _olm_ed25519_key_pair *value ) { - return sizeof(value.public_key) + sizeof(value.private_key); + return sizeof(value->public_key.public_key) + + sizeof(value->private_key.private_key); } -std::uint8_t * olm::pickle( +std::uint8_t * _olm_pickle_ed25519_key_pair( std::uint8_t * pos, - const olm::Ed25519KeyPair & value + const _olm_ed25519_key_pair *value ) { pos = olm::pickle_bytes( - pos, value.public_key, sizeof(value.public_key) + pos, value->public_key.public_key, + sizeof(value->public_key.public_key) ); pos = olm::pickle_bytes( - pos, value.private_key, sizeof(value.private_key) + pos, value->private_key.private_key, + sizeof(value->private_key.private_key) ); return pos; } -std::uint8_t const * olm::unpickle( +std::uint8_t const * _olm_unpickle_ed25519_key_pair( std::uint8_t const * pos, std::uint8_t const * end, - olm::Ed25519KeyPair & value + _olm_ed25519_key_pair *value ) { pos = olm::unpickle_bytes( - pos, end, value.public_key, sizeof(value.public_key) + pos, end, value->public_key.public_key, + sizeof(value->public_key.public_key) ); pos = olm::unpickle_bytes( - pos, end, value.private_key, sizeof(value.private_key) + pos, end, value->private_key.private_key, + sizeof(value->private_key.private_key) ); return pos; } + +uint8_t * _olm_pickle_uint32(uint8_t * pos, uint32_t value) { + return olm::pickle(pos, value); +} + +uint8_t const * _olm_unpickle_uint32( + uint8_t const * pos, uint8_t const * end, + uint32_t *value +) { + return olm::unpickle(pos, end, *value); +} + +uint8_t * _olm_pickle_bool(uint8_t * pos, int value) { + return olm::pickle(pos, (bool)value); +} + +uint8_t const * _olm_unpickle_bool( + uint8_t const * pos, uint8_t const * end, + int *value +) { + return olm::unpickle(pos, end, *reinterpret_cast<bool *>(value)); +} + +uint8_t * _olm_pickle_bytes(uint8_t * pos, uint8_t const * bytes, + size_t bytes_length) { + return olm::pickle_bytes(pos, bytes, bytes_length); +} + +uint8_t const * _olm_unpickle_bytes(uint8_t const * pos, uint8_t const * end, + uint8_t * bytes, size_t bytes_length) { + return olm::unpickle_bytes(pos, end, bytes, bytes_length); +} diff --git a/src/pickle_encoding.c b/src/pickle_encoding.c new file mode 100644 index 0000000..5d5f8d7 --- /dev/null +++ b/src/pickle_encoding.c @@ -0,0 +1,92 @@ +/* Copyright 2016 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/pickle_encoding.h" + +#include "olm/base64.h" +#include "olm/cipher.h" +#include "olm/olm.h" + +static const struct _olm_cipher_aes_sha_256 PICKLE_CIPHER = + OLM_CIPHER_INIT_AES_SHA_256("Pickle"); + +size_t _olm_enc_output_length( + size_t raw_length +) { + const struct _olm_cipher *cipher = OLM_CIPHER_BASE(&PICKLE_CIPHER); + size_t length = cipher->ops->encrypt_ciphertext_length(cipher, raw_length); + length += cipher->ops->mac_length(cipher); + return _olm_encode_base64_length(length); +} + +uint8_t * _olm_enc_output_pos( + uint8_t * output, + size_t raw_length +) { + const struct _olm_cipher *cipher = OLM_CIPHER_BASE(&PICKLE_CIPHER); + size_t length = cipher->ops->encrypt_ciphertext_length(cipher, raw_length); + length += cipher->ops->mac_length(cipher); + return output + _olm_encode_base64_length(length) - length; +} + +size_t _olm_enc_output( + uint8_t const * key, size_t key_length, + uint8_t * output, size_t raw_length +) { + const struct _olm_cipher *cipher = OLM_CIPHER_BASE(&PICKLE_CIPHER); + size_t ciphertext_length = cipher->ops->encrypt_ciphertext_length( + cipher, raw_length + ); + size_t length = ciphertext_length + cipher->ops->mac_length(cipher); + size_t base64_length = _olm_encode_base64_length(length); + uint8_t * raw_output = output + base64_length - length; + cipher->ops->encrypt( + cipher, + key, key_length, + raw_output, raw_length, + raw_output, ciphertext_length, + raw_output, length + ); + _olm_encode_base64(raw_output, length, output); + return raw_length; +} + + +size_t _olm_enc_input(uint8_t const * key, size_t key_length, + uint8_t * input, size_t b64_length, + enum OlmErrorCode * last_error +) { + size_t enc_length = _olm_decode_base64_length(b64_length); + if (enc_length == (size_t)-1) { + if (last_error) { + *last_error = OLM_INVALID_BASE64; + } + return (size_t)-1; + } + _olm_decode_base64(input, b64_length, input); + const struct _olm_cipher *cipher = OLM_CIPHER_BASE(&PICKLE_CIPHER); + size_t raw_length = enc_length - cipher->ops->mac_length(cipher); + size_t result = cipher->ops->decrypt( + cipher, + key, key_length, + input, enc_length, + input, raw_length, + input, raw_length + ); + if (result == (size_t)-1 && last_error) { + *last_error = OLM_BAD_ACCOUNT_KEY; + } + return result; +} diff --git a/src/ratchet.cpp b/src/ratchet.cpp index 5ef1e56..279c4c0 100644 --- a/src/ratchet.cpp +++ b/src/ratchet.cpp @@ -1,4 +1,4 @@ -/* Copyright 2015 OpenMarket Ltd +/* Copyright 2015, 2016 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,10 +15,9 @@ #include "olm/ratchet.hh" #include "olm/message.hh" #include "olm/memory.hh" -#include "olm/cipher.hh" +#include "olm/cipher.h" #include "olm/pickle.hh" - #include <cstring> namespace { @@ -28,18 +27,30 @@ static const std::uint8_t MESSAGE_KEY_SEED[1] = {0x01}; static const std::uint8_t CHAIN_KEY_SEED[1] = {0x02}; static const std::size_t MAX_MESSAGE_GAP = 2000; + +/** + * Advance the root key, creating a new message chain. + * + * @param root_key previous root key R(n-1) + * @param our_key our new ratchet key T(n) + * @param their_key their most recent ratchet key T(n-1) + * @param info table of constants for the ratchet function + * @param new_root_key[out] returns the new root key R(n) + * @param new_chain_key[out] returns the first chain key in the new chain + * C(n,0) + */ static void create_chain_key( olm::SharedKey const & root_key, - olm::Curve25519KeyPair const & our_key, - olm::Curve25519PublicKey const & their_key, + _olm_curve25519_key_pair const & our_key, + _olm_curve25519_public_key const & their_key, olm::KdfInfo const & info, olm::SharedKey & new_root_key, olm::ChainKey & new_chain_key ) { olm::SharedKey secret; - olm::curve25519_shared_secret(our_key, their_key, secret); - std::uint8_t derived_secrets[2 * olm::KEY_LENGTH]; - olm::hkdf_sha256( + _olm_crypto_curve25519_shared_secret(&our_key, &their_key, secret); + std::uint8_t derived_secrets[2 * olm::OLM_SHARED_KEY_LENGTH]; + _olm_crypto_hkdf_sha256( secret, sizeof(secret), root_key, sizeof(root_key), info.ratchet_info, info.ratchet_info_length, @@ -58,7 +69,7 @@ static void advance_chain_key( olm::ChainKey const & chain_key, olm::ChainKey & new_chain_key ) { - olm::hmac_sha256( + _olm_crypto_hmac_sha256( chain_key.key, sizeof(chain_key.key), CHAIN_KEY_SEED, sizeof(CHAIN_KEY_SEED), new_chain_key.key @@ -70,9 +81,8 @@ static void advance_chain_key( static void create_message_keys( olm::ChainKey const & chain_key, olm::KdfInfo const & info, - olm::MessageKey & message_key -) { - olm::hmac_sha256( + olm::MessageKey & message_key) { + _olm_crypto_hmac_sha256( chain_key.key, sizeof(chain_key.key), MESSAGE_KEY_SEED, sizeof(MESSAGE_KEY_SEED), message_key.key @@ -82,12 +92,13 @@ static void create_message_keys( static std::size_t verify_mac_and_decrypt( - olm::Cipher const & cipher, + _olm_cipher const *cipher, olm::MessageKey const & message_key, olm::MessageReader const & reader, std::uint8_t * plaintext, std::size_t max_plaintext_length ) { - return cipher.decrypt( + return cipher->ops->decrypt( + cipher, message_key.key, sizeof(message_key.key), reader.input, reader.input_length, reader.ciphertext, reader.ciphertext_length, @@ -155,7 +166,6 @@ static std::size_t verify_mac_and_decrypt_for_new_chain( new_chain.ratchet_key, session.kdf_info, new_root_key, new_chain.chain_key ); - std::size_t result = verify_mac_and_decrypt_for_existing_chain( session, new_chain.chain_key, reader, plaintext, max_plaintext_length @@ -170,19 +180,19 @@ static std::size_t verify_mac_and_decrypt_for_new_chain( olm::Ratchet::Ratchet( olm::KdfInfo const & kdf_info, - Cipher const & ratchet_cipher + _olm_cipher const * ratchet_cipher ) : kdf_info(kdf_info), ratchet_cipher(ratchet_cipher), - last_error(olm::ErrorCode::SUCCESS) { + last_error(OlmErrorCode::OLM_SUCCESS) { } void olm::Ratchet::initialise_as_bob( std::uint8_t const * shared_secret, std::size_t shared_secret_length, - olm::Curve25519PublicKey const & their_ratchet_key + _olm_curve25519_public_key const & their_ratchet_key ) { - std::uint8_t derived_secrets[2 * olm::KEY_LENGTH]; - olm::hkdf_sha256( + std::uint8_t derived_secrets[2 * olm::OLM_SHARED_KEY_LENGTH]; + _olm_crypto_hkdf_sha256( shared_secret, shared_secret_length, nullptr, 0, kdf_info.root_info, kdf_info.root_info_length, @@ -200,10 +210,10 @@ void olm::Ratchet::initialise_as_bob( void olm::Ratchet::initialise_as_alice( std::uint8_t const * shared_secret, std::size_t shared_secret_length, - olm::Curve25519KeyPair const & our_ratchet_key + _olm_curve25519_key_pair const & our_ratchet_key ) { - std::uint8_t derived_secrets[2 * olm::KEY_LENGTH]; - olm::hkdf_sha256( + std::uint8_t derived_secrets[2 * olm::OLM_SHARED_KEY_LENGTH]; + _olm_crypto_hkdf_sha256( shared_secret, shared_secret_length, nullptr, 0, kdf_info.root_info, kdf_info.root_info_length, @@ -224,7 +234,7 @@ namespace olm { static std::size_t pickle_length( const olm::SharedKey & value ) { - return olm::KEY_LENGTH; + return olm::OLM_SHARED_KEY_LENGTH; } @@ -232,7 +242,7 @@ static std::uint8_t * pickle( std::uint8_t * pos, const olm::SharedKey & value ) { - return olm::pickle_bytes(pos, value, olm::KEY_LENGTH); + return olm::pickle_bytes(pos, value, olm::OLM_SHARED_KEY_LENGTH); } @@ -240,7 +250,7 @@ static std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, olm::SharedKey & value ) { - return olm::unpickle_bytes(pos, end, value, olm::KEY_LENGTH); + return olm::unpickle_bytes(pos, end, value, olm::OLM_SHARED_KEY_LENGTH); } @@ -349,7 +359,7 @@ std::size_t olm::pickle_length( olm::Ratchet const & value ) { std::size_t length = 0; - length += olm::KEY_LENGTH; + length += olm::OLM_SHARED_KEY_LENGTH; length += olm::pickle_length(value.sender_chain); length += olm::pickle_length(value.receiver_chains); length += olm::pickle_length(value.skipped_message_keys); @@ -370,12 +380,19 @@ std::uint8_t * olm::pickle( std::uint8_t const * olm::unpickle( std::uint8_t const * pos, std::uint8_t const * end, - olm::Ratchet & value + olm::Ratchet & value, + bool includes_chain_index ) { pos = unpickle(pos, end, value.root_key); pos = unpickle(pos, end, value.sender_chain); pos = unpickle(pos, end, value.receiver_chains); pos = unpickle(pos, end, value.skipped_message_keys); + + // pickle v 0x80000001 includes a chain index; pickle v1 does not. + if (includes_chain_index) { + std::uint32_t dummy; + pos = unpickle(pos, end, dummy); + } return pos; } @@ -387,17 +404,18 @@ std::size_t olm::Ratchet::encrypt_output_length( if (!sender_chain.empty()) { counter = sender_chain[0].chain_key.index; } - std::size_t padded = ratchet_cipher.encrypt_ciphertext_length( + std::size_t padded = ratchet_cipher->ops->encrypt_ciphertext_length( + ratchet_cipher, plaintext_length ); return olm::encode_message_length( - counter, olm::KEY_LENGTH, padded, ratchet_cipher.mac_length() + counter, CURVE25519_KEY_LENGTH, padded, ratchet_cipher->ops->mac_length(ratchet_cipher) ); } std::size_t olm::Ratchet::encrypt_random_length() { - return sender_chain.empty() ? olm::KEY_LENGTH : 0; + return sender_chain.empty() ? CURVE25519_RANDOM_LENGTH : 0; } @@ -409,17 +427,17 @@ std::size_t olm::Ratchet::encrypt( std::size_t output_length = encrypt_output_length(plaintext_length); if (random_length < encrypt_random_length()) { - last_error = olm::ErrorCode::NOT_ENOUGH_RANDOM; + last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM; return std::size_t(-1); } if (max_output_length < output_length) { - last_error = olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL; + last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } if (sender_chain.empty()) { sender_chain.insert(); - olm::curve25519_generate_key(random, sender_chain[0].ratchet_key); + _olm_crypto_curve25519_generate_key(random, &sender_chain[0].ratchet_key); create_chain_key( root_key, sender_chain[0].ratchet_key, @@ -433,22 +451,26 @@ std::size_t olm::Ratchet::encrypt( create_message_keys(sender_chain[0].chain_key, kdf_info, keys); advance_chain_key(sender_chain[0].chain_key, sender_chain[0].chain_key); - std::size_t ciphertext_length = ratchet_cipher.encrypt_ciphertext_length( + std::size_t ciphertext_length = ratchet_cipher->ops->encrypt_ciphertext_length( + ratchet_cipher, plaintext_length ); std::uint32_t counter = keys.index; - Curve25519PublicKey const & ratchet_key = sender_chain[0].ratchet_key; + _olm_curve25519_public_key const & ratchet_key = + sender_chain[0].ratchet_key.public_key; olm::MessageWriter writer; olm::encode_message( - writer, PROTOCOL_VERSION, counter, olm::KEY_LENGTH, ciphertext_length, + writer, PROTOCOL_VERSION, counter, CURVE25519_KEY_LENGTH, + ciphertext_length, output ); olm::store_array(writer.ratchet_key, ratchet_key.public_key); - ratchet_cipher.encrypt( + ratchet_cipher->ops->encrypt( + ratchet_cipher, keys.key, sizeof(keys.key), plaintext, plaintext_length, writer.ciphertext, ciphertext_length, @@ -465,15 +487,17 @@ std::size_t olm::Ratchet::decrypt_max_plaintext_length( ) { olm::MessageReader reader; olm::decode_message( - reader, input, input_length, ratchet_cipher.mac_length() + reader, input, input_length, + ratchet_cipher->ops->mac_length(ratchet_cipher) ); if (!reader.ciphertext) { - last_error = olm::ErrorCode::BAD_MESSAGE_FORMAT; + last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT; return std::size_t(-1); } - return ratchet_cipher.decrypt_max_plaintext_length(reader.ciphertext_length); + return ratchet_cipher->ops->decrypt_max_plaintext_length( + ratchet_cipher, reader.ciphertext_length); } @@ -483,38 +507,41 @@ std::size_t olm::Ratchet::decrypt( ) { olm::MessageReader reader; olm::decode_message( - reader, input, input_length, ratchet_cipher.mac_length() + reader, input, input_length, + ratchet_cipher->ops->mac_length(ratchet_cipher) ); if (reader.version != PROTOCOL_VERSION) { - last_error = olm::ErrorCode::BAD_MESSAGE_VERSION; + last_error = OlmErrorCode::OLM_BAD_MESSAGE_VERSION; return std::size_t(-1); } if (!reader.has_counter || !reader.ratchet_key || !reader.ciphertext) { - last_error = olm::ErrorCode::BAD_MESSAGE_FORMAT; + last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT; return std::size_t(-1); } - std::size_t max_length = ratchet_cipher.decrypt_max_plaintext_length( + std::size_t max_length = ratchet_cipher->ops->decrypt_max_plaintext_length( + ratchet_cipher, reader.ciphertext_length ); if (max_plaintext_length < max_length) { - last_error = olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL; + last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } - if (reader.ratchet_key_length != olm::KEY_LENGTH) { - last_error = olm::ErrorCode::BAD_MESSAGE_FORMAT; + if (reader.ratchet_key_length != CURVE25519_KEY_LENGTH) { + last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT; return std::size_t(-1); } ReceiverChain * chain = nullptr; + for (olm::ReceiverChain & receiver_chain : receiver_chains) { if (0 == std::memcmp( receiver_chain.ratchet_key.public_key, reader.ratchet_key, - olm::KEY_LENGTH + CURVE25519_KEY_LENGTH )) { chain = &receiver_chain; break; @@ -534,7 +561,7 @@ std::size_t olm::Ratchet::decrypt( if (reader.counter == skipped.message_key.index && 0 == std::memcmp( skipped.ratchet_key.public_key, reader.ratchet_key, - olm::KEY_LENGTH + CURVE25519_KEY_LENGTH ) ) { /* Found the key for this message. Check the MAC. */ @@ -555,26 +582,32 @@ std::size_t olm::Ratchet::decrypt( } } else { result = verify_mac_and_decrypt_for_existing_chain( - *this, chain->chain_key, reader, plaintext, max_plaintext_length + *this, chain->chain_key, + reader, plaintext, max_plaintext_length ); } if (result == std::size_t(-1)) { - last_error = olm::ErrorCode::BAD_MESSAGE_MAC; + last_error = OlmErrorCode::OLM_BAD_MESSAGE_MAC; return std::size_t(-1); } if (!chain) { - /* They have started using a new empheral ratchet key. + /* They have started using a new ephemeral ratchet key. * We need to derive a new set of chain keys. * We can discard our previous empheral ratchet key. * We will generate a new key when we send the next message. */ + chain = receiver_chains.insert(); olm::load_array(chain->ratchet_key.public_key, reader.ratchet_key); + + // TODO: we've already done this once, in + // verify_mac_and_decrypt_for_new_chain(). we could reuse the result. create_chain_key( root_key, sender_chain[0].ratchet_key, chain->ratchet_key, kdf_info, root_key, chain->chain_key ); + olm::unset(sender_chain[0]); sender_chain.erase(sender_chain.begin()); } diff --git a/src/session.cpp b/src/session.cpp index 71c80e4..f1bc5a7 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -1,4 +1,4 @@ -/* Copyright 2015 OpenMarket Ltd +/* Copyright 2015, 2016 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,8 +13,8 @@ * limitations under the License. */ #include "olm/session.hh" -#include "olm/cipher.hh" -#include "olm/crypto.hh" +#include "olm/cipher.h" +#include "olm/crypto.h" #include "olm/account.hh" #include "olm/memory.hh" #include "olm/message.hh" @@ -30,64 +30,64 @@ static const std::uint8_t ROOT_KDF_INFO[] = "OLM_ROOT"; static const std::uint8_t RATCHET_KDF_INFO[] = "OLM_RATCHET"; static const std::uint8_t CIPHER_KDF_INFO[] = "OLM_KEYS"; -static const olm::CipherAesSha256 OLM_CIPHER( - CIPHER_KDF_INFO, sizeof(CIPHER_KDF_INFO) -1 -); - static const olm::KdfInfo OLM_KDF_INFO = { ROOT_KDF_INFO, sizeof(ROOT_KDF_INFO) - 1, RATCHET_KDF_INFO, sizeof(RATCHET_KDF_INFO) - 1 }; +static const struct _olm_cipher_aes_sha_256 OLM_CIPHER = + OLM_CIPHER_INIT_AES_SHA_256(CIPHER_KDF_INFO); + } // namespace olm::Session::Session( -) : ratchet(OLM_KDF_INFO, OLM_CIPHER), - last_error(olm::ErrorCode::SUCCESS), +) : ratchet(OLM_KDF_INFO, OLM_CIPHER_BASE(&OLM_CIPHER)), + last_error(OlmErrorCode::OLM_SUCCESS), received_message(false) { } std::size_t olm::Session::new_outbound_session_random_length() { - return olm::KEY_LENGTH * 2; + return CURVE25519_RANDOM_LENGTH * 2; } std::size_t olm::Session::new_outbound_session( olm::Account const & local_account, - olm::Curve25519PublicKey const & identity_key, - olm::Curve25519PublicKey const & one_time_key, + _olm_curve25519_public_key const & identity_key, + _olm_curve25519_public_key const & one_time_key, std::uint8_t const * random, std::size_t random_length ) { if (random_length < new_outbound_session_random_length()) { - last_error = olm::ErrorCode::NOT_ENOUGH_RANDOM; + last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM; return std::size_t(-1); } - olm::Curve25519KeyPair base_key; - olm::curve25519_generate_key(random, base_key); + _olm_curve25519_key_pair base_key; + _olm_crypto_curve25519_generate_key(random, &base_key); - olm::Curve25519KeyPair ratchet_key; - olm::curve25519_generate_key(random + olm::KEY_LENGTH, ratchet_key); + _olm_curve25519_key_pair ratchet_key; + _olm_crypto_curve25519_generate_key(random + CURVE25519_RANDOM_LENGTH, &ratchet_key); - olm::Curve25519KeyPair const & alice_identity_key_pair = ( + _olm_curve25519_key_pair const & alice_identity_key_pair = ( local_account.identity_keys.curve25519_key ); received_message = false; - alice_identity_key = alice_identity_key_pair; - alice_base_key = base_key; + alice_identity_key = alice_identity_key_pair.public_key; + alice_base_key = base_key.public_key; bob_one_time_key = one_time_key; - std::uint8_t secret[3 * olm::KEY_LENGTH]; + // Calculate the shared secret S via triple DH + std::uint8_t secret[3 * CURVE25519_SHARED_SECRET_LENGTH]; std::uint8_t * pos = secret; - olm::curve25519_shared_secret(alice_identity_key_pair, one_time_key, pos); - pos += olm::KEY_LENGTH; - olm::curve25519_shared_secret(base_key, identity_key, pos); - pos += olm::KEY_LENGTH; - olm::curve25519_shared_secret(base_key, one_time_key, pos); + _olm_crypto_curve25519_shared_secret(&alice_identity_key_pair, &one_time_key, pos); + pos += CURVE25519_SHARED_SECRET_LENGTH; + _olm_crypto_curve25519_shared_secret(&base_key, &identity_key, pos); + pos += CURVE25519_SHARED_SECRET_LENGTH; + _olm_crypto_curve25519_shared_secret(&base_key, &one_time_key, pos); ratchet.initialise_as_alice(secret, sizeof(secret), ratchet_key); @@ -106,13 +106,13 @@ static bool check_message_fields( bool ok = true; ok = ok && (have_their_identity_key || reader.identity_key); if (reader.identity_key) { - ok = ok && reader.identity_key_length == olm::KEY_LENGTH; + ok = ok && reader.identity_key_length == CURVE25519_KEY_LENGTH; } ok = ok && reader.message; ok = ok && reader.base_key; - ok = ok && reader.base_key_length == olm::KEY_LENGTH; + ok = ok && reader.base_key_length == CURVE25519_KEY_LENGTH; ok = ok && reader.one_time_key; - ok = ok && reader.one_time_key_length == olm::KEY_LENGTH; + ok = ok && reader.one_time_key_length == CURVE25519_KEY_LENGTH; return ok; } @@ -121,43 +121,44 @@ static bool check_message_fields( std::size_t olm::Session::new_inbound_session( olm::Account & local_account, - olm::Curve25519PublicKey const * their_identity_key, + _olm_curve25519_public_key const * their_identity_key, std::uint8_t const * one_time_key_message, std::size_t message_length ) { olm::PreKeyMessageReader reader; decode_one_time_key_message(reader, one_time_key_message, message_length); if (!check_message_fields(reader, their_identity_key)) { - last_error = olm::ErrorCode::BAD_MESSAGE_FORMAT; + last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT; return std::size_t(-1); } if (reader.identity_key && their_identity_key) { bool same = 0 == std::memcmp( - their_identity_key->public_key, reader.identity_key, olm::KEY_LENGTH + their_identity_key->public_key, reader.identity_key, CURVE25519_KEY_LENGTH ); if (!same) { - last_error = olm::ErrorCode::BAD_MESSAGE_KEY_ID; + last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID; return std::size_t(-1); } } + olm::load_array(alice_identity_key.public_key, reader.identity_key); + olm::load_array(alice_base_key.public_key, reader.base_key); + olm::load_array(bob_one_time_key.public_key, reader.one_time_key); + olm::MessageReader message_reader; decode_message( message_reader, reader.message, reader.message_length, - ratchet.ratchet_cipher.mac_length() + ratchet.ratchet_cipher->ops->mac_length(ratchet.ratchet_cipher) ); if (!message_reader.ratchet_key - || message_reader.ratchet_key_length != olm::KEY_LENGTH) { - last_error = olm::ErrorCode::BAD_MESSAGE_FORMAT; + || message_reader.ratchet_key_length != CURVE25519_KEY_LENGTH) { + last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT; return std::size_t(-1); } - olm::load_array(alice_identity_key.public_key, reader.identity_key); - olm::load_array(alice_base_key.public_key, reader.base_key); - olm::load_array(bob_one_time_key.public_key, reader.one_time_key); - olm::Curve25519PublicKey ratchet_key; + _olm_curve25519_public_key ratchet_key; olm::load_array(ratchet_key.public_key, message_reader.ratchet_key); olm::OneTimeKey const * our_one_time_key = local_account.lookup_key( @@ -165,32 +166,34 @@ std::size_t olm::Session::new_inbound_session( ); if (!our_one_time_key) { - last_error = olm::ErrorCode::BAD_MESSAGE_KEY_ID; + last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID; return std::size_t(-1); } - olm::Curve25519KeyPair const & bob_identity_key = ( + _olm_curve25519_key_pair const & bob_identity_key = ( local_account.identity_keys.curve25519_key ); - olm::Curve25519KeyPair const & bob_one_time_key = our_one_time_key->key; + _olm_curve25519_key_pair const & bob_one_time_key = our_one_time_key->key; - std::uint8_t secret[olm::KEY_LENGTH * 3]; + // Calculate the shared secret S via triple DH + std::uint8_t secret[CURVE25519_SHARED_SECRET_LENGTH * 3]; std::uint8_t * pos = secret; - olm::curve25519_shared_secret(bob_one_time_key, alice_identity_key, pos); - pos += olm::KEY_LENGTH; - olm::curve25519_shared_secret(bob_identity_key, alice_base_key, pos); - pos += olm::KEY_LENGTH; - olm::curve25519_shared_secret(bob_one_time_key, alice_base_key, pos); + _olm_crypto_curve25519_shared_secret(&bob_one_time_key, &alice_identity_key, pos); + pos += CURVE25519_SHARED_SECRET_LENGTH; + _olm_crypto_curve25519_shared_secret(&bob_identity_key, &alice_base_key, pos); + pos += CURVE25519_SHARED_SECRET_LENGTH; + _olm_crypto_curve25519_shared_secret(&bob_one_time_key, &alice_base_key, pos); ratchet.initialise_as_bob(secret, sizeof(secret), ratchet_key); olm::unset(secret); + return std::size_t(0); } std::size_t olm::Session::session_id_length() { - return olm::SHA256_OUTPUT_LENGTH; + return SHA256_OUTPUT_LENGTH; } @@ -198,21 +201,21 @@ std::size_t olm::Session::session_id( std::uint8_t * id, std::size_t id_length ) { if (id_length < session_id_length()) { - last_error = olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL; + last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } - std::uint8_t tmp[olm::KEY_LENGTH * 3]; + std::uint8_t tmp[CURVE25519_KEY_LENGTH * 3]; std::uint8_t * pos = tmp; pos = olm::store_array(pos, alice_identity_key.public_key); pos = olm::store_array(pos, alice_base_key.public_key); pos = olm::store_array(pos, bob_one_time_key.public_key); - olm::sha256(tmp, sizeof(tmp), id); + _olm_crypto_sha256(tmp, sizeof(tmp), id); return session_id_length(); } bool olm::Session::matches_inbound_session( - olm::Curve25519PublicKey const * their_identity_key, + _olm_curve25519_public_key const * their_identity_key, std::uint8_t const * one_time_key_message, std::size_t message_length ) { olm::PreKeyMessageReader reader; @@ -225,20 +228,20 @@ bool olm::Session::matches_inbound_session( bool same = true; if (reader.identity_key) { same = same && 0 == std::memcmp( - reader.identity_key, alice_identity_key.public_key, olm::KEY_LENGTH + reader.identity_key, alice_identity_key.public_key, CURVE25519_KEY_LENGTH ); } if (their_identity_key) { same = same && 0 == std::memcmp( their_identity_key->public_key, alice_identity_key.public_key, - olm::KEY_LENGTH + CURVE25519_KEY_LENGTH ); } same = same && 0 == std::memcmp( - reader.base_key, alice_base_key.public_key, olm::KEY_LENGTH + reader.base_key, alice_base_key.public_key, CURVE25519_KEY_LENGTH ); same = same && 0 == std::memcmp( - reader.one_time_key, bob_one_time_key.public_key, olm::KEY_LENGTH + reader.one_time_key, bob_one_time_key.public_key, CURVE25519_KEY_LENGTH ); return same; } @@ -265,9 +268,9 @@ std::size_t olm::Session::encrypt_message_length( } return encode_one_time_key_message_length( - olm::KEY_LENGTH, - olm::KEY_LENGTH, - olm::KEY_LENGTH, + CURVE25519_KEY_LENGTH, + CURVE25519_KEY_LENGTH, + CURVE25519_KEY_LENGTH, message_length ); } @@ -284,7 +287,7 @@ std::size_t olm::Session::encrypt( std::uint8_t * message, std::size_t message_length ) { if (message_length < encrypt_message_length(plaintext_length)) { - last_error = olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL; + last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } std::uint8_t * message_body; @@ -299,9 +302,9 @@ std::size_t olm::Session::encrypt( encode_one_time_key_message( writer, PROTOCOL_VERSION, - olm::KEY_LENGTH, - olm::KEY_LENGTH, - olm::KEY_LENGTH, + CURVE25519_KEY_LENGTH, + CURVE25519_KEY_LENGTH, + CURVE25519_KEY_LENGTH, message_body_length, message ); @@ -319,8 +322,10 @@ std::size_t olm::Session::encrypt( if (result == std::size_t(-1)) { last_error = ratchet.last_error; - ratchet.last_error = olm::ErrorCode::SUCCESS; + ratchet.last_error = OlmErrorCode::OLM_SUCCESS; + return result; } + return result; } @@ -338,7 +343,7 @@ std::size_t olm::Session::decrypt_max_plaintext_length( olm::PreKeyMessageReader reader; decode_one_time_key_message(reader, message, message_length); if (!reader.message) { - last_error = olm::ErrorCode::BAD_MESSAGE_FORMAT; + last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT; return std::size_t(-1); } message_body = reader.message; @@ -351,7 +356,7 @@ std::size_t olm::Session::decrypt_max_plaintext_length( if (result == std::size_t(-1)) { last_error = ratchet.last_error; - ratchet.last_error = olm::ErrorCode::SUCCESS; + ratchet.last_error = OlmErrorCode::OLM_SUCCESS; } return result; } @@ -371,7 +376,7 @@ std::size_t olm::Session::decrypt( olm::PreKeyMessageReader reader; decode_one_time_key_message(reader, message, message_length); if (!reader.message) { - last_error = olm::ErrorCode::BAD_MESSAGE_FORMAT; + last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT; return std::size_t(-1); } message_body = reader.message; @@ -384,14 +389,17 @@ std::size_t olm::Session::decrypt( if (result == std::size_t(-1)) { last_error = ratchet.last_error; - ratchet.last_error = olm::ErrorCode::SUCCESS; - } else { - received_message = true; + ratchet.last_error = OlmErrorCode::OLM_SUCCESS; + return result; } + + received_message = true; return result; } namespace { +// the master branch writes pickle version 1; the logging_enabled branch writes +// 0x80000001. static const std::uint32_t SESSION_PICKLE_VERSION = 1; } @@ -429,15 +437,26 @@ std::uint8_t const * olm::unpickle( ) { uint32_t pickle_version; pos = olm::unpickle(pos, end, pickle_version); - if (pickle_version != SESSION_PICKLE_VERSION) { - value.last_error = olm::ErrorCode::UNKNOWN_PICKLE_VERSION; - return end; + + bool includes_chain_index; + switch (pickle_version) { + case 1: + includes_chain_index = false; + break; + + case 0x80000001UL: + includes_chain_index = true; + break; + + default: + value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION; + return end; } + pos = olm::unpickle(pos, end, value.received_message); pos = olm::unpickle(pos, end, value.alice_identity_key); pos = olm::unpickle(pos, end, value.alice_base_key); pos = olm::unpickle(pos, end, value.bob_one_time_key); - pos = olm::unpickle(pos, end, value.ratchet); + pos = olm::unpickle(pos, end, value.ratchet, includes_chain_index); return pos; } - diff --git a/src/utility.cpp b/src/utility.cpp index bc51cff..e9688de 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -14,16 +14,16 @@ */ #include "olm/utility.hh" -#include "olm/crypto.hh" +#include "olm/crypto.h" olm::Utility::Utility( -) : last_error(olm::ErrorCode::SUCCESS) { +) : last_error(OlmErrorCode::OLM_SUCCESS) { } size_t olm::Utility::sha256_length() { - return olm::SHA256_OUTPUT_LENGTH; + return SHA256_OUTPUT_LENGTH; } @@ -32,25 +32,25 @@ size_t olm::Utility::sha256( std::uint8_t * output, std::size_t output_length ) { if (output_length < sha256_length()) { - last_error = olm::ErrorCode::OUTPUT_BUFFER_TOO_SMALL; + last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } - olm::sha256(input, input_length, output); - return olm::SHA256_OUTPUT_LENGTH; + _olm_crypto_sha256(input, input_length, output); + return SHA256_OUTPUT_LENGTH; } size_t olm::Utility::ed25519_verify( - Ed25519PublicKey const & key, + _olm_ed25519_public_key const & key, std::uint8_t const * message, std::size_t message_length, std::uint8_t const * signature, std::size_t signature_length ) { - if (signature_length < olm::SIGNATURE_LENGTH) { - last_error = olm::ErrorCode::BAD_MESSAGE_MAC; + if (signature_length < ED25519_SIGNATURE_LENGTH) { + last_error = OlmErrorCode::OLM_BAD_MESSAGE_MAC; return std::size_t(-1); } - if (!olm::ed25519_verify(key, message, message_length, signature)) { - last_error = olm::ErrorCode::BAD_MESSAGE_MAC; + if (!_olm_crypto_ed25519_verify(&key, message, message_length, signature)) { + last_error = OlmErrorCode::OLM_BAD_MESSAGE_MAC; return std::size_t(-1); } return std::size_t(0); |