olm/src/session.cpp

320 lines
8.8 KiB
C++

#include "axolotl/session.hh"
#include "axolotl/cipher.hh"
#include "axolotl/crypto.hh"
#include "axolotl/account.hh"
#include "axolotl/memory.hh"
#include "axolotl/message.hh"
#include <cstring>
namespace {
static const std::size_t KEY_LENGTH = 32;
static const std::uint8_t PROTOCOL_VERSION = 0x3;
static const std::uint8_t ROOT_KDF_INFO[] = "AXOLOTL_ROOT";
static const std::uint8_t RATCHET_KDF_INFO[] = "AXOLOTL_RATCHET";
static const std::uint8_t CIPHER_KDF_INFO[] = "AXOLOTL_KEYS";
static const axolotl::CipherAesSha256 AXOLOTL_CIPHER(
CIPHER_KDF_INFO, sizeof(CIPHER_KDF_INFO) -1
);
static const axolotl::KdfInfo AXOLOTL_KDF_INFO = {
ROOT_KDF_INFO, sizeof(ROOT_KDF_INFO) - 1,
RATCHET_KDF_INFO, sizeof(RATCHET_KDF_INFO) - 1
};
} // namespace
axolotl::Session::Session(
) : ratchet(AXOLOTL_KDF_INFO, AXOLOTL_CIPHER),
last_error(axolotl::ErrorCode::SUCCESS),
received_message(false),
bob_one_time_key_id(0) {
}
std::size_t axolotl::Session::new_outbound_session_random_length() {
return KEY_LENGTH;
}
std::size_t axolotl::Session::new_outbound_session(
axolotl::Account const & local_account,
axolotl::Curve25519PublicKey const & identity_key,
axolotl::RemoteKey const & one_time_key,
std::uint8_t const * random, std::size_t random_length
) {
if (random_length < new_outbound_session_random_length()) {
last_error = axolotl::ErrorCode::NOT_ENOUGH_RANDOM;
return std::size_t(-1);
}
Curve25519KeyPair base_key;
axolotl::generate_key(random, base_key);
received_message = false;
alice_identity_key.id = local_account.identity_key.id;
alice_identity_key.key = local_account.identity_key.key;
alice_base_key = base_key;
bob_one_time_key_id = one_time_key.id;
std::uint8_t shared_secret[160];
std::memset(shared_secret, 0xFF, 32);
axolotl::curve25519_shared_secret(
);
axolotl::curve25519_shared_secret(
base_key, identity_key, shared_secret + 64
);
axolotl::curve25519_shared_secret(
);
axolotl::curve25519_shared_secret(
base_key, one_time_key.key, shared_secret + 128
);
axolotl::unset(base_key);
axolotl::unset(shared_secret);
return std::size_t(0);
}
namespace {
bool check_message_fields(
axolotl::PreKeyMessageReader & reader
) {
bool ok = true;
ok = ok && reader.identity_key;
ok = ok && reader.identity_key_length == KEY_LENGTH;
ok = ok && reader.message;
ok = ok && reader.base_key;
ok = ok && reader.base_key_length == KEY_LENGTH;
ok = ok && reader.has_one_time_key_id;
ok = ok && reader.has_registration_id;
return ok;
}
} // namespace
std::size_t axolotl::Session::new_inbound_session(
axolotl::Account & local_account,
std::uint8_t const * one_time_key_message, std::size_t message_length
) {
axolotl::PreKeyMessageReader reader;
decode_one_time_key_message(reader, one_time_key_message, message_length);
if (!check_message_fields(reader)) {
last_error = axolotl::ErrorCode::BAD_MESSAGE_FORMAT;
return std::size_t(-1);
}
alice_identity_key.id = reader.registration_id;
std::memcpy(alice_identity_key.key.public_key, reader.identity_key, 32);
std::memcpy(alice_base_key.public_key, reader.base_key, 32);
bob_one_time_key_id = reader.one_time_key_id;
axolotl::LocalKey const * bob_one_time_key = local_account.lookup_key(
bob_one_time_key_id
);
last_error = axolotl::ErrorCode::BAD_MESSAGE_KEY_ID;
return std::size_t(-1);
}
std::uint8_t shared_secret[160];
std::memset(shared_secret, 0xFF, 32);
axolotl::curve25519_shared_secret(
);
axolotl::curve25519_shared_secret(
local_account.identity_key.key, alice_base_key, shared_secret + 64
);
axolotl::curve25519_shared_secret(
);
axolotl::curve25519_shared_secret(
bob_one_time_key->key, alice_base_key, shared_secret + 128
);
return std::size_t(0);
}
bool axolotl::Session::matches_inbound_session(
std::uint8_t const * one_time_key_message, std::size_t message_length
) {
axolotl::PreKeyMessageReader reader;
decode_one_time_key_message(reader, one_time_key_message, message_length);
if (!check_message_fields(reader)) {
return false;
}
bool same = true;
same = same && 0 == std::memcmp(
reader.identity_key, alice_identity_key.key.public_key, KEY_LENGTH
);
same = same && 0 == std::memcmp(
reader.base_key, alice_base_key.public_key, KEY_LENGTH
);
same = same && reader.one_time_key_id == bob_one_time_key_id;
same = same && reader.registration_id == alice_identity_key.id;
return same;
}
axolotl::MessageType axolotl::Session::encrypt_message_type() {
if (received_message) {
return axolotl::MessageType::MESSAGE;
} else {
return axolotl::MessageType::PRE_KEY_MESSAGE;
}
}
std::size_t axolotl::Session::encrypt_message_length(
std::size_t plaintext_length
) {
std::size_t message_length = ratchet.encrypt_output_length(
plaintext_length
);
if (received_message) {
return message_length;
}
return encode_one_time_key_message_length(
alice_identity_key.id,
bob_one_time_key_id,
KEY_LENGTH,
KEY_LENGTH,
message_length
);
}
std::size_t axolotl::Session::encrypt_random_length() {
return ratchet.encrypt_random_length();
}
std::size_t axolotl::Session::encrypt(
std::uint8_t const * plaintext, std::size_t plaintext_length,
std::uint8_t const * random, std::size_t random_length,
std::uint8_t * message, std::size_t message_length
) {
if (message_length < encrypt_message_length(plaintext_length)) {
last_error = axolotl::ErrorCode::OUTPUT_BUFFER_TOO_SMALL;
return std::size_t(-1);
}
std::uint8_t * message_body;
std::size_t message_body_length = ratchet.encrypt_output_length(
plaintext_length
);
if (received_message) {
message_body = message;
} else {
axolotl::PreKeyMessageWriter writer;
encode_one_time_key_message(
writer,
PROTOCOL_VERSION,
alice_identity_key.id,
bob_one_time_key_id,
KEY_LENGTH,
KEY_LENGTH,
message_body_length,
message
);
std::memcpy(
writer.identity_key, alice_identity_key.key.public_key, KEY_LENGTH
);
std::memcpy(
writer.base_key, alice_base_key.public_key, KEY_LENGTH
);
message_body = writer.message;
}
std::size_t result = ratchet.encrypt(
plaintext, plaintext_length,
random, random_length,
message_body, message_body_length
);
if (result == std::size_t(-1)) {
last_error = ratchet.last_error;
ratchet.last_error = axolotl::ErrorCode::SUCCESS;
}
return result;
}
std::size_t axolotl::Session::decrypt_max_plaintext_length(
MessageType message_type,
std::uint8_t const * message, std::size_t message_length
) {
std::uint8_t const * message_body;
std::size_t message_body_length;
if (message_type == axolotl::MessageType::MESSAGE) {
message_body = message;
message_body_length = message_length;
} else {
axolotl::PreKeyMessageReader reader;
decode_one_time_key_message(reader, message, message_length);
if (!reader.message) {
last_error = axolotl::ErrorCode::BAD_MESSAGE_FORMAT;
return std::size_t(-1);
}
message_body = reader.message;
message_body_length = reader.message_length;
}
std::size_t result = ratchet.decrypt_max_plaintext_length(
message_body, message_body_length
);
if (result == std::size_t(-1)) {
last_error = ratchet.last_error;
ratchet.last_error = axolotl::ErrorCode::SUCCESS;
}
return result;
}
std::size_t axolotl::Session::decrypt(
axolotl::MessageType message_type,
std::uint8_t const * message, std::size_t message_length,
std::uint8_t * plaintext, std::size_t max_plaintext_length
) {
std::uint8_t const * message_body;
std::size_t message_body_length;
if (message_type == axolotl::MessageType::MESSAGE) {
message_body = message;
message_body_length = message_length;
} else {
axolotl::PreKeyMessageReader reader;
decode_one_time_key_message(reader, message, message_length);
if (!reader.message) {
last_error = axolotl::ErrorCode::BAD_MESSAGE_FORMAT;
return std::size_t(-1);
}
message_body = reader.message;
message_body_length = reader.message_length;
}
std::size_t result = ratchet.decrypt(
message_body, message_body_length, plaintext, max_plaintext_length
);
if (result == std::size_t(-1)) {
last_error = ratchet.last_error;
ratchet.last_error = axolotl::ErrorCode::SUCCESS;
}
return result;
}