aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Haines <mark.haines@matrix.org>2015-06-15 17:49:20 +0100
committerMark Haines <mark.haines@matrix.org>2015-06-15 17:49:20 +0100
commit4abead9e9eaf9e508babde5620644ce5e00e1fb4 (patch)
treede99fe2d6c075e4f21fa9ba410955f1297dbbfab
parent7cdde735604de3d1be54fc535df3985b89eebd0a (diff)
Add c interface which wraps the cpp classes
-rw-r--r--include/axolotl/axolotl.hh163
-rw-r--r--src/axolotl.cpp548
2 files changed, 711 insertions, 0 deletions
diff --git a/include/axolotl/axolotl.hh b/include/axolotl/axolotl.hh
new file mode 100644
index 0000000..09276a9
--- /dev/null
+++ b/include/axolotl/axolotl.hh
@@ -0,0 +1,163 @@
+#ifndef AXOLOTL_HH_
+#define AXOLOTL_HH_
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static const size_t AXOLOTL_MESSAGE_TYPE_PRE_KEY = 0;
+static const size_t AXOLOTL_MESSAGE_TYPE_MESSAGE = 1;
+
+struct AxolotlAccount;
+struct AxolotlSession;
+
+size_t axolotl_account_size();
+
+size_t axolotl_session_size();
+
+AxolotlAccount * axolotl_account(
+ void * memory
+);
+
+AxolotlSession * axolotl_session(
+ void * memory
+);
+
+size_t axolotl_error();
+
+const char * axolotl_account_last_error(
+ AxolotlSession * account
+);
+
+const char * axolotl_session_last_error(
+ AxolotlSession * session
+);
+
+size_t axolotl_pickle_account_length(
+ AxolotlAccount * account
+);
+
+size_t axolotl_pickle_session_length(
+ AxolotlSession * session
+);
+
+size_t axolotl_pickle_account(
+ AxolotlAccount * account,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+);
+
+size_t axolotl_pickle_session(
+ AxolotlSession * session,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+);
+
+size_t axolotl_unpickle_account(
+ AxolotlAccount * account,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+);
+
+size_t axolotl_unpickle_session(
+ AxolotlSession * session,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+);
+
+size_t axolotl_create_account_random_length(
+ AxolotlAccount * account
+);
+
+size_t axolotl_create_account(
+ AxolotlAccount * account,
+ void const * random, size_t random_length
+);
+
+size_t axolotl_account_identity_keys_length(
+ AxolotlAccount * account
+);
+
+size_t axolotl_account_identity_keys(
+ AxolotlAccount * account,
+ void * identity_keys, size_t identity_key_length
+);
+
+size_t axolotl_account_one_time_keys_length(
+ AxolotlAccount * account
+);
+
+size_t axolotl_account_one_time_keys(
+ AxolotlAccount * account,
+ void * identity_keys, size_t identity_key_length
+);
+
+/* TODO: Add methods for marking keys as used, generating new keys, and
+ * tracking which keys have been uploaded to the central servers */
+
+size_t axolotl_create_outbound_session_random_length(
+ AxolotlSession * session
+);
+
+size_t axolotl_create_outbound_session(
+ AxolotlSession * session,
+ AxolotlAccount * account,
+ void const * their_identity_key, size_t their_identity_key_length,
+ unsigned their_one_time_key_id,
+ void const * their_one_time_key, size_t their_one_time_key_length,
+ void const * random, size_t random_length
+);
+
+size_t axolotl_create_inbound_session(
+ AxolotlSession * session,
+ AxolotlAccount * account,
+ void * one_time_key_message, size_t message_length
+);
+
+size_t axolotl_matches_inbound_session(
+ AxolotlSession * session,
+ void * one_time_key_message, size_t message_length
+);
+
+size_t axolotl_encrypt_message_type(
+ AxolotlSession * session
+);
+
+size_t axolotl_encrypt_random_length(
+ AxolotlSession * session
+);
+
+size_t axolotl_encrypt_message_length(
+ AxolotlSession * session,
+ size_t plaintext_length
+);
+
+size_t axolotl_encrypt(
+ AxolotlSession * session,
+ void const * plaintext, size_t plaintext_length,
+ void const * random, size_t random_length,
+ void * message, size_t message_length
+);
+
+size_t axolotl_decrypt_max_plaintext_length(
+ AxolotlSession * session,
+ size_t message_type,
+ void * message, size_t message_length
+);
+
+size_t axolotl_decrypt(
+ AxolotlSession * session,
+ size_t message_type,
+ void * message, size_t message_length,
+ void * plaintext, size_t max_plaintext_length
+);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AXOLOTL_HH_ */
diff --git a/src/axolotl.cpp b/src/axolotl.cpp
new file mode 100644
index 0000000..e11e2b0
--- /dev/null
+++ b/src/axolotl.cpp
@@ -0,0 +1,548 @@
+#include "axolotl/axolotl.hh"
+#include "axolotl/session.hh"
+#include "axolotl/account.hh"
+#include "axolotl/base64.hh"
+#include "axolotl/cipher.hh"
+
+#include <new>
+#include <cstring>
+
+namespace {
+
+static AxolotlAccount * to_c(axolotl::Account * account) {
+ return reinterpret_cast<AxolotlAccount *>(account);
+}
+
+static AxolotlSession * to_c(axolotl::Session * account) {
+ return reinterpret_cast<AxolotlSession *>(account);
+}
+
+static axolotl::Account * from_c(AxolotlAccount * account) {
+ return reinterpret_cast<axolotl::Account *>(account);
+}
+
+static axolotl::Session * from_c(AxolotlSession * account) {
+ return reinterpret_cast<axolotl::Session *>(account);
+}
+
+static std::uint8_t * from_c(void * bytes) {
+ return reinterpret_cast<std::uint8_t *>(bytes);
+}
+
+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 axolotl::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 axolotl::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 + axolotl::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 = axolotl::encode_base64_length(length);
+ std::uint8_t * raw_output = output + base64_length - length;
+ length -= PICKLE_CIPHER.mac_length();
+ PICKLE_CIPHER.encrypt(
+ key, key_length,
+ raw_output, raw_length,
+ raw_output, ciphertext_length,
+ raw_output, length
+ );
+ axolotl::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,
+ axolotl::ErrorCode & last_error
+) {
+ std::size_t enc_length = axolotl::decode_base64_length(b64_length);
+ if (enc_length == std::size_t(-1)) {
+ last_error = axolotl::ErrorCode::INVALID_BASE64;
+ return std::size_t(-1);
+ }
+ axolotl::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 = axolotl::ErrorCode::BAD_ACCOUNT_KEY;
+ }
+ return result;
+}
+
+
+std::size_t b64_output_length(
+ size_t raw_length
+) {
+ return axolotl::encode_base64_length(raw_length);
+}
+
+std::uint8_t * b64_output_pos(
+ std::uint8_t * output,
+ size_t raw_length
+) {
+ return output + axolotl::encode_base64_length(raw_length) - raw_length;
+}
+
+std::size_t b64_output(
+ std::uint8_t * output, size_t raw_length
+) {
+ std::size_t base64_length = axolotl::encode_base64_length(raw_length);
+ std::uint8_t * raw_output = output + base64_length - raw_length;
+ axolotl::encode_base64(raw_output, raw_length, output);
+ return base64_length;
+}
+
+std::size_t b64_input(
+ std::uint8_t * input, size_t b64_length,
+ axolotl::ErrorCode & last_error
+) {
+ std::size_t raw_length = axolotl::decode_base64_length(b64_length);
+ if (raw_length == std::size_t(-1)) {
+ last_error = axolotl::ErrorCode::INVALID_BASE64;
+ return std::size_t(-1);
+ }
+ axolotl::decode_base64(input, b64_length, input);
+ return raw_length;
+}
+
+const char * errors[9] {
+ "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",
+};
+
+} // namespace
+
+
+extern "C" {
+
+
+size_t axolotl_error() {
+ return std::size_t(-1);
+}
+
+
+const char * axolotl_account_last_error(
+ AxolotlSession * account
+) {
+ unsigned error = unsigned(from_c(account)->last_error);
+ if (error < 9) {
+ return errors[error];
+ } else {
+ return "UNKNOWN_ERROR";
+ }
+}
+
+
+const char * axolotl_session_last_error(
+ AxolotlSession * session
+) {
+ unsigned error = unsigned(from_c(session)->last_error);
+ if (error < 9) {
+ return errors[error];
+ } else {
+ return "UNKNOWN_ERROR";
+ }
+}
+
+
+size_t axolotl_account_size() {
+ return sizeof(axolotl::Account);
+}
+
+
+size_t axolotl_session_size() {
+ return sizeof(axolotl::Session);
+}
+
+
+AxolotlAccount * axolotl_account(
+ void * memory
+) {
+ return to_c(new(memory) axolotl::Account());
+}
+
+
+AxolotlSession * axolotl_session(
+ void * memory
+) {
+ return to_c(new(memory) axolotl::Session());
+}
+
+
+size_t axolotl_pickle_account_length(
+ AxolotlAccount * account
+) {
+ return enc_output_length(pickle_length(*from_c(account)));
+}
+
+
+size_t axolotl_pickle_session_length(
+ AxolotlSession * session
+) {
+ return enc_output_length(pickle_length(*from_c(session)));
+}
+
+
+size_t axolotl_pickle_account(
+ AxolotlAccount * account,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+) {
+ axolotl::Account & object = *from_c(account);
+ std::size_t raw_length = pickle_length(object);
+ if (pickled_length < enc_output_length(raw_length)) {
+ object.last_error = axolotl::ErrorCode::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);
+}
+
+
+size_t axolotl_pickle_session(
+ AxolotlSession * session,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+) {
+ axolotl::Session & object = *from_c(session);
+ std::size_t raw_length = pickle_length(object);
+ if (pickled_length < enc_output_length(raw_length)) {
+ object.last_error = axolotl::ErrorCode::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);
+}
+
+
+size_t axolotl_unpickle_account(
+ AxolotlAccount * account,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+) {
+ axolotl::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
+ );
+ if (raw_length == std::size_t(-1)) {
+ return std::size_t(-1);
+ }
+ std::uint8_t * const end = pos + raw_length;
+ unpickle(pos, end, object);
+ return pickled_length;
+}
+
+
+size_t axolotl_unpickle_session(
+ AxolotlSession * session,
+ void const * key, size_t key_length,
+ void * pickled, size_t pickled_length
+) {
+ axolotl::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
+ );
+ if (raw_length == std::size_t(-1)) {
+ return std::size_t(-1);
+ }
+ std::uint8_t * const end = pos + raw_length;
+ unpickle(pos, end, object);
+ return pickled_length;
+}
+
+
+size_t axolotl_create_account_random_length(
+ AxolotlAccount * account
+) {
+ return from_c(account)->new_account_random_length();
+}
+
+
+size_t axolotl_create_account(
+ AxolotlAccount * account,
+ void const * random, size_t random_length
+) {
+ return from_c(account)->new_account(from_c(random), random_length);
+}
+
+namespace {
+
+static const std::size_t OUTPUT_KEY_LENGTH = 2 + 10 + 2 +
+ axolotl::encode_base64_length(32) + 3;
+
+void output_key(
+ axolotl::LocalKey const & key,
+ std::uint8_t sep,
+ std::uint8_t * output
+) {
+ output[0] = sep;
+ output[1] = '[';
+ std::memset(output + 2, ' ', 10);
+ uint32_t value = key.id;
+ uint8_t * number = output + 11;
+ *number = '0' + value % 10;
+ value /= 10;
+ while (value) {
+ *(--number) = '0' + value % 10;
+ value /= 10;
+ }
+ output[12] = ',';
+ output[13] = '"';
+ axolotl::encode_base64(key.key.public_key, 32, output + 14);
+ output[OUTPUT_KEY_LENGTH - 3] = '"';
+ output[OUTPUT_KEY_LENGTH - 2] = ']';
+ output[OUTPUT_KEY_LENGTH - 1] = '\n';
+}
+
+} // namespace
+
+
+size_t axolotl_account_identity_keys_length(
+ AxolotlAccount * account
+) {
+ return OUTPUT_KEY_LENGTH * 2 + 1;
+}
+
+
+size_t axolotl_account_identity_keys(
+ AxolotlAccount * account,
+ void * identity_keys, size_t identity_key_length
+) {
+ std::size_t length = OUTPUT_KEY_LENGTH * 2 + 1;
+ if (identity_key_length < length) {
+ from_c(account)->last_error =
+ axolotl::ErrorCode::OUTPUT_BUFFER_TOO_SMALL;
+ return size_t(-1);
+ }
+ std::uint8_t * output = from_c(identity_keys);
+ output_key(from_c(account)->identity_key, '[', output);
+ output += OUTPUT_KEY_LENGTH;
+ output += OUTPUT_KEY_LENGTH;
+ output[0] = ']';
+ return length;
+}
+
+
+size_t axolotl_account_one_time_keys_length(
+ AxolotlAccount * account
+) {
+ size_t count = from_c(account)->one_time_keys.size();
+ return OUTPUT_KEY_LENGTH * (count + 1) + 1;
+}
+
+
+size_t axolotl_account_one_time_keys(
+ AxolotlAccount * account,
+ void * identity_keys, size_t identity_key_length
+) {
+ std::size_t length = axolotl_account_one_time_keys_length(account);
+ if (identity_key_length < length) {
+ from_c(account)->last_error =
+ axolotl::ErrorCode::OUTPUT_BUFFER_TOO_SMALL;
+ return size_t(-1);
+ }
+ std::uint8_t * output = from_c(identity_keys);
+ output_key(from_c(account)->last_resort_one_time_key, '[', output);
+ output += OUTPUT_KEY_LENGTH;
+ for (auto const & key : from_c(account)->one_time_keys) {
+ output_key(key, ',', output);
+ output += OUTPUT_KEY_LENGTH;
+ }
+ output[0] = ']';
+ return length;
+}
+
+
+size_t axolotl_create_outbound_session(
+ AxolotlSession * session,
+ AxolotlAccount * account,
+ void const * their_identity_key, size_t their_identity_key_length,
+ unsigned their_one_time_key_id,
+ void const * their_one_time_key, size_t their_one_time_key_length,
+ void const * random, size_t random_length
+) {
+ if (axolotl::decode_base64_length(their_identity_key_length) != 32
+ || axolotl::decode_base64_length(their_one_time_key_length) != 32
+ ) {
+ from_c(session)->last_error = axolotl::ErrorCode::INVALID_BASE64;
+ return std::size_t(-1);
+ }
+ axolotl::Curve25519PublicKey identity_key;
+ axolotl::RemoteKey one_time_key;
+
+ axolotl::decode_base64(
+ from_c(their_identity_key), their_identity_key_length,
+ identity_key.public_key
+ );
+ one_time_key.id = their_one_time_key_id;
+ axolotl::decode_base64(
+ from_c(their_one_time_key), their_one_time_key_length,
+ one_time_key.key.public_key
+ );
+
+ return from_c(session)->new_outbound_session(
+ *from_c(account), identity_key, one_time_key,
+ from_c(random), random_length
+ );
+}
+
+
+size_t axolotl_create_inbound_session(
+ AxolotlSession * session,
+ AxolotlAccount * account,
+ void * one_time_key_message, size_t message_length
+) {
+ std::size_t raw_length = b64_input(
+ from_c(one_time_key_message), message_length, from_c(session)->last_error
+ );
+ if (raw_length == std::size_t(-1)) {
+ return std::size_t(-1);
+ }
+ return from_c(session)->new_inbound_session(
+ *from_c(account), from_c(one_time_key_message), raw_length
+ );
+}
+
+
+size_t axolotl_matches_inbound_session(
+ AxolotlSession * session,
+ void * one_time_key_message, size_t message_length
+) {
+ std::size_t raw_length = b64_input(
+ from_c(one_time_key_message), message_length, from_c(session)->last_error
+ );
+ if (raw_length == std::size_t(-1)) {
+ return std::size_t(-1);
+ }
+ bool matches = from_c(session)->matches_inbound_session(
+ from_c(one_time_key_message), raw_length
+ );
+ return matches ? 1 : 0;
+}
+
+
+size_t axolotl_encrypt_message_type(
+ AxolotlSession * session
+) {
+ return size_t(from_c(session)->encrypt_message_type());
+}
+
+
+size_t axolotl_encrypt_random_length(
+ AxolotlSession * session
+) {
+ return from_c(session)->encrypt_random_length();
+}
+
+
+size_t axolotl_encrypt_message_length(
+ AxolotlSession * session,
+ size_t plaintext_length
+) {
+ return b64_output_length(
+ from_c(session)->encrypt_message_length(plaintext_length)
+ );
+}
+
+
+size_t axolotl_encrypt(
+ AxolotlSession * session,
+ void const * plaintext, size_t plaintext_length,
+ void const * random, size_t random_length,
+ void * message, size_t message_length
+) {
+ std::size_t raw_length = from_c(session)->encrypt_message_length(
+ plaintext_length
+ );
+ if (message_length < raw_length) {
+ from_c(session)->last_error =
+ axolotl::ErrorCode::OUTPUT_BUFFER_TOO_SMALL;
+ return std::size_t(-1);
+ }
+ from_c(session)->encrypt(
+ from_c(plaintext), plaintext_length,
+ from_c(random), random_length,
+ b64_output_pos(from_c(message), raw_length), raw_length
+ );
+ return b64_output(from_c(message), raw_length);
+}
+
+
+size_t axolotl_decrypt_max_plaintext_length(
+ AxolotlSession * session,
+ size_t message_type,
+ void * message, size_t message_length
+) {
+ std::size_t raw_length = b64_input(
+ from_c(message), message_length, from_c(session)->last_error
+ );
+ if (raw_length == std::size_t(-1)) {
+ return std::size_t(-1);
+ }
+ return from_c(session)->decrypt_max_plaintext_length(
+ axolotl::MessageType(message_type), from_c(message), raw_length
+ );
+}
+
+
+size_t axolotl_decrypt(
+ AxolotlSession * session,
+ size_t message_type,
+ void * message, size_t message_length,
+ void * plaintext, size_t max_plaintext_length
+) {
+ std::size_t raw_length = b64_input(
+ from_c(message), message_length, from_c(session)->last_error
+ );
+ if (raw_length == std::size_t(-1)) {
+ return std::size_t(-1);
+ }
+ return from_c(session)->decrypt(
+ axolotl::MessageType(message_type), from_c(message), raw_length,
+ from_c(plaintext), max_plaintext_length
+ );
+}
+
+}