aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/account.cpp75
-rw-r--r--src/base64.cpp39
-rw-r--r--src/cipher.cpp115
-rw-r--r--src/crypto.cpp175
-rw-r--r--src/ed25519.c (renamed from src/libs.cpp)9
-rw-r--r--src/error.c43
-rw-r--r--src/inbound_group_session.c386
-rw-r--r--src/megolm.c150
-rw-r--r--src/memory.cpp6
-rw-r--r--src/message.cpp79
-rw-r--r--src/olm.cpp201
-rw-r--r--src/outbound_group_session.c361
-rw-r--r--src/pickle.cpp162
-rw-r--r--src/pickle_encoding.c92
-rw-r--r--src/ratchet.cpp139
-rw-r--r--src/session.cpp173
-rw-r--r--src/utility.cpp22
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);