]> gitweb.ps.run Git - matrix_esp_thesis/blobdiff - ext/olm/src/outbound_group_session.c
add dependencies to repo
[matrix_esp_thesis] / ext / olm / src / outbound_group_session.c
diff --git a/ext/olm/src/outbound_group_session.c b/ext/olm/src/outbound_group_session.c
new file mode 100644 (file)
index 0000000..cbbba9c
--- /dev/null
@@ -0,0 +1,390 @@
+/* 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 "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(void) {
+    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);
+}
+
+enum OlmErrorCode olm_outbound_group_session_last_error_code(
+    const OlmOutboundGroupSession *session
+) {
+    return 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;
+    }
+
+#ifndef OLM_FUZZING
+    pos = _olm_enc_output_pos(pickled, raw_length);
+#else
+    pos = pickled;
+#endif
+
+    pos = _olm_pickle_uint32(pos, PICKLE_VERSION);
+    pos = megolm_pickle(&(session->ratchet), pos);
+    pos = _olm_pickle_ed25519_key_pair(pos, &(session->signing_key));
+
+#ifndef OLM_FUZZING
+    return _olm_enc_output(key, key_length, pickled, raw_length);
+#else
+    return raw_length;
+#endif
+}
+
+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;
+
+#ifndef OLM_FUZZING
+    size_t raw_length = _olm_enc_input(
+        key, key_length, pickled, pickled_length, &(session->last_error)
+    );
+#else
+    size_t raw_length = pickled_length;
+#endif
+
+    if (raw_length == (size_t)-1) {
+        return raw_length;
+    }
+
+    pos = pickled;
+    end = pos + raw_length;
+
+    pos = _olm_unpickle_uint32(pos, end, &pickle_version);
+    FAIL_ON_CORRUPTED_PICKLE(pos, session);
+
+    if (pickle_version != PICKLE_VERSION) {
+        session->last_error = OLM_UNKNOWN_PICKLE_VERSION;
+        return (size_t)-1;
+    }
+
+    pos = megolm_unpickle(&(session->ratchet), pos, end);
+    FAIL_ON_CORRUPTED_PICKLE(pos, session);
+
+    pos = _olm_unpickle_ed25519_key_pair(pos, end, &(session->signing_key));
+    FAIL_ON_CORRUPTED_PICKLE(pos, session);
+
+    if (pos != end) {
+        /* Input was longer than expected. */
+        session->last_error = OLM_PICKLE_EXTRA_DATA;
+        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 *random, size_t random_length
+) {
+    const uint8_t *random_ptr = random;
+
+    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_ptr, 0);
+    random_ptr += MEGOLM_RATCHET_LENGTH;
+
+    _olm_crypto_ed25519_generate_key(random_ptr, &(session->signing_key));
+    random_ptr += ED25519_RANDOM_LENGTH;
+
+    _olm_unset(random, 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);
+}