Merge branch 'master' into logging_enabled

This commit is contained in:
Richard van der Hoff 2016-09-22 16:04:57 +01:00
commit 9001d3520a
46 changed files with 1175 additions and 751 deletions

6
.gitignore vendored
View file

@ -1,2 +1,6 @@
build /build
/CHANGELOG.html
/docs/olm.html
/olm-*.tgz /olm-*.tgz
/README.html
/tracing/README.html

69
CHANGELOG.rst Normal file
View file

@ -0,0 +1,69 @@
Changes in `1.3.0 <http://matrix.org/git/olm/commit/?h=1.3.0>`_
===============================================================
The release updates the group session identifier to avoid collisions.
The group sessions are now identified by their ed25519 public key.
These changes alter the pickle format of outbound group sessions, attempting
to unpickle an outbound group session created with a previous version of olm
will give ``OLM_CORRUPTED_PICKLE``. Inbound sessions are unaffected.
This release alters the format of group session_key messages to include the
ratchet counter. The session_key messages are now self signed with their
ed25519 key. No attempt was made to preserve backwards-compatibility.
Attempting to send session_keys between old and new versions will give
``OLM_BAD_SESSION_KEY``.
Changes in `1.2.0 <http://matrix.org/git/olm/commit/?h=1.2.0>`_
===============================================================
This release updates the implementation of group session communications, to
include Ed25519 signatures on group messages, to ensure that participants in
group sessions cannot masquerade as each other.
These changes necessitate changes to the pickle format of inbound and outbound
group sessions, as well as the session_keys exchanged between them. No attempt
has been made to preserve backwards-compatibility:
* Attempting to restore old pickles will give ``OLM_CORRUPTED_PICKLE``.
* Attempting to send session_keys between old and new versions will give
``OLM_BAD_SESSION_KEY``.
* Attempting to send messages between old and new versions will give one of a
number of errors.
There were also a number of implementation changes made as part of this
release, aimed at making the codebase more consistent, and to help with the
implementation of the group message signatures.
Changes in `1.1.0 <http://matrix.org/git/olm/commit/?h=1.1.0>`_
===============================================================
This release includes a fix to a bug which caused Ed25519 keypairs to be
generated and used insecurely. Any Ed25519 keys generated by libolm 1.0.0
or earlier should be considered compromised.
The fix necessitates a change to the format of the OlmAccount pickle; since
existing OlmAccounts should in any case be considered compromised (as above),
the library refuses to load them, returning OLM_BAD_LEGACY_ACCOUNT_PICKLE.
Changes in `1.0.0 <http://matrix.org/git/olm/commit/?h=1.0.0>`_
===============================================================
This release includes a fix to a bug which had the potential to leak sensitive
data to the application: see
https://github.com/vector-im/vector-web/issues/1719. Users of pre-1.x.x
versions of the Olm library should upgrade. Our thanks to `Dmitry Luyciv
<https://github.com/dluciv>`_ for bringing our attention to the bug.
Other changes since 0.1.0:
* *Experimental* implementation of the primitives for group sessions. This
implementation has not yet been used in an application and developers are
advised not to rely on its stability.
* Replace custom build scripts with a Makefile.
* Include the major version number in the soname of libolm.so (credit to
Emmanuel Gil Peyrot).

View file

@ -1,5 +1,10 @@
#!/usr/bin/make -f #!/usr/bin/make -f
MAJOR := 1
MINOR := 3
PATCH := 0
VERSION := $(MAJOR).$(MINOR).$(PATCH)
PREFIX ?= /usr/local
BUILD_DIR := build BUILD_DIR := build
RELEASE_OPTIMIZE_FLAGS ?= -g -O3 RELEASE_OPTIMIZE_FLAGS ?= -g -O3
DEBUG_OPTIMIZE_FLAGS ?= -g -O0 DEBUG_OPTIMIZE_FLAGS ?= -g -O0
@ -9,8 +14,9 @@ CC = gcc
EMCC = emcc EMCC = emcc
AFL_CC = afl-gcc AFL_CC = afl-gcc
AFL_CXX = afl-g++ AFL_CXX = afl-g++
RELEASE_TARGET := $(BUILD_DIR)/libolm.so
DEBUG_TARGET := $(BUILD_DIR)/libolm_debug.so RELEASE_TARGET := $(BUILD_DIR)/libolm.so.$(VERSION)
DEBUG_TARGET := $(BUILD_DIR)/libolm_debug.so.$(VERSION)
JS_TARGET := javascript/olm.js JS_TARGET := javascript/olm.js
JS_EXPORTED_FUNCTIONS := javascript/exported_functions.json JS_EXPORTED_FUNCTIONS := javascript/exported_functions.json
@ -37,8 +43,15 @@ JS_PRE := $(wildcard javascript/*pre.js)
JS_POST := javascript/olm_outbound_group_session.js \ JS_POST := javascript/olm_outbound_group_session.js \
javascript/olm_inbound_group_session.js \ javascript/olm_inbound_group_session.js \
javascript/olm_post.js javascript/olm_post.js
DOCS := tracing/README.html \
docs/olm.html \
README.html \
CHANGELOG.html
CPPFLAGS += -Iinclude -Ilib \
-DOLMLIB_VERSION_MAJOR=$(MAJOR) -DOLMLIB_VERSION_MINOR=$(MINOR) \
-DOLMLIB_VERSION_PATCH=$(PATCH)
CPPFLAGS += -Iinclude -Ilib
# we rely on <stdint.h>, which was introduced in C99 # we rely on <stdint.h>, which was introduced in C99
CFLAGS += -Wall -Werror -std=c99 -fPIC CFLAGS += -Wall -Werror -std=c99 -fPIC
CXXFLAGS += -Wall -Werror -std=c++11 -fPIC CXXFLAGS += -Wall -Werror -std=c++11 -fPIC
@ -92,16 +105,20 @@ lib: $(RELEASE_TARGET)
$(RELEASE_TARGET): $(RELEASE_OBJECTS) $(RELEASE_TARGET): $(RELEASE_OBJECTS)
$(CXX) $(LDFLAGS) --shared -fPIC \ $(CXX) $(LDFLAGS) --shared -fPIC \
-Wl,-soname,libolm.so.$(MAJOR) \
-Wl,--version-script,version_script.ver \ -Wl,--version-script,version_script.ver \
$(OUTPUT_OPTION) $(RELEASE_OBJECTS) $(OUTPUT_OPTION) $(RELEASE_OBJECTS)
ln -sf libolm.so.$(VERSION) $(BUILD_DIR)/libolm.so.$(MAJOR)
debug: $(DEBUG_TARGET) debug: $(DEBUG_TARGET)
.PHONY: debug .PHONY: debug
$(DEBUG_TARGET): $(DEBUG_OBJECTS) $(DEBUG_TARGET): $(DEBUG_OBJECTS)
$(CXX) $(LDFLAGS) --shared -fPIC \ $(CXX) $(LDFLAGS) --shared -fPIC \
-Wl,-soname,libolm_debug.so.$(MAJOR) \
-Wl,--version-script,version_script.ver \ -Wl,--version-script,version_script.ver \
$(OUTPUT_OPTION) $(DEBUG_OBJECTS) $(OUTPUT_OPTION) $(DEBUG_OBJECTS)
ln -sf libolm_debug.so.$(VERSION) $(BUILD_DIR)/libolm_debug.so.$(MAJOR)
js: $(JS_TARGET) js: $(JS_TARGET)
.PHONY: js .PHONY: js
@ -128,13 +145,32 @@ $(JS_EXPORTED_FUNCTIONS): $(PUBLIC_HEADERS)
perl -MJSON -ne '$$f{"_$$1"}=1 if /(olm_[^( ]*)\(/; END { @f=sort keys %f; print encode_json \@f }' $^ > $@.tmp perl -MJSON -ne '$$f{"_$$1"}=1 if /(olm_[^( ]*)\(/; END { @f=sort keys %f; print encode_json \@f }' $^ > $@.tmp
mv $@.tmp $@ mv $@.tmp $@
all: test js lib debug all: test js lib debug doc
.PHONY: all .PHONY: all
install-debug: debug
test -d $(DESTDIR)$(PREFIX) || mkdir -p $(DESTDIR)$(PREFIX)
test -d $(DESTDIR)$(PREFIX)/lib || mkdir $(DESTDIR)$(PREFIX)/lib
install -Dm755 $(DEBUG_TARGET) $(DESTDIR)$(PREFIX)/lib/libolm_debug.so.$(VERSION)
ln -s libolm_debug.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm_debug.so.$(MAJOR)
ln -s libolm_debug.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm_debug.so
.PHONY: install-debug
install: lib
test -d $(DESTDIR)$(PREFIX) || mkdir -p $(DESTDIR)$(PREFIX)
test -d $(DESTDIR)$(PREFIX)/lib || mkdir $(DESTDIR)$(PREFIX)/lib
install -Dm755 $(RELEASE_TARGET) $(DESTDIR)$(PREFIX)/lib/libolm.so.$(VERSION)
ln -s libolm.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm.so.$(MAJOR)
ln -s libolm.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm.so
.PHONY: install
clean:; clean:;
rm -rf $(BUILD_DIR) rm -rf $(BUILD_DIR) $(DOCS)
.PHONY: clean .PHONY: clean
doc: $(DOCS)
.PHONY: doc
### rules for building objects ### rules for building objects
$(BUILD_DIR)/release/%.o: %.c $(BUILD_DIR)/release/%.o: %.c
mkdir -p $(dir $@) mkdir -p $(dir $@)
@ -188,6 +224,9 @@ $(BUILD_DIR)/fuzzers/debug_%: fuzzers/fuzz_%.c $(DEBUG_OBJECTS)
$(BUILD_DIR)/fuzzers/debug_%: fuzzers/fuzz_%.cpp $(DEBUG_OBJECTS) $(BUILD_DIR)/fuzzers/debug_%: fuzzers/fuzz_%.cpp $(DEBUG_OBJECTS)
$(LINK.cc) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@ $(LINK.cc) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
%.html: %.rst
rst2html $< $@
### dependencies ### dependencies
-include $(RELEASE_OBJECTS:.o=.d) -include $(RELEASE_OBJECTS:.o=.d)

View file

@ -1,8 +1,9 @@
Olm Olm
=== ===
An implementation of the cryptographic ratchet described by An implementation of the Double Ratchet cryptographic ratchet described by
https://github.com/trevp/axolotl/wiki, written in C++11 and exposed as a C API https://github.com/trevp/double_ratchet/wiki, written in C and C++11 and
exposed as a C API.
The specification of the Olm ratchet can be found in docs/olm.rst or The specification of the Olm ratchet can be found in docs/olm.rst or
https://matrix.org/docs/spec/olm.html https://matrix.org/docs/spec/olm.html
@ -27,15 +28,28 @@ To build the javascript bindings, install emscripten from http://kripken.github.
.. code:: bash .. code:: bash
make js make js
npm pack javascript # packages olm.js into olm-x.y.z.tgz
Remember to make a tag after releasing a tarball: Release process
---------------
.. code:: bash .. code:: bash
git tag x.y.z # Bump version numbers in ``Makefile`` and ``javascript/package.json``
# Prepare changelog
git commit
make clean
make test
make js
npm pack javascript
VERSION=x.y.z
scp olm-$VERSION.tgz packages@ldc-prd-matrix-001:/sites/matrix/packages/npm/olm/
git tag $VERSION -s
git push --tags git push --tags
It's probably sensible to do the above on a release branch (``release-vx.y.z``
by convention), and merge back to master once complete.
Design Design
------ ------

View file

@ -16,7 +16,7 @@
#define OLM_ACCOUNT_HH_ #define OLM_ACCOUNT_HH_
#include "olm/list.hh" #include "olm/list.hh"
#include "olm/crypto.hh" #include "olm/crypto.h"
#include "olm/error.h" #include "olm/error.h"
#include <cstdint> #include <cstdint>
@ -25,14 +25,14 @@ namespace olm {
struct IdentityKeys { struct IdentityKeys {
Ed25519KeyPair ed25519_key; _olm_ed25519_key_pair ed25519_key;
Curve25519KeyPair curve25519_key; _olm_curve25519_key_pair curve25519_key;
}; };
struct OneTimeKey { struct OneTimeKey {
std::uint32_t id; std::uint32_t id;
bool published; bool published;
Curve25519KeyPair key; _olm_curve25519_key_pair key;
}; };
@ -128,12 +128,12 @@ struct Account {
/** Lookup a one time key with the given public key */ /** Lookup a one time key with the given public key */
OneTimeKey const * lookup_key( OneTimeKey const * lookup_key(
Curve25519PublicKey const & public_key _olm_curve25519_public_key const & public_key
); );
/** Remove a one time key with the given public key */ /** Remove a one time key with the given public key */
std::size_t remove_key( std::size_t remove_key(
Curve25519PublicKey const & public_key _olm_curve25519_public_key const & public_key
); );
}; };

View file

@ -27,10 +27,100 @@
extern "C" { extern "C" {
#endif #endif
const size_t SHA256_OUTPUT_LENGTH = 32; /** length of a sha256 hash */
#define SHA256_OUTPUT_LENGTH 32
/** Computes SHA-256 of the input. The output buffer must be a least 32 /** length of a public or private Curve25519 key */
* bytes long. */ #define CURVE25519_KEY_LENGTH 32
/** length of the shared secret created by a Curve25519 ECDH operation */
#define CURVE25519_SHARED_SECRET_LENGTH 32
/** amount of random data required to create a Curve25519 keypair */
#define CURVE25519_RANDOM_LENGTH CURVE25519_KEY_LENGTH
/** length of a public Ed25519 key */
#define ED25519_PUBLIC_KEY_LENGTH 32
/** length of a private Ed25519 key */
#define ED25519_PRIVATE_KEY_LENGTH 64
/** amount of random data required to create a Ed25519 keypair */
#define ED25519_RANDOM_LENGTH 32
/** length of an Ed25519 signature */
#define ED25519_SIGNATURE_LENGTH 64
/** length of an aes256 key */
#define AES256_KEY_LENGTH 32
/** length of an aes256 initialisation vector */
#define AES256_IV_LENGTH 16
struct _olm_aes256_key {
uint8_t key[AES256_KEY_LENGTH];
};
struct _olm_aes256_iv {
uint8_t iv[AES256_IV_LENGTH];
};
struct _olm_curve25519_public_key {
uint8_t public_key[CURVE25519_KEY_LENGTH];
};
struct _olm_curve25519_private_key {
uint8_t private_key[CURVE25519_KEY_LENGTH];
};
struct _olm_curve25519_key_pair {
struct _olm_curve25519_public_key public_key;
struct _olm_curve25519_private_key private_key;
};
struct _olm_ed25519_public_key {
uint8_t public_key[ED25519_PUBLIC_KEY_LENGTH];
};
struct _olm_ed25519_private_key {
uint8_t private_key[ED25519_PRIVATE_KEY_LENGTH];
};
struct _olm_ed25519_key_pair {
struct _olm_ed25519_public_key public_key;
struct _olm_ed25519_private_key private_key;
};
/** The length of output the aes_encrypt_cbc function will write */
size_t _olm_crypto_aes_encrypt_cbc_length(
size_t input_length
);
/** Encrypts the input using AES256 in CBC mode with PKCS#7 padding.
* The output buffer must be big enough to hold the output including padding */
void _olm_crypto_aes_encrypt_cbc(
const struct _olm_aes256_key *key,
const struct _olm_aes256_iv *iv,
const uint8_t *input, size_t input_length,
uint8_t *output
);
/** Decrypts the input using AES256 in CBC mode. The output buffer must be at
* least the same size as the input buffer. Returns the length of the plaintext
* without padding on success or std::size_t(-1) if the padding is invalid.
*/
size_t _olm_crypto_aes_decrypt_cbc(
const struct _olm_aes256_key *key,
const struct _olm_aes256_iv *iv,
uint8_t const * input, size_t input_length,
uint8_t * output
);
/** Computes SHA-256 of the input. The output buffer must be a least
* SHA256_OUTPUT_LENGTH (32) bytes long. */
void _olm_crypto_sha256( void _olm_crypto_sha256(
uint8_t const * input, size_t input_length, uint8_t const * input, size_t input_length,
uint8_t * output uint8_t * output
@ -39,7 +129,7 @@ void _olm_crypto_sha256(
/** HMAC: Keyed-Hashing for Message Authentication /** HMAC: Keyed-Hashing for Message Authentication
* http://tools.ietf.org/html/rfc2104 * http://tools.ietf.org/html/rfc2104
* Computes HMAC-SHA-256 of the input for the key. The output buffer must * Computes HMAC-SHA-256 of the input for the key. The output buffer must
* be at least 32 bytes long. */ * be at least SHA256_OUTPUT_LENGTH (32) bytes long. */
void _olm_crypto_hmac_sha256( void _olm_crypto_hmac_sha256(
uint8_t const * key, size_t key_length, uint8_t const * key, size_t key_length,
uint8_t const * input, size_t input_length, uint8_t const * input, size_t input_length,
@ -58,6 +148,53 @@ void _olm_crypto_hkdf_sha256(
); );
/** Generate a curve25519 key pair
* random_32_bytes should be CURVE25519_RANDOM_LENGTH (32) bytes long.
*/
void _olm_crypto_curve25519_generate_key(
uint8_t const * random_32_bytes,
struct _olm_curve25519_key_pair *output
);
/** Create a shared secret using our private key and their public key.
* The output buffer must be at least CURVE25519_SHARED_SECRET_LENGTH (32) bytes long.
*/
void _olm_crypto_curve25519_shared_secret(
const struct _olm_curve25519_key_pair *our_key,
const struct _olm_curve25519_public_key *their_key,
uint8_t * output
);
/** Generate an ed25519 key pair
* random_32_bytes should be ED25519_RANDOM_LENGTH (32) bytes long.
*/
void _olm_crypto_ed25519_generate_key(
uint8_t const * random_bytes,
struct _olm_ed25519_key_pair *output
);
/** Signs the message using our private key.
*
* The output buffer must be at least ED25519_SIGNATURE_LENGTH (64) bytes
* long. */
void _olm_crypto_ed25519_sign(
const struct _olm_ed25519_key_pair *our_key,
const uint8_t * message, size_t message_length,
uint8_t * output
);
/** Verify an ed25519 signature
* The signature input buffer must be ED25519_SIGNATURE_LENGTH (64) bytes long.
* Returns non-zero if the signature is valid. */
int _olm_crypto_ed25519_verify(
const struct _olm_ed25519_public_key *their_key,
const uint8_t * message, size_t message_length,
const uint8_t * signature
);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

View file

@ -1,154 +0,0 @@
/* Copyright 2015 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.
*/
#ifndef OLM_CRYPTO_HH_
#define OLM_CRYPTO_HH_
#include <cstdint>
#include <cstddef>
#include <string>
// eventually all of this needs to move into crypto.h, and everything should
// use that. For now, include crypto.h here.
#include "olm/crypto.h"
namespace olm {
static const std::size_t KEY_LENGTH = 32;
static const std::size_t SIGNATURE_LENGTH = 64;
static const std::size_t IV_LENGTH = 16;
struct Curve25519PublicKey {
std::uint8_t public_key[KEY_LENGTH];
std::string to_string() const;
};
struct Curve25519KeyPair : public Curve25519PublicKey {
std::uint8_t private_key[KEY_LENGTH];
};
struct Ed25519PublicKey {
std::uint8_t public_key[KEY_LENGTH];
};
struct Ed25519KeyPair : public Ed25519PublicKey {
std::uint8_t private_key[KEY_LENGTH];
};
/** Generate a curve25519 key pair from 32 random bytes. */
void curve25519_generate_key(
std::uint8_t const * random_32_bytes,
Curve25519KeyPair & key_pair
);
/** Create a shared secret using our private key and their public key.
* The output buffer must be at least 32 bytes long. */
void curve25519_shared_secret(
Curve25519KeyPair const & our_key,
Curve25519PublicKey const & their_key,
std::uint8_t * output
);
/** Signs the message using our private key.
* The output buffer must be at least 64 bytes long. */
void curve25519_sign(
Curve25519KeyPair const & our_key,
std::uint8_t const * message, std::size_t message_length,
std::uint8_t * output
);
/** Verify their message using their public key.
* The signature input buffer must be 64 bytes long.
* Returns true if the signature is valid. */
bool curve25519_verify(
Curve25519PublicKey const & their_key,
std::uint8_t const * message, std::size_t message_length,
std::uint8_t const * signature
);
/** Generate a curve25519 key pair from 32 random bytes. */
void ed25519_generate_key(
std::uint8_t const * random_32_bytes,
Ed25519KeyPair & key_pair
);
/** Signs the message using our private key.
* The output buffer must be at least 64 bytes long. */
void ed25519_sign(
Ed25519KeyPair const & our_key,
std::uint8_t const * message, std::size_t message_length,
std::uint8_t * output
);
/** Verify their message using their public key.
* The signature input buffer must be 64 bytes long.
* Returns true if the signature is valid. */
bool ed25519_verify(
Ed25519PublicKey const & their_key,
std::uint8_t const * message, std::size_t message_length,
std::uint8_t const * signature
);
struct Aes256Key {
std::uint8_t key[KEY_LENGTH];
};
struct Aes256Iv {
std::uint8_t iv[IV_LENGTH];
};
/** The length of output the aes_encrypt_cbc function will write */
std::size_t aes_encrypt_cbc_length(
std::size_t input_length
);
/** Encrypts the input using AES256 in CBC mode with PKCS#7 padding.
* The output buffer must be big enough to hold the output including padding */
void aes_encrypt_cbc(
Aes256Key const & key,
Aes256Iv const & iv,
std::uint8_t const * input, std::size_t input_length,
std::uint8_t * output
);
/** Decrypts the input using AES256 in CBC mode. The output buffer must be at
* least the same size as the input buffer. Returns the length of the plaintext
* without padding on success or std::size_t(-1) if the padding is invalid.
*/
std::size_t aes_decrypt_cbc(
Aes256Key const & key,
Aes256Iv const & iv,
std::uint8_t const * input, std::size_t input_length,
std::uint8_t * output
);
} // namespace olm
#endif /* OLM_CRYPTO_HH_ */

View file

@ -39,6 +39,18 @@ enum OlmErrorCode {
* known session key. * known session key.
*/ */
/**
* Attempt to unpickle an account which uses pickle version 1 (which did
* not save enough space for the Ed25519 key; the key should be considered
* compromised. We don't let the user reload the account.
*/
OLM_BAD_LEGACY_ACCOUNT_PICKLE = 13,
/**
* Received message had a bad signature
*/
OLM_BAD_SIGNATURE = 14,
/* remember to update the list of string constants in error.c when updating /* remember to update the list of string constants in error.c when updating
* this list. */ * this list. */
}; };

View file

@ -95,9 +95,7 @@ size_t olm_unpickle_inbound_group_session(
*/ */
size_t olm_init_inbound_group_session( size_t olm_init_inbound_group_session(
OlmInboundGroupSession *session, OlmInboundGroupSession *session,
uint32_t message_index, /* base64-encoded keys */
/* base64-encoded key */
uint8_t const * session_key, size_t session_key_length uint8_t const * session_key, size_t session_key_length
); );
@ -146,6 +144,27 @@ size_t olm_group_decrypt(
); );
/**
* Get the number of bytes returned by olm_inbound_group_session_id()
*/
size_t olm_inbound_group_session_id_length(
const OlmInboundGroupSession *session
);
/**
* Get a base64-encoded identifier for this session.
*
* Returns the length of the session id on success or olm_error() on
* failure. On failure last_error will be set with an error code. The
* last_error will be OUTPUT_BUFFER_TOO_SMALL if the id buffer was too
* small.
*/
size_t olm_inbound_group_session_id(
OlmInboundGroupSession *session,
uint8_t * id, size_t id_length
);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

View file

@ -37,7 +37,8 @@ extern "C" {
size_t _olm_encode_group_message_length( size_t _olm_encode_group_message_length(
uint32_t chain_index, uint32_t chain_index,
size_t ciphertext_length, size_t ciphertext_length,
size_t mac_length size_t mac_length,
size_t signature_length
); );
/** /**
@ -49,7 +50,8 @@ size_t _olm_encode_group_message_length(
* output: where to write the output. Should be at least * output: where to write the output. Should be at least
* olm_encode_group_message_length() bytes long. * olm_encode_group_message_length() bytes long.
* ciphertext_ptr: returns the address that the ciphertext * ciphertext_ptr: returns the address that the ciphertext
* should be written to, followed by the MAC. * should be written to, followed by the MAC and the
* signature.
* *
* Returns the size of the message, up to the MAC. * Returns the size of the message, up to the MAC.
*/ */
@ -76,7 +78,7 @@ struct _OlmDecodeGroupMessageResults {
*/ */
void _olm_decode_group_message( void _olm_decode_group_message(
const uint8_t *input, size_t input_length, const uint8_t *input, size_t input_length,
size_t mac_length, size_t mac_length, size_t signature_length,
/* output structure: updated with results */ /* output structure: updated with results */
struct _OlmDecodeGroupMessageResults *results struct _OlmDecodeGroupMessageResults *results

View file

@ -33,6 +33,11 @@ typedef struct OlmAccount OlmAccount;
typedef struct OlmSession OlmSession; typedef struct OlmSession OlmSession;
typedef struct OlmUtility OlmUtility; typedef struct OlmUtility OlmUtility;
/** Get the version number of the library.
* Arguments will be updated if non-null.
*/
void olm_get_library_version(uint8_t *major, uint8_t *minor, uint8_t *patch);
/** The size of an account object in bytes */ /** The size of an account object in bytes */
size_t olm_account_size(); size_t olm_account_size();
@ -295,6 +300,10 @@ size_t olm_session_id(
void * id, size_t id_length void * id, size_t id_length
); );
int olm_session_has_received_message(
OlmSession *session
);
/** Checks if the PRE_KEY message is for this in-bound session. This can happen /** Checks if the PRE_KEY message is for this in-bound session. This can happen
* if multiple messages are sent to this account before this account sends a * if multiple messages are sent to this account before this account sends a
* message in reply. Returns olm_error() on failure. If the base64 * message in reply. Returns olm_error() on failure. If the base64

View file

@ -160,7 +160,7 @@ size_t olm_outbound_group_session_key_length(
/** /**
* Get the base64-encoded current ratchet key for this session. * Get the base64-encoded current ratchet key for this session.
* *
* Each message is sent with a diffent ratchet key. This function returns the * Each message is sent with a different ratchet key. This function returns the
* ratchet key that will be used for the next message. * ratchet key that will be used for the next message.
* *
* Returns the length of the ratchet key on success or olm_error() on * Returns the length of the ratchet key on success or olm_error() on

View file

@ -21,6 +21,10 @@
extern "C" { extern "C" {
#endif #endif
struct _olm_ed25519_public_key;
struct _olm_ed25519_key_pair;
#define _olm_pickle_uint32_length(value) 4 #define _olm_pickle_uint32_length(value) 4
uint8_t * _olm_pickle_uint32(uint8_t * pos, uint32_t value); uint8_t * _olm_pickle_uint32(uint8_t * pos, uint32_t value);
uint8_t const * _olm_unpickle_uint32( uint8_t const * _olm_unpickle_uint32(
@ -43,6 +47,42 @@ uint8_t const * _olm_unpickle_bytes(uint8_t const * pos, uint8_t const * end,
uint8_t * bytes, size_t bytes_length); uint8_t * bytes, size_t bytes_length);
/** Get the number of bytes needed to pickle an ed25519 public key */
size_t _olm_pickle_ed25519_public_key_length(
const struct _olm_ed25519_public_key * value
);
/** Pickle the ed25519 public key. Returns a pointer to the next free space in
* the buffer. */
uint8_t * _olm_pickle_ed25519_public_key(
uint8_t *pos, const struct _olm_ed25519_public_key * value
);
/** Unpickle the ed25519 public key. Returns a pointer to the next item in the
* buffer. */
const uint8_t * _olm_unpickle_ed25519_public_key(
const uint8_t *pos, const uint8_t *end,
struct _olm_ed25519_public_key * value
);
/** Get the number of bytes needed to pickle an ed25519 key pair */
size_t _olm_pickle_ed25519_key_pair_length(
const struct _olm_ed25519_key_pair * value
);
/** Pickle the ed25519 key pair. Returns a pointer to the next free space in
* the buffer. */
uint8_t * _olm_pickle_ed25519_key_pair(
uint8_t *pos, const struct _olm_ed25519_key_pair * value
);
/** Unpickle the ed25519 key pair. Returns a pointer to the next item in the
* buffer. */
const uint8_t * _olm_unpickle_ed25519_key_pair(
const uint8_t *pos, const uint8_t *end,
struct _olm_ed25519_key_pair * value
);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

View file

@ -16,7 +16,7 @@
#define OLM_PICKLE_HH_ #define OLM_PICKLE_HH_
#include "olm/list.hh" #include "olm/list.hh"
#include "olm/crypto.hh" #include "olm/crypto.h"
#include <cstring> #include <cstring>
#include <cstdint> #include <cstdint>
@ -109,70 +109,36 @@ std::uint8_t const * unpickle_bytes(
std::size_t pickle_length( std::size_t pickle_length(
const Curve25519PublicKey & value const _olm_curve25519_public_key & value
); );
std::uint8_t * pickle( std::uint8_t * pickle(
std::uint8_t * pos, std::uint8_t * pos,
const Curve25519PublicKey & value const _olm_curve25519_public_key & value
); );
std::uint8_t const * unpickle( std::uint8_t const * unpickle(
std::uint8_t const * pos, std::uint8_t const * end, std::uint8_t const * pos, std::uint8_t const * end,
Curve25519PublicKey & value _olm_curve25519_public_key & value
); );
std::size_t pickle_length( std::size_t pickle_length(
const Curve25519KeyPair & value const _olm_curve25519_key_pair & value
); );
std::uint8_t * pickle( std::uint8_t * pickle(
std::uint8_t * pos, std::uint8_t * pos,
const Curve25519KeyPair & value const _olm_curve25519_key_pair & value
); );
std::uint8_t const * unpickle( std::uint8_t const * unpickle(
std::uint8_t const * pos, std::uint8_t const * end, std::uint8_t const * pos, std::uint8_t const * end,
Curve25519KeyPair & value _olm_curve25519_key_pair & value
);
std::size_t pickle_length(
const Ed25519PublicKey & value
);
std::uint8_t * pickle(
std::uint8_t * pos,
const Ed25519PublicKey & value
);
std::uint8_t const * unpickle(
std::uint8_t const * pos, std::uint8_t const * end,
Ed25519PublicKey & value
);
std::size_t pickle_length(
const Ed25519KeyPair & value
);
std::uint8_t * pickle(
std::uint8_t * pos,
const Ed25519KeyPair & value
);
std::uint8_t const * unpickle(
std::uint8_t const * pos, std::uint8_t const * end,
Ed25519KeyPair & value
); );
} // namespace olm } // namespace olm

View file

@ -13,7 +13,9 @@
* limitations under the License. * limitations under the License.
*/ */
#include "olm/crypto.hh" #include <cstdint>
#include "olm/crypto.h"
#include "olm/list.hh" #include "olm/list.hh"
#include "olm/error.h" #include "olm/error.h"
@ -21,8 +23,13 @@ struct _olm_cipher;
namespace olm { namespace olm {
typedef std::uint8_t SharedKey[olm::KEY_LENGTH]; /** length of a shared key: the root key R(i), chain key C(i,j), and message key
* M(i,j)). They are all only used to stuff into HMACs, so could be any length
* for that. The chain key and message key are both derived from SHA256
* operations, so their length is determined by that. */
const std::size_t OLM_SHARED_KEY_LENGTH = SHA256_OUTPUT_LENGTH;
typedef std::uint8_t SharedKey[OLM_SHARED_KEY_LENGTH];
struct ChainKey { struct ChainKey {
std::uint32_t index; std::uint32_t index;
@ -36,19 +43,19 @@ struct MessageKey {
struct SenderChain { struct SenderChain {
Curve25519KeyPair ratchet_key; _olm_curve25519_key_pair ratchet_key;
ChainKey chain_key; ChainKey chain_key;
}; };
struct ReceiverChain { struct ReceiverChain {
Curve25519PublicKey ratchet_key; _olm_curve25519_public_key ratchet_key;
ChainKey chain_key; ChainKey chain_key;
}; };
struct SkippedMessageKey { struct SkippedMessageKey {
Curve25519PublicKey ratchet_key; _olm_curve25519_public_key ratchet_key;
MessageKey message_key; MessageKey message_key;
}; };
@ -113,14 +120,14 @@ struct Ratchet {
* remote's first ratchet key */ * remote's first ratchet key */
void initialise_as_bob( void initialise_as_bob(
std::uint8_t const * shared_secret, std::size_t shared_secret_length, std::uint8_t const * shared_secret, std::size_t shared_secret_length,
Curve25519PublicKey const & their_ratchet_key _olm_curve25519_public_key const & their_ratchet_key
); );
/** Initialise the session using a shared secret and the public/private key /** Initialise the session using a shared secret and the public/private key
* pair for the first ratchet key */ * pair for the first ratchet key */
void initialise_as_alice( void initialise_as_alice(
std::uint8_t const * shared_secret, std::size_t shared_secret_length, std::uint8_t const * shared_secret, std::size_t shared_secret_length,
Curve25519KeyPair const & our_ratchet_key _olm_curve25519_key_pair const & our_ratchet_key
); );
/** The number of bytes of output the encrypt method will write for /** The number of bytes of output the encrypt method will write for

View file

@ -35,9 +35,9 @@ struct Session {
bool received_message; bool received_message;
Curve25519PublicKey alice_identity_key; _olm_curve25519_public_key alice_identity_key;
Curve25519PublicKey alice_base_key; _olm_curve25519_public_key alice_base_key;
Curve25519PublicKey bob_one_time_key; _olm_curve25519_public_key bob_one_time_key;
/** The number of random bytes that are needed to create a new outbound /** The number of random bytes that are needed to create a new outbound
* session. This will be 64 bytes since two ephemeral keys are needed. */ * session. This will be 64 bytes since two ephemeral keys are needed. */
@ -48,8 +48,8 @@ struct Session {
* NOT_ENOUGH_RANDOM if the number of random bytes was too small. */ * NOT_ENOUGH_RANDOM if the number of random bytes was too small. */
std::size_t new_outbound_session( std::size_t new_outbound_session(
Account const & local_account, Account const & local_account,
Curve25519PublicKey const & identity_key, _olm_curve25519_public_key const & identity_key,
Curve25519PublicKey const & one_time_key, _olm_curve25519_public_key const & one_time_key,
std::uint8_t const * random, std::size_t random_length std::uint8_t const * random, std::size_t random_length
); );
@ -59,7 +59,7 @@ struct Session {
* the message headers could not be decoded. */ * the message headers could not be decoded. */
std::size_t new_inbound_session( std::size_t new_inbound_session(
Account & local_account, Account & local_account,
Curve25519PublicKey const * their_identity_key, _olm_curve25519_public_key const * their_identity_key,
std::uint8_t const * pre_key_message, std::size_t message_length std::uint8_t const * pre_key_message, std::size_t message_length
); );
@ -82,7 +82,7 @@ struct Session {
* session does not match or the pre-key message could not be decoded. * session does not match or the pre-key message could not be decoded.
*/ */
bool matches_inbound_session( bool matches_inbound_session(
Curve25519PublicKey const * their_identity_key, _olm_curve25519_public_key const * their_identity_key,
std::uint8_t const * pre_key_message, std::size_t message_length std::uint8_t const * pre_key_message, std::size_t message_length
); );

View file

@ -21,9 +21,9 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
namespace olm { struct _olm_ed25519_public_key;
struct Ed25519PublicKey; namespace olm {
struct Utility { struct Utility {
@ -48,7 +48,7 @@ struct Utility {
* last_error will be set with an error code. If the signature was too short * last_error will be set with an error code. If the signature was too short
* or was not a valid signature then last_error will be BAD_MESSAGE_MAC. */ * or was not a valid signature then last_error will be BAD_MESSAGE_MAC. */
std::size_t ed25519_verify( std::size_t 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 * message, std::size_t message_length,
std::uint8_t const * signature, std::size_t signature_length std::uint8_t const * signature, std::size_t signature_length
); );

View file

@ -261,7 +261,7 @@ DemoUser.prototype.receiveOneToOne = function(jsonpacket) {
if (!self.peerGroupSessions[sender] || if (!self.peerGroupSessions[sender] ||
!self.peerGroupSessions[sender][body.session_id]) { !self.peerGroupSessions[sender][body.session_id]) {
self.createInboundSession( self.createInboundSession(
sender, body.session_id, body.message_index, body.session_key sender, body.session_id, body.session_key
); );
} }
}); });
@ -337,15 +337,18 @@ DemoUser.prototype.getGroupSession = function() {
* add a task to create an inbound group session * add a task to create an inbound group session
*/ */
DemoUser.prototype.createInboundSession = function( DemoUser.prototype.createInboundSession = function(
peer_id, session_id, message_index, session_key, callback peer_id, session_id, session_key, callback
) { ) {
var self = this; var self = this;
this.addTask("init inbound session", function(done) { this.addTask("init inbound session", function(done) {
session = new Olm.InboundGroupSession(); session = new Olm.InboundGroupSession();
session.create(message_index, session_key); session.create(session_key);
if (!self.peerGroupSessions[peer_id]) { if (!self.peerGroupSessions[peer_id]) {
self.peerGroupSessions[peer_id] = {}; self.peerGroupSessions[peer_id] = {};
} }
if (session_id != session.session_id()) {
throw new Error("Mismatched session_ids");
}
self.peerGroupSessions[peer_id][session_id] = session; self.peerGroupSessions[peer_id][session_id] = session;
done(session); done(session);
}, callback); }, callback);

View file

@ -1,3 +1,9 @@
/* The 'length' argument to Pointer_stringify doesn't work if the input includes
* characters >= 128; we therefore need to add a NULL character to all of our
* strings. This acts as a symbolic constant to help show what we're doing.
*/
var NULL_BYTE_PADDING_LENGTH = 1;
function InboundGroupSession() { function InboundGroupSession() {
var size = Module['_olm_inbound_group_session_size'](); var size = Module['_olm_inbound_group_session_size']();
this.buf = malloc(size); this.buf = malloc(size);
@ -28,11 +34,11 @@ InboundGroupSession.prototype['pickle'] = restore_stack(function(key) {
Module['_olm_pickle_inbound_group_session_length'] Module['_olm_pickle_inbound_group_session_length']
)(this.ptr); )(this.ptr);
var key_buffer = stack(key_array); var key_buffer = stack(key_array);
var pickle_buffer = stack(pickle_length); var pickle_buffer = stack(pickle_length + NULL_BYTE_PADDING_LENGTH);
inbound_group_session_method(Module['_olm_pickle_inbound_group_session'])( inbound_group_session_method(Module['_olm_pickle_inbound_group_session'])(
this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length
); );
return Pointer_stringify(pickle_buffer, pickle_length); return Pointer_stringify(pickle_buffer);
}); });
InboundGroupSession.prototype['unpickle'] = restore_stack(function(key, pickle) { InboundGroupSession.prototype['unpickle'] = restore_stack(function(key, pickle) {
@ -46,12 +52,12 @@ InboundGroupSession.prototype['unpickle'] = restore_stack(function(key, pickle)
); );
}); });
InboundGroupSession.prototype['create'] = restore_stack(function(message_index, session_key) { InboundGroupSession.prototype['create'] = restore_stack(function(session_key) {
var key_array = array_from_string(session_key); var key_array = array_from_string(session_key);
var key_buffer = stack(key_array); var key_buffer = stack(key_array);
inbound_group_session_method(Module['_olm_init_inbound_group_session'])( inbound_group_session_method(Module['_olm_init_inbound_group_session'])(
this.ptr, message_index, key_buffer, key_array.length this.ptr, key_buffer, key_array.length
); );
}); });
@ -60,19 +66,38 @@ InboundGroupSession.prototype['decrypt'] = restore_stack(function(
) { ) {
var message_array = array_from_string(message); var message_array = array_from_string(message);
var message_buffer = stack(message_array); var message_buffer = stack(message_array);
var max_plaintext_length = session_method( var max_plaintext_length = inbound_group_session_method(
Module['_olm_group_decrypt_max_plaintext_length'] Module['_olm_group_decrypt_max_plaintext_length']
)(this.ptr, message_buffer, message_array.length); )(this.ptr, message_buffer, message_array.length);
// caculating the length destroys the input buffer. // caculating the length destroys the input buffer.
// So we copy the array to a new buffer // So we copy the array to a new buffer
var message_buffer = stack(message_array); var message_buffer = stack(message_array);
var plaintext_buffer = stack(max_plaintext_length); var plaintext_buffer = stack(max_plaintext_length + NULL_BYTE_PADDING_LENGTH);
var plaintext_length = session_method(Module["_olm_group_decrypt"])( var plaintext_length = inbound_group_session_method(Module["_olm_group_decrypt"])(
this.ptr, this.ptr,
message_buffer, message_array.length, message_buffer, message_array.length,
plaintext_buffer, max_plaintext_length plaintext_buffer, max_plaintext_length
); );
return Pointer_stringify(plaintext_buffer, plaintext_length);
// Pointer_stringify requires a null-terminated argument (the optional
// 'len' argument doesn't work for UTF-8 data).
Module['setValue'](
plaintext_buffer+plaintext_length,
0, "i8"
);
return Pointer_stringify(plaintext_buffer);
});
InboundGroupSession.prototype['session_id'] = restore_stack(function() {
var length = inbound_group_session_method(
Module['_olm_inbound_group_session_id_length']
)(this.ptr);
var session_id = stack(length + NULL_BYTE_PADDING_LENGTH);
inbound_group_session_method(Module['_olm_inbound_group_session_id'])(
this.ptr, session_id, length
);
return Pointer_stringify(session_id);
}); });
olm_exports['InboundGroupSession'] = InboundGroupSession; olm_exports['InboundGroupSession'] = InboundGroupSession;

View file

@ -1,3 +1,9 @@
/* The 'length' argument to Pointer_stringify doesn't work if the input includes
* characters >= 128; we therefore need to add a NULL character to all of our
* strings. This acts as a symbolic constant to help show what we're doing.
*/
var NULL_BYTE_PADDING_LENGTH = 1;
function OutboundGroupSession() { function OutboundGroupSession() {
var size = Module['_olm_outbound_group_session_size'](); var size = Module['_olm_outbound_group_session_size']();
@ -29,11 +35,11 @@ OutboundGroupSession.prototype['pickle'] = restore_stack(function(key) {
Module['_olm_pickle_outbound_group_session_length'] Module['_olm_pickle_outbound_group_session_length']
)(this.ptr); )(this.ptr);
var key_buffer = stack(key_array); var key_buffer = stack(key_array);
var pickle_buffer = stack(pickle_length); var pickle_buffer = stack(pickle_length + NULL_BYTE_PADDING_LENGTH);
outbound_group_session_method(Module['_olm_pickle_outbound_group_session'])( outbound_group_session_method(Module['_olm_pickle_outbound_group_session'])(
this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length
); );
return Pointer_stringify(pickle_buffer, pickle_length); return Pointer_stringify(pickle_buffer);
}); });
OutboundGroupSession.prototype['unpickle'] = restore_stack(function(key, pickle) { OutboundGroupSession.prototype['unpickle'] = restore_stack(function(key, pickle) {
@ -47,8 +53,8 @@ OutboundGroupSession.prototype['unpickle'] = restore_stack(function(key, pickle)
); );
}); });
OutboundGroupSession.prototype['create'] = restore_stack(function(key) { OutboundGroupSession.prototype['create'] = restore_stack(function() {
var random_length = session_method( var random_length = outbound_group_session_method(
Module['_olm_init_outbound_group_session_random_length'] Module['_olm_init_outbound_group_session_random_length']
)(this.ptr); )(this.ptr);
var random = random_stack(random_length); var random = random_stack(random_length);
@ -63,35 +69,35 @@ OutboundGroupSession.prototype['encrypt'] = restore_stack(function(plaintext) {
Module['_olm_group_encrypt_message_length'] Module['_olm_group_encrypt_message_length']
)(this.ptr, plaintext_array.length); )(this.ptr, plaintext_array.length);
var plaintext_buffer = stack(plaintext_array); var plaintext_buffer = stack(plaintext_array);
var message_buffer = stack(message_length); var message_buffer = stack(message_length + NULL_BYTE_PADDING_LENGTH);
outbound_group_session_method(Module['_olm_group_encrypt'])( outbound_group_session_method(Module['_olm_group_encrypt'])(
this.ptr, this.ptr,
plaintext_buffer, plaintext_array.length, plaintext_buffer, plaintext_array.length,
message_buffer, message_length message_buffer, message_length
); );
return Pointer_stringify(message_buffer, message_length); return Pointer_stringify(message_buffer);
}); });
OutboundGroupSession.prototype['session_id'] = restore_stack(function(key) { OutboundGroupSession.prototype['session_id'] = restore_stack(function() {
var length = outbound_group_session_method( var length = outbound_group_session_method(
Module['_olm_outbound_group_session_id_length'] Module['_olm_outbound_group_session_id_length']
)(this.ptr); )(this.ptr);
var session_id = stack(length); var session_id = stack(length + NULL_BYTE_PADDING_LENGTH);
outbound_group_session_method(Module['_olm_outbound_group_session_id'])( outbound_group_session_method(Module['_olm_outbound_group_session_id'])(
this.ptr, session_id, length this.ptr, session_id, length
); );
return Pointer_stringify(session_id, length); return Pointer_stringify(session_id);
}); });
OutboundGroupSession.prototype['session_key'] = restore_stack(function(key) { OutboundGroupSession.prototype['session_key'] = restore_stack(function() {
var key_length = outbound_group_session_method( var key_length = outbound_group_session_method(
Module['_olm_outbound_group_session_key_length'] Module['_olm_outbound_group_session_key_length']
)(this.ptr); )(this.ptr);
var key = stack(key_length); var key = stack(key_length + NULL_BYTE_PADDING_LENGTH);
outbound_group_session_method(Module['_olm_outbound_group_session_key'])( outbound_group_session_method(Module['_olm_outbound_group_session_key'])(
this.ptr, key, key_length this.ptr, key, key_length
); );
return Pointer_stringify(key, key_length); return Pointer_stringify(key);
}); });
OutboundGroupSession.prototype['message_index'] = function() { OutboundGroupSession.prototype['message_index'] = function() {

View file

@ -4,6 +4,16 @@ var free = Module['_free'];
var Pointer_stringify = Module['Pointer_stringify']; var Pointer_stringify = Module['Pointer_stringify'];
var OLM_ERROR = Module['_olm_error'](); var OLM_ERROR = Module['_olm_error']();
/* The 'length' argument to Pointer_stringify doesn't work if the input includes
* characters >= 128; we therefore need to add a NULL character to all of our
* strings. This acts as a symbolic constant to help show what we're doing.
*/
var NULL_BYTE_PADDING_LENGTH = 1;
/* allocate a number of bytes of storage on the stack.
*
* If size_or_array is a Number, allocates that number of zero-initialised bytes.
*/
function stack(size_or_array) { function stack(size_or_array) {
return Module['allocate'](size_or_array, 'i8', Module['ALLOC_STACK']); return Module['allocate'](size_or_array, 'i8', Module['ALLOC_STACK']);
} }
@ -68,11 +78,11 @@ Account.prototype['identity_keys'] = restore_stack(function() {
var keys_length = account_method( var keys_length = account_method(
Module['_olm_account_identity_keys_length'] Module['_olm_account_identity_keys_length']
)(this.ptr); )(this.ptr);
var keys = stack(keys_length); var keys = stack(keys_length + NULL_BYTE_PADDING_LENGTH);
account_method(Module['_olm_account_identity_keys'])( account_method(Module['_olm_account_identity_keys'])(
this.ptr, keys, keys_length this.ptr, keys, keys_length
); );
return Pointer_stringify(keys, keys_length); return Pointer_stringify(keys);
}); });
Account.prototype['sign'] = restore_stack(function(message) { Account.prototype['sign'] = restore_stack(function(message) {
@ -81,24 +91,24 @@ Account.prototype['sign'] = restore_stack(function(message) {
)(this.ptr); )(this.ptr);
var message_array = array_from_string(message); var message_array = array_from_string(message);
var message_buffer = stack(message_array); var message_buffer = stack(message_array);
var signature_buffer = stack(signature_length); var signature_buffer = stack(signature_length + NULL_BYTE_PADDING_LENGTH);
account_method(Module['_olm_account_sign'])( account_method(Module['_olm_account_sign'])(
this.ptr, this.ptr,
message_buffer, message_array.length, message_buffer, message_array.length,
signature_buffer, signature_length signature_buffer, signature_length
); );
return Pointer_stringify(signature_buffer, signature_length); return Pointer_stringify(signature_buffer);
}); });
Account.prototype['one_time_keys'] = restore_stack(function() { Account.prototype['one_time_keys'] = restore_stack(function() {
var keys_length = account_method( var keys_length = account_method(
Module['_olm_account_one_time_keys_length'] Module['_olm_account_one_time_keys_length']
)(this.ptr); )(this.ptr);
var keys = stack(keys_length); var keys = stack(keys_length + NULL_BYTE_PADDING_LENGTH);
account_method(Module['_olm_account_one_time_keys'])( account_method(Module['_olm_account_one_time_keys'])(
this.ptr, keys, keys_length this.ptr, keys, keys_length
); );
return Pointer_stringify(keys, keys_length); return Pointer_stringify(keys);
}); });
Account.prototype['mark_keys_as_published'] = restore_stack(function() { Account.prototype['mark_keys_as_published'] = restore_stack(function() {
@ -135,11 +145,11 @@ Account.prototype['pickle'] = restore_stack(function(key) {
Module['_olm_pickle_account_length'] Module['_olm_pickle_account_length']
)(this.ptr); )(this.ptr);
var key_buffer = stack(key_array); var key_buffer = stack(key_array);
var pickle_buffer = stack(pickle_length); var pickle_buffer = stack(pickle_length + NULL_BYTE_PADDING_LENGTH);
account_method(Module['_olm_pickle_account'])( account_method(Module['_olm_pickle_account'])(
this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length
); );
return Pointer_stringify(pickle_buffer, pickle_length); return Pointer_stringify(pickle_buffer);
}); });
Account.prototype['unpickle'] = restore_stack(function(key, pickle) { Account.prototype['unpickle'] = restore_stack(function(key, pickle) {
@ -183,11 +193,11 @@ Session.prototype['pickle'] = restore_stack(function(key) {
Module['_olm_pickle_session_length'] Module['_olm_pickle_session_length']
)(this.ptr); )(this.ptr);
var key_buffer = stack(key_array); var key_buffer = stack(key_array);
var pickle_buffer = stack(pickle_length); var pickle_buffer = stack(pickle_length + NULL_BYTE_PADDING_LENGTH);
session_method(Module['_olm_pickle_session'])( session_method(Module['_olm_pickle_session'])(
this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length
); );
return Pointer_stringify(pickle_buffer, pickle_length); return Pointer_stringify(pickle_buffer);
}); });
Session.prototype['unpickle'] = restore_stack(function(key, pickle) { Session.prototype['unpickle'] = restore_stack(function(key, pickle) {
@ -246,13 +256,20 @@ Session.prototype['create_inbound_from'] = restore_stack(function(
Session.prototype['session_id'] = restore_stack(function() { Session.prototype['session_id'] = restore_stack(function() {
var id_length = session_method(Module['_olm_session_id_length'])(this.ptr); var id_length = session_method(Module['_olm_session_id_length'])(this.ptr);
var id_buffer = stack(id_length); var id_buffer = stack(id_length + NULL_BYTE_PADDING_LENGTH);
session_method(Module['_olm_session_id'])( session_method(Module['_olm_session_id'])(
this.ptr, id_buffer, id_length this.ptr, id_buffer, id_length
); );
return Pointer_stringify(id_buffer, id_length); return Pointer_stringify(id_buffer);
}); });
Session.prototype['has_received_message'] = function() {
return session_method(Module['_olm_session_has_received_message'])(
this.ptr
) ? true : false;
};
Session.prototype['matches_inbound'] = restore_stack(function( Session.prototype['matches_inbound'] = restore_stack(function(
one_time_key_message one_time_key_message
) { ) {
@ -292,7 +309,7 @@ Session.prototype['encrypt'] = restore_stack(function(
)(this.ptr, plaintext_array.length); )(this.ptr, plaintext_array.length);
var random = random_stack(random_length); var random = random_stack(random_length);
var plaintext_buffer = stack(plaintext_array); var plaintext_buffer = stack(plaintext_array);
var message_buffer = stack(message_length); var message_buffer = stack(message_length + NULL_BYTE_PADDING_LENGTH);
session_method(Module['_olm_encrypt'])( session_method(Module['_olm_encrypt'])(
this.ptr, this.ptr,
plaintext_buffer, plaintext_array.length, plaintext_buffer, plaintext_array.length,
@ -301,7 +318,7 @@ Session.prototype['encrypt'] = restore_stack(function(
); );
return { return {
"type": message_type, "type": message_type,
"body": Pointer_stringify(message_buffer, message_length) "body": Pointer_stringify(message_buffer)
}; };
}); });
@ -316,13 +333,23 @@ Session.prototype['decrypt'] = restore_stack(function(
// caculating the length destroys the input buffer. // caculating the length destroys the input buffer.
// So we copy the array to a new buffer // So we copy the array to a new buffer
var message_buffer = stack(message_array); var message_buffer = stack(message_array);
var plaintext_buffer = stack(max_plaintext_length); var plaintext_buffer = stack(
max_plaintext_length + NULL_BYTE_PADDING_LENGTH
);
var plaintext_length = session_method(Module["_olm_decrypt"])( var plaintext_length = session_method(Module["_olm_decrypt"])(
this.ptr, message_type, this.ptr, message_type,
message_buffer, message.length, message_buffer, message.length,
plaintext_buffer, max_plaintext_length plaintext_buffer, max_plaintext_length
); );
return Pointer_stringify(plaintext_buffer, plaintext_length);
// Pointer_stringify requires a null-terminated argument (the optional
// 'len' argument doesn't work for UTF-8 data).
Module['setValue'](
plaintext_buffer+plaintext_length,
0, "i8"
);
return Pointer_stringify(plaintext_buffer);
}); });
function Utility() { function Utility() {
@ -353,13 +380,13 @@ Utility.prototype['sha256'] = restore_stack(function(input) {
var output_length = utility_method(Module['_olm_sha256_length'])(this.ptr); var output_length = utility_method(Module['_olm_sha256_length'])(this.ptr);
var input_array = array_from_string(input); var input_array = array_from_string(input);
var input_buffer = stack(input_array); var input_buffer = stack(input_array);
var output_buffer = stack(output_length); var output_buffer = stack(output_length + NULL_BYTE_PADDING_LENGTH);
utility_method(Module['_olm_sha2516'])( utility_method(Module['_olm_sha2516'])(
this.ptr, this.ptr,
input_buffer, input_array.length(), input_buffer, input_array.length(),
output_buffer, output_length output_buffer, output_length
); );
return Pointer_stringify(output_buffer, output_length); return Pointer_stringify(output_buffer);
}); });
Utility.prototype['ed25519_verify'] = restore_stack(function( Utility.prototype['ed25519_verify'] = restore_stack(function(
@ -384,4 +411,14 @@ olm_exports["Session"] = Session;
olm_exports["Utility"] = Utility; olm_exports["Utility"] = Utility;
olm_exports['set_log_level'] = Module['_olm_set_log_level']; olm_exports['set_log_level'] = Module['_olm_set_log_level'];
olm_exports["get_library_version"] = restore_stack(function() {
var buf = stack(3);
Module['_olm_get_library_version'](buf, buf+1, buf+2);
return [
getValue(buf, 'i8'),
getValue(buf+1, 'i8'),
getValue(buf+2, 'i8'),
];
});
}(); }();

View file

@ -1,7 +1,7 @@
{ {
"name": "olm", "name": "olm",
"version": "0.1.0", "version": "1.3.0",
"description": "An implementation of a well known cryptographic ratchet", "description": "An implementation of the Double Ratchet cryptographic ratchet",
"main": "olm.js", "main": "olm.js",
"files": [ "files": [
"olm.js", "olm.js",

View file

@ -3,7 +3,12 @@
set -e set -e
make clean make clean
rm olm-*.tgz
make lib make lib
make test make test
./python/test_olm.sh ./python/test_olm.sh
. ~/.emsdk_set_env.sh
make js
npm pack javascript

View file

@ -1,43 +0,0 @@
void convert_curve25519_to_ed25519(
unsigned char * public_key,
unsigned char * signature
) {
fe mont_x, mont_x_minus_one, mont_x_plus_one, inv_mont_x_plus_one;
fe one;
fe ed_y;
fe_frombytes(mont_x, public_key);
fe_1(one);
fe_sub(mont_x_minus_one, mont_x, one);
fe_add(mont_x_plus_one, mont_x, one);
fe_invert(inv_mont_x_plus_one, mont_x_plus_one);
fe_mul(ed_y, mont_x_minus_one, inv_mont_x_plus_one);
fe_tobytes(public_key, ed_y);
public_key[31] &= 0x7F;
public_key[31] |= (signature[63] & 0x80);
signature[63] &= 0x7F;
}
void convert_ed25519_to_curve25519(
unsigned char const * public_key,
unsigned char * signature
) {
unsigned char sign_bit = public_key[31] & 0x80;
signature[63] &= 0x7F;
signature[63] |= sign_bit;
}
void ed25519_keypair(
unsigned char * private_key,
unsigned char * public_key
) {
ge_p3 A;
private_key[0] &= 248;
private_key[31] &= 63;
private_key[31] |= 64;
ge_scalarmult_base(&A, private_key);
ge_p3_tobytes(public_key, &A);
}

View file

@ -1,24 +0,0 @@
#ifndef ED25519_ADDITIONS_H
#define ED25519_ADDITIONS_H
#ifdef __cplusplus
extern "C" {
#endif
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);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -7,7 +7,7 @@ def read_random(n):
return f.read(n) return f.read(n)
lib = cdll.LoadLibrary(os.path.join( lib = cdll.LoadLibrary(os.path.join(
os.path.dirname(__file__), "..", "..", "build", "libolm.so") os.path.dirname(__file__), "..", "..", "build", "libolm.so.1")
) )
lib.olm_error.argtypes = [] lib.olm_error.argtypes = []

View file

@ -33,7 +33,7 @@ inbound_group_session_function(
) )
inbound_group_session_function( inbound_group_session_function(
lib.olm_init_inbound_group_session, c_uint32, c_void_p, c_size_t lib.olm_init_inbound_group_session, c_void_p, c_size_t
) )
inbound_group_session_function( inbound_group_session_function(
@ -45,6 +45,9 @@ inbound_group_session_function(
c_void_p, c_size_t, # plaintext c_void_p, c_size_t, # plaintext
) )
inbound_group_session_function(lib.olm_inbound_group_session_id_length)
inbound_group_session_function(lib.olm_inbound_group_session_id, c_void_p, c_size_t)
class InboundGroupSession(object): class InboundGroupSession(object):
def __init__(self): def __init__(self):
self.buf = create_string_buffer(lib.olm_inbound_group_session_size()) self.buf = create_string_buffer(lib.olm_inbound_group_session_size())
@ -66,10 +69,10 @@ class InboundGroupSession(object):
self.ptr, key_buffer, len(key), pickle_buffer, len(pickle) self.ptr, key_buffer, len(key), pickle_buffer, len(pickle)
) )
def init(self, message_index, session_key): def init(self, session_key):
key_buffer = create_string_buffer(session_key) key_buffer = create_string_buffer(session_key)
lib.olm_init_inbound_group_session( lib.olm_init_inbound_group_session(
self.ptr, message_index, key_buffer, len(session_key) self.ptr, key_buffer, len(session_key)
) )
def decrypt(self, message): def decrypt(self, message):
@ -84,3 +87,9 @@ class InboundGroupSession(object):
plaintext_buffer, max_plaintext_length plaintext_buffer, max_plaintext_length
) )
return plaintext_buffer.raw[:plaintext_length] return plaintext_buffer.raw[:plaintext_length]
def session_id(self):
id_length = lib.olm_inbound_group_session_id_length(self.ptr)
id_buffer = create_string_buffer(id_length)
lib.olm_inbound_group_session_id(self.ptr, id_buffer, id_length);
return id_buffer.raw

View file

@ -15,6 +15,7 @@
#include "olm/account.hh" #include "olm/account.hh"
#include "olm/base64.hh" #include "olm/base64.hh"
#include "olm/logging.h" #include "olm/logging.h"
#include "olm/pickle.h"
#include "olm/pickle.hh" #include "olm/pickle.hh"
#include "olm/memory.hh" #include "olm/memory.hh"
@ -27,10 +28,10 @@ olm::Account::Account(
olm::OneTimeKey const * olm::Account::lookup_key( 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) { 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; return &key;
} }
} }
@ -38,11 +39,11 @@ olm::OneTimeKey const * olm::Account::lookup_key(
} }
std::size_t olm::Account::remove_key( std::size_t olm::Account::remove_key(
olm::Curve25519PublicKey const & public_key _olm_curve25519_public_key const & public_key
) { ) {
OneTimeKey * i; OneTimeKey * i;
for (i = one_time_keys.begin(); i != one_time_keys.end(); ++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; std::uint32_t id = i->id;
one_time_keys.erase(i); one_time_keys.erase(i);
olm_logf(OLM_LOG_INFO, LOG_CATEGORY, "removed key id %i", id); olm_logf(OLM_LOG_INFO, LOG_CATEGORY, "removed key id %i", id);
@ -54,7 +55,7 @@ std::size_t olm::Account::remove_key(
} }
std::size_t olm::Account::new_account_random_length() { 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( std::size_t olm::Account::new_account(
@ -65,9 +66,9 @@ std::size_t olm::Account::new_account(
return std::size_t(-1); return std::size_t(-1);
} }
olm::ed25519_generate_key(random, identity_keys.ed25519_key); _olm_crypto_ed25519_generate_key(random, &identity_keys.ed25519_key);
random += KEY_LENGTH; random += ED25519_RANDOM_LENGTH;
olm::curve25519_generate_key(random, identity_keys.curve25519_key); _olm_crypto_curve25519_generate_key(random, &identity_keys.curve25519_key);
olm_logf(OLM_LOG_DEBUG, LOG_CATEGORY, "Created new account"); olm_logf(OLM_LOG_DEBUG, LOG_CATEGORY, "Created new account");
@ -125,16 +126,16 @@ std::size_t olm::Account::get_identity_json(
pos = write_string(pos, KEY_JSON_CURVE25519); pos = write_string(pos, KEY_JSON_CURVE25519);
*(pos++) = '\"'; *(pos++) = '\"';
pos = olm::encode_base64( pos = olm::encode_base64(
identity_keys.curve25519_key.public_key, identity_keys.curve25519_key.public_key.public_key,
sizeof(identity_keys.curve25519_key.public_key), sizeof(identity_keys.curve25519_key.public_key.public_key),
pos pos
); );
*(pos++) = '\"'; *(pos++) = ','; *(pos++) = '\"'; *(pos++) = ',';
pos = write_string(pos, KEY_JSON_ED25519); pos = write_string(pos, KEY_JSON_ED25519);
*(pos++) = '\"'; *(pos++) = '\"';
pos = olm::encode_base64( pos = olm::encode_base64(
identity_keys.ed25519_key.public_key, identity_keys.ed25519_key.public_key.public_key,
sizeof(identity_keys.ed25519_key.public_key), sizeof(identity_keys.ed25519_key.public_key.public_key),
pos pos
); );
*(pos++) = '\"'; *(pos++) = '}'; *(pos++) = '\"'; *(pos++) = '}';
@ -144,7 +145,7 @@ std::size_t olm::Account::get_identity_json(
std::size_t olm::Account::signature_length( std::size_t olm::Account::signature_length(
) { ) {
return olm::SIGNATURE_LENGTH; return ED25519_SIGNATURE_LENGTH;
} }
@ -156,8 +157,8 @@ std::size_t olm::Account::sign(
last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
return std::size_t(-1); return std::size_t(-1);
} }
olm::ed25519_sign( _olm_crypto_ed25519_sign(
identity_keys.ed25519_key, message, message_length, signature &identity_keys.ed25519_key, message, message_length, signature
); );
return this->signature_length(); return this->signature_length();
} }
@ -209,7 +210,7 @@ std::size_t olm::Account::get_one_time_keys_json(
pos = olm::encode_base64(key_id, sizeof(key_id), pos); pos = olm::encode_base64(key_id, sizeof(key_id), pos);
*(pos++) = '\"'; *(pos++) = ':'; *(pos++) = '\"'; *(pos++) = '\"'; *(pos++) = ':'; *(pos++) = '\"';
pos = olm::encode_base64( 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++) = '\"'; *(pos++) = '\"';
sep = ','; sep = ',';
@ -245,7 +246,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 olm::Account::generate_one_time_keys_random_length(
std::size_t number_of_keys 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( std::size_t olm::Account::generate_one_time_keys(
@ -260,8 +261,8 @@ std::size_t olm::Account::generate_one_time_keys(
OneTimeKey & key = *one_time_keys.insert(one_time_keys.begin()); OneTimeKey & key = *one_time_keys.insert(one_time_keys.begin());
key.id = ++next_one_time_key_id; key.id = ++next_one_time_key_id;
key.published = false; key.published = false;
olm::curve25519_generate_key(random, key.key); _olm_crypto_curve25519_generate_key(random, &key.key);
random += olm::KEY_LENGTH; random += CURVE25519_RANDOM_LENGTH;
} }
return number_of_keys; return number_of_keys;
} }
@ -272,7 +273,7 @@ static std::size_t pickle_length(
olm::IdentityKeys const & value olm::IdentityKeys const & value
) { ) {
size_t length = 0; 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); length += olm::pickle_length(value.curve25519_key);
return length; return length;
} }
@ -282,7 +283,7 @@ static std::uint8_t * pickle(
std::uint8_t * pos, std::uint8_t * pos,
olm::IdentityKeys const & value 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); pos = olm::pickle(pos, value.curve25519_key);
return pos; return pos;
} }
@ -292,7 +293,7 @@ static std::uint8_t const * unpickle(
std::uint8_t const * pos, std::uint8_t const * end, std::uint8_t const * pos, std::uint8_t const * end,
olm::IdentityKeys & value 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); pos = olm::unpickle(pos, end, value.curve25519_key);
return pos; return pos;
} }
@ -333,7 +334,9 @@ static std::uint8_t const * unpickle(
} // namespace olm } // namespace olm
namespace { 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;
} }
@ -367,9 +370,15 @@ std::uint8_t const * olm::unpickle(
) { ) {
uint32_t pickle_version; uint32_t pickle_version;
pos = olm::unpickle(pos, end, pickle_version); pos = olm::unpickle(pos, end, pickle_version);
if (pickle_version != ACCOUNT_PICKLE_VERSION) { switch (pickle_version) {
value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION; case ACCOUNT_PICKLE_VERSION:
return end; 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.identity_keys);
pos = olm::unpickle(pos, end, value.one_time_keys); pos = olm::unpickle(pos, end, value.one_time_keys);

View file

@ -13,16 +13,18 @@
* limitations under the License. * limitations under the License.
*/ */
#include "olm/cipher.h" #include "olm/cipher.h"
#include "olm/crypto.hh" #include "olm/crypto.h"
#include "olm/memory.hh" #include "olm/memory.hh"
#include <cstring> #include <cstring>
const std::size_t HMAC_KEY_LENGTH = 32;
namespace { namespace {
struct DerivedKeys { struct DerivedKeys {
olm::Aes256Key aes_key; _olm_aes256_key aes_key;
std::uint8_t mac_key[olm::KEY_LENGTH]; std::uint8_t mac_key[HMAC_KEY_LENGTH];
olm::Aes256Iv aes_iv; _olm_aes256_iv aes_iv;
}; };
@ -31,7 +33,9 @@ static void derive_keys(
std::uint8_t const * key, std::size_t key_length, std::uint8_t const * key, std::size_t key_length,
DerivedKeys & keys DerivedKeys & keys
) { ) {
std::uint8_t derived_secrets[2 * olm::KEY_LENGTH + olm::IV_LENGTH]; std::uint8_t derived_secrets[
AES256_KEY_LENGTH + HMAC_KEY_LENGTH + AES256_IV_LENGTH
];
_olm_crypto_hkdf_sha256( _olm_crypto_hkdf_sha256(
key, key_length, key, key_length,
nullptr, 0, nullptr, 0,
@ -54,7 +58,7 @@ size_t aes_sha_256_cipher_mac_length(const struct _olm_cipher *cipher) {
size_t aes_sha_256_cipher_encrypt_ciphertext_length( size_t aes_sha_256_cipher_encrypt_ciphertext_length(
const struct _olm_cipher *cipher, size_t plaintext_length const struct _olm_cipher *cipher, size_t plaintext_length
) { ) {
return olm::aes_encrypt_cbc_length(plaintext_length); return _olm_crypto_aes_encrypt_cbc_length(plaintext_length);
} }
size_t aes_sha_256_cipher_encrypt( size_t aes_sha_256_cipher_encrypt(
@ -76,12 +80,12 @@ size_t aes_sha_256_cipher_encrypt(
derive_keys(c->kdf_info, c->kdf_info_length, key, key_length, keys); derive_keys(c->kdf_info, c->kdf_info_length, key, key_length, keys);
olm::aes_encrypt_cbc( _olm_crypto_aes_encrypt_cbc(
keys.aes_key, keys.aes_iv, plaintext, plaintext_length, ciphertext &keys.aes_key, &keys.aes_iv, plaintext, plaintext_length, ciphertext
); );
_olm_crypto_hmac_sha256( _olm_crypto_hmac_sha256(
keys.mac_key, olm::KEY_LENGTH, output, output_length - MAC_LENGTH, mac keys.mac_key, HMAC_KEY_LENGTH, output, output_length - MAC_LENGTH, mac
); );
std::memcpy(output + output_length - MAC_LENGTH, mac, MAC_LENGTH); std::memcpy(output + output_length - MAC_LENGTH, mac, MAC_LENGTH);
@ -113,7 +117,7 @@ size_t aes_sha_256_cipher_decrypt(
derive_keys(c->kdf_info, c->kdf_info_length, key, key_length, keys); derive_keys(c->kdf_info, c->kdf_info_length, key, key_length, keys);
_olm_crypto_hmac_sha256( _olm_crypto_hmac_sha256(
keys.mac_key, olm::KEY_LENGTH, input, input_length - MAC_LENGTH, mac keys.mac_key, HMAC_KEY_LENGTH, input, input_length - MAC_LENGTH, mac
); );
std::uint8_t const * input_mac = input + input_length - MAC_LENGTH; std::uint8_t const * input_mac = input + input_length - MAC_LENGTH;
@ -122,8 +126,8 @@ size_t aes_sha_256_cipher_decrypt(
return std::size_t(-1); return std::size_t(-1);
} }
std::size_t plaintext_length = olm::aes_decrypt_cbc( std::size_t plaintext_length = _olm_crypto_aes_decrypt_cbc(
keys.aes_key, keys.aes_iv, ciphertext, ciphertext_length, plaintext &keys.aes_key, &keys.aes_iv, ciphertext, ciphertext_length, plaintext
); );
olm::unset(keys); olm::unset(keys);

View file

@ -12,7 +12,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#include "olm/crypto.hh" #include "olm/crypto.h"
#include "olm/memory.hh" #include "olm/memory.hh"
#include <cstring> #include <cstring>
@ -25,14 +25,13 @@ extern "C" {
} }
#include "ed25519/src/ed25519.h" #include "ed25519/src/ed25519.h"
#include "ed25519_additions.h"
#include "curve25519-donna.h" #include "curve25519-donna.h"
namespace { namespace {
static const std::uint8_t CURVE25519_BASEPOINT[32] = {9}; 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_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 AES_BLOCK_LENGTH = 16;
static const std::size_t SHA256_BLOCK_LENGTH = 64; static const std::size_t SHA256_BLOCK_LENGTH = 64;
static const std::uint8_t HKDF_DEFAULT_SALT[32] = {}; static const std::uint8_t HKDF_DEFAULT_SALT[32] = {};
@ -101,119 +100,86 @@ inline static void hmac_sha256_final(
} // namespace } // namespace
std::string olm::Curve25519PublicKey::to_string() const { void _olm_crypto_curve25519_generate_key(
return olm::bytes_to_string(std::begin(public_key), uint8_t const * random_32_bytes,
std::end(public_key)); struct _olm_curve25519_key_pair *key_pair
};
void olm::curve25519_generate_key(
std::uint8_t const * random_32_bytes,
olm::Curve25519KeyPair & 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( ::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( void _olm_crypto_curve25519_shared_secret(
olm::Curve25519KeyPair const & our_key, const struct _olm_curve25519_key_pair *our_key,
olm::Curve25519PublicKey const & their_key, const struct _olm_curve25519_public_key * their_key,
std::uint8_t * output std::uint8_t * output
) { ) {
::curve25519_donna(output, our_key.private_key, their_key.public_key); ::curve25519_donna(output, our_key->private_key.private_key, their_key->public_key);
} }
void olm::curve25519_sign( void _olm_crypto_ed25519_generate_key(
olm::Curve25519KeyPair const & our_key,
std::uint8_t const * message, std::size_t message_length,
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);
}
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(
std::uint8_t const * random_32_bytes, 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_create_keypair(
::ed25519_keypair(key_pair.private_key, key_pair.public_key); key_pair->public_key.public_key, key_pair->private_key.private_key,
random_32_bytes
);
} }
void olm::ed25519_sign( void _olm_crypto_ed25519_sign(
olm::Ed25519KeyPair const & our_key, const struct _olm_ed25519_key_pair *our_key,
std::uint8_t const * message, std::size_t message_length, std::uint8_t const * message, std::size_t message_length,
std::uint8_t * output std::uint8_t * output
) { ) {
::ed25519_sign( ::ed25519_sign(
output, output,
message, message_length, 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( int _olm_crypto_ed25519_verify(
olm::Ed25519PublicKey const & their_key, const struct _olm_ed25519_public_key *their_key,
std::uint8_t const * message, std::size_t message_length, std::uint8_t const * message, std::size_t message_length,
std::uint8_t const * signature std::uint8_t const * signature
) { ) {
return 0 != ::ed25519_verify( return 0 != ::ed25519_verify(
signature, signature,
message, message_length, 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 std::size_t input_length
) { ) {
return input_length + AES_BLOCK_LENGTH - input_length % AES_BLOCK_LENGTH; return input_length + AES_BLOCK_LENGTH - input_length % AES_BLOCK_LENGTH;
} }
void olm::aes_encrypt_cbc( void _olm_crypto_aes_encrypt_cbc(
olm::Aes256Key const & key, _olm_aes256_key const *key,
olm::Aes256Iv const & iv, _olm_aes256_iv const *iv,
std::uint8_t const * input, std::size_t input_length, std::uint8_t const * input, std::size_t input_length,
std::uint8_t * output std::uint8_t * output
) { ) {
std::uint32_t key_schedule[AES_KEY_SCHEDULE_LENGTH]; 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::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) { while (input_length >= AES_BLOCK_LENGTH) {
xor_block<AES_BLOCK_LENGTH>(input_block, input); xor_block<AES_BLOCK_LENGTH>(input_block, input);
::aes_encrypt(input_block, output, key_schedule, AES_KEY_BITS); ::aes_encrypt(input_block, output, key_schedule, AES_KEY_BITS);
@ -235,17 +201,17 @@ void olm::aes_encrypt_cbc(
} }
std::size_t olm::aes_decrypt_cbc( std::size_t _olm_crypto_aes_decrypt_cbc(
olm::Aes256Key const & key, _olm_aes256_key const *key,
olm::Aes256Iv const & iv, _olm_aes256_iv const *iv,
std::uint8_t const * input, std::size_t input_length, std::uint8_t const * input, std::size_t input_length,
std::uint8_t * output std::uint8_t * output
) { ) {
std::uint32_t key_schedule[AES_KEY_SCHEDULE_LENGTH]; 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 block1[AES_BLOCK_LENGTH];
std::uint8_t block2[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) { for (std::size_t i = 0; i < input_length; i += AES_BLOCK_LENGTH) {
std::memcpy(block2, &input[i], AES_BLOCK_LENGTH); std::memcpy(block2, &input[i], AES_BLOCK_LENGTH);
::aes_decrypt(&input[i], &output[i], key_schedule, AES_KEY_BITS); ::aes_decrypt(&input[i], &output[i], key_schedule, AES_KEY_BITS);

View file

@ -16,7 +16,7 @@
#include "ed25519/src/fe.c" #include "ed25519/src/fe.c"
#include "ed25519/src/sc.c" #include "ed25519/src/sc.c"
#include "ed25519/src/ge.c" #include "ed25519/src/ge.c"
#include "ed25519/src/keypair.c"
#include "ed25519/src/sha512.c" #include "ed25519/src/sha512.c"
#include "ed25519/src/verify.c" #include "ed25519/src/verify.c"
#include "ed25519/src/sign.c" #include "ed25519/src/sign.c"
#include "ed25519_additions.c"

View file

@ -29,6 +29,8 @@ static const char * ERRORS[] = {
"CORRUPTED_PICKLE", "CORRUPTED_PICKLE",
"BAD_SESSION_KEY", "BAD_SESSION_KEY",
"UNKNOWN_MESSAGE_INDEX", "UNKNOWN_MESSAGE_INDEX",
"BAD_LEGACY_ACCOUNT_PICKLE",
"BAD_SIGNATURE",
}; };
const char * _olm_error_to_string(enum OlmErrorCode error) const char * _olm_error_to_string(enum OlmErrorCode error)

View file

@ -19,6 +19,7 @@
#include "olm/base64.h" #include "olm/base64.h"
#include "olm/cipher.h" #include "olm/cipher.h"
#include "olm/crypto.h"
#include "olm/error.h" #include "olm/error.h"
#include "olm/megolm.h" #include "olm/megolm.h"
#include "olm/memory.h" #include "olm/memory.h"
@ -28,7 +29,9 @@
#define OLM_PROTOCOL_VERSION 3 #define OLM_PROTOCOL_VERSION 3
#define GROUP_SESSION_ID_LENGTH ED25519_PUBLIC_KEY_LENGTH
#define PICKLE_VERSION 1 #define PICKLE_VERSION 1
#define SESSION_KEY_VERSION 2
struct OlmInboundGroupSession { struct OlmInboundGroupSession {
/** our earliest known ratchet value */ /** our earliest known ratchet value */
@ -37,6 +40,9 @@ struct OlmInboundGroupSession {
/** The most recent ratchet value */ /** The most recent ratchet value */
Megolm latest_ratchet; Megolm latest_ratchet;
/** The ed25519 signing key */
struct _olm_ed25519_public_key signing_key;
enum OlmErrorCode last_error; enum OlmErrorCode last_error;
}; };
@ -65,30 +71,69 @@ size_t olm_clear_inbound_group_session(
return 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( size_t olm_init_inbound_group_session(
OlmInboundGroupSession *session, OlmInboundGroupSession *session,
uint32_t message_index,
const uint8_t * session_key, size_t session_key_length const uint8_t * session_key, size_t session_key_length
) { ) {
uint8_t key_buf[MEGOLM_RATCHET_LENGTH]; uint8_t key_buf[SESSION_KEY_RAW_LENGTH];
size_t raw_length = _olm_decode_base64_length(session_key_length); size_t raw_length = _olm_decode_base64_length(session_key_length);
size_t result;
if (raw_length == (size_t)-1) { if (raw_length == (size_t)-1) {
session->last_error = OLM_INVALID_BASE64; session->last_error = OLM_INVALID_BASE64;
return (size_t)-1; return (size_t)-1;
} }
if (raw_length != MEGOLM_RATCHET_LENGTH) { if (raw_length != SESSION_KEY_RAW_LENGTH) {
session->last_error = OLM_BAD_SESSION_KEY; session->last_error = OLM_BAD_SESSION_KEY;
return (size_t)-1; return (size_t)-1;
} }
_olm_decode_base64(session_key, session_key_length, key_buf); _olm_decode_base64(session_key, session_key_length, key_buf);
megolm_init(&session->initial_ratchet, key_buf, message_index); result = _init_group_session_keys(session, key_buf);
megolm_init(&session->latest_ratchet, key_buf, message_index); _olm_unset(key_buf, SESSION_KEY_RAW_LENGTH);
_olm_unset(key_buf, MEGOLM_RATCHET_LENGTH); return result;
return 0;
} }
static size_t raw_pickle_length( static size_t raw_pickle_length(
@ -98,6 +143,7 @@ static size_t raw_pickle_length(
length += _olm_pickle_uint32_length(PICKLE_VERSION); length += _olm_pickle_uint32_length(PICKLE_VERSION);
length += megolm_pickle_length(&session->initial_ratchet); length += megolm_pickle_length(&session->initial_ratchet);
length += megolm_pickle_length(&session->latest_ratchet); length += megolm_pickle_length(&session->latest_ratchet);
length += _olm_pickle_ed25519_public_key_length(&session->signing_key);
return length; return length;
} }
@ -124,6 +170,7 @@ size_t olm_pickle_inbound_group_session(
pos = _olm_pickle_uint32(pos, PICKLE_VERSION); pos = _olm_pickle_uint32(pos, PICKLE_VERSION);
pos = megolm_pickle(&session->initial_ratchet, pos); pos = megolm_pickle(&session->initial_ratchet, pos);
pos = megolm_pickle(&session->latest_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); return _olm_enc_output(key, key_length, pickled, raw_length);
} }
@ -153,6 +200,7 @@ size_t olm_unpickle_inbound_group_session(
} }
pos = megolm_unpickle(&session->initial_ratchet, pos, end); pos = megolm_unpickle(&session->initial_ratchet, pos, end);
pos = megolm_unpickle(&session->latest_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) { if (end != pos) {
/* We had the wrong number of bytes in the input. */ /* We had the wrong number of bytes in the input. */
@ -175,6 +223,7 @@ static size_t _decrypt_max_plaintext_length(
_olm_decode_group_message( _olm_decode_group_message(
message, message_length, message, message_length,
megolm_cipher->ops->mac_length(megolm_cipher), megolm_cipher->ops->mac_length(megolm_cipher),
ED25519_SIGNATURE_LENGTH,
&decoded_results); &decoded_results);
if (decoded_results.version != OLM_PROTOCOL_VERSION) { if (decoded_results.version != OLM_PROTOCOL_VERSION) {
@ -224,6 +273,7 @@ static size_t _decrypt(
_olm_decode_group_message( _olm_decode_group_message(
message, message_length, message, message_length,
megolm_cipher->ops->mac_length(megolm_cipher), megolm_cipher->ops->mac_length(megolm_cipher),
ED25519_SIGNATURE_LENGTH,
&decoded_results); &decoded_results);
if (decoded_results.version != OLM_PROTOCOL_VERSION) { if (decoded_results.version != OLM_PROTOCOL_VERSION) {
@ -231,11 +281,27 @@ static size_t _decrypt(
return (size_t)-1; return (size_t)-1;
} }
if (!decoded_results.has_message_index || !decoded_results.ciphertext ) { if (!decoded_results.has_message_index || !decoded_results.ciphertext) {
session->last_error = OLM_BAD_MESSAGE_FORMAT; session->last_error = OLM_BAD_MESSAGE_FORMAT;
return (size_t)-1; 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( max_length = megolm_cipher->ops->decrypt_max_plaintext_length(
megolm_cipher, megolm_cipher,
decoded_results.ciphertext_length decoded_results.ciphertext_length
@ -298,3 +364,23 @@ size_t olm_group_decrypt(
plaintext, max_plaintext_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
);
}

View file

@ -354,12 +354,14 @@ static const std::uint8_t GROUP_CIPHERTEXT_TAG = 022;
size_t _olm_encode_group_message_length( size_t _olm_encode_group_message_length(
uint32_t message_index, uint32_t message_index,
size_t ciphertext_length, size_t ciphertext_length,
size_t mac_length size_t mac_length,
size_t signature_length
) { ) {
size_t length = VERSION_LENGTH; size_t length = VERSION_LENGTH;
length += 1 + varint_length(message_index); length += 1 + varint_length(message_index);
length += 1 + varstring_length(ciphertext_length); length += 1 + varstring_length(ciphertext_length);
length += mac_length; length += mac_length;
length += signature_length;
return length; return length;
} }
@ -381,11 +383,12 @@ size_t _olm_encode_group_message(
void _olm_decode_group_message( void _olm_decode_group_message(
const uint8_t *input, size_t input_length, const uint8_t *input, size_t input_length,
size_t mac_length, size_t mac_length, size_t signature_length,
struct _OlmDecodeGroupMessageResults *results struct _OlmDecodeGroupMessageResults *results
) { ) {
std::uint8_t const * pos = input; std::uint8_t const * pos = input;
std::uint8_t const * end = input + input_length - mac_length; 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; std::uint8_t const * unknown = nullptr;
bool has_message_index = false; bool has_message_index = false;
@ -393,8 +396,7 @@ void _olm_decode_group_message(
results->ciphertext = nullptr; results->ciphertext = nullptr;
results->ciphertext_length = 0; results->ciphertext_length = 0;
if (pos == end) return; if (input_length < trailer_length) return;
if (input_length < mac_length) return;
results->version = *(pos++); results->version = *(pos++);
while (pos != end) { while (pos != end) {

View file

@ -99,6 +99,11 @@ std::size_t b64_input(
extern "C" { 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() { size_t olm_error() {
return std::size_t(-1); return std::size_t(-1);
@ -432,14 +437,14 @@ size_t olm_create_outbound_session(
std::size_t id_key_length = their_identity_key_length; std::size_t id_key_length = their_identity_key_length;
std::size_t ot_key_length = their_one_time_key_length; std::size_t ot_key_length = their_one_time_key_length;
if (olm::decode_base64_length(id_key_length) != olm::KEY_LENGTH if (olm::decode_base64_length(id_key_length) != CURVE25519_KEY_LENGTH
|| olm::decode_base64_length(ot_key_length) != olm::KEY_LENGTH || olm::decode_base64_length(ot_key_length) != CURVE25519_KEY_LENGTH
) { ) {
from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64; from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64;
return std::size_t(-1); return std::size_t(-1);
} }
olm::Curve25519PublicKey identity_key; _olm_curve25519_public_key identity_key;
olm::Curve25519PublicKey one_time_key; _olm_curve25519_public_key one_time_key;
olm::decode_base64(id_key, id_key_length, identity_key.public_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); olm::decode_base64(ot_key, ot_key_length, one_time_key.public_key);
@ -479,11 +484,11 @@ size_t olm_create_inbound_session_from(
std::uint8_t const * id_key = from_c(their_identity_key); std::uint8_t const * id_key = from_c(their_identity_key);
std::size_t id_key_length = their_identity_key_length; std::size_t id_key_length = their_identity_key_length;
if (olm::decode_base64_length(id_key_length) != olm::KEY_LENGTH) { if (olm::decode_base64_length(id_key_length) != CURVE25519_KEY_LENGTH) {
from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64; from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64;
return std::size_t(-1); 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); olm::decode_base64(id_key, id_key_length, identity_key.public_key);
std::size_t raw_length = b64_input( std::size_t raw_length = b64_input(
@ -525,6 +530,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( size_t olm_matches_inbound_session(
OlmSession * session, OlmSession * session,
void * one_time_key_message, size_t message_length void * one_time_key_message, size_t message_length
@ -550,11 +561,11 @@ size_t olm_matches_inbound_session_from(
std::uint8_t const * id_key = from_c(their_identity_key); std::uint8_t const * id_key = from_c(their_identity_key);
std::size_t id_key_length = their_identity_key_length; std::size_t id_key_length = their_identity_key_length;
if (olm::decode_base64_length(id_key_length) != olm::KEY_LENGTH) { if (olm::decode_base64_length(id_key_length) != CURVE25519_KEY_LENGTH) {
from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64; from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64;
return std::size_t(-1); 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); olm::decode_base64(id_key, id_key_length, identity_key.public_key);
std::size_t raw_length = b64_input( std::size_t raw_length = b64_input(
@ -706,11 +717,11 @@ size_t olm_ed25519_verify(
void const * message, size_t message_length, void const * message, size_t message_length,
void * signature, size_t signature_length void * signature, size_t signature_length
) { ) {
if (olm::decode_base64_length(key_length) != olm::KEY_LENGTH) { if (olm::decode_base64_length(key_length) != CURVE25519_KEY_LENGTH) {
from_c(utility)->last_error = OlmErrorCode::OLM_INVALID_BASE64; from_c(utility)->last_error = OlmErrorCode::OLM_INVALID_BASE64;
return std::size_t(-1); 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); olm::decode_base64(from_c(key), key_length, verify_key.public_key);
std::size_t raw_signature_length = b64_input( std::size_t raw_signature_length = b64_input(
from_c(signature), signature_length, from_c(utility)->last_error from_c(signature), signature_length, from_c(utility)->last_error

View file

@ -20,6 +20,7 @@
#include "olm/base64.h" #include "olm/base64.h"
#include "olm/cipher.h" #include "olm/cipher.h"
#include "olm/crypto.h"
#include "olm/error.h" #include "olm/error.h"
#include "olm/megolm.h" #include "olm/megolm.h"
#include "olm/memory.h" #include "olm/memory.h"
@ -28,16 +29,16 @@
#include "olm/pickle_encoding.h" #include "olm/pickle_encoding.h"
#define OLM_PROTOCOL_VERSION 3 #define OLM_PROTOCOL_VERSION 3
#define SESSION_ID_RANDOM_BYTES 4 #define GROUP_SESSION_ID_LENGTH ED25519_PUBLIC_KEY_LENGTH
#define GROUP_SESSION_ID_LENGTH (sizeof(struct timeval) + SESSION_ID_RANDOM_BYTES)
#define PICKLE_VERSION 1 #define PICKLE_VERSION 1
#define SESSION_KEY_VERSION 2
struct OlmOutboundGroupSession { struct OlmOutboundGroupSession {
/** the Megolm ratchet providing the encryption keys */ /** the Megolm ratchet providing the encryption keys */
Megolm ratchet; Megolm ratchet;
/** unique identifier for this session */ /** The ed25519 keypair used for signing the messages */
uint8_t session_id[GROUP_SESSION_ID_LENGTH]; struct _olm_ed25519_key_pair signing_key;
enum OlmErrorCode last_error; enum OlmErrorCode last_error;
}; };
@ -74,8 +75,7 @@ static size_t raw_pickle_length(
size_t length = 0; size_t length = 0;
length += _olm_pickle_uint32_length(PICKLE_VERSION); length += _olm_pickle_uint32_length(PICKLE_VERSION);
length += megolm_pickle_length(&(session->ratchet)); length += megolm_pickle_length(&(session->ratchet));
length += _olm_pickle_bytes_length(session->session_id, length += _olm_pickle_ed25519_key_pair_length(&(session->signing_key));
GROUP_SESSION_ID_LENGTH);
return length; return length;
} }
@ -101,7 +101,7 @@ size_t olm_pickle_outbound_group_session(
pos = _olm_enc_output_pos(pickled, raw_length); pos = _olm_enc_output_pos(pickled, raw_length);
pos = _olm_pickle_uint32(pos, PICKLE_VERSION); pos = _olm_pickle_uint32(pos, PICKLE_VERSION);
pos = megolm_pickle(&(session->ratchet), pos); pos = megolm_pickle(&(session->ratchet), pos);
pos = _olm_pickle_bytes(pos, session->session_id, GROUP_SESSION_ID_LENGTH); pos = _olm_pickle_ed25519_key_pair(pos, &(session->signing_key));
return _olm_enc_output(key, key_length, pickled, raw_length); return _olm_enc_output(key, key_length, pickled, raw_length);
} }
@ -130,7 +130,7 @@ size_t olm_unpickle_outbound_group_session(
return (size_t)-1; return (size_t)-1;
} }
pos = megolm_unpickle(&(session->ratchet), pos, end); pos = megolm_unpickle(&(session->ratchet), pos, end);
pos = _olm_unpickle_bytes(pos, end, session->session_id, GROUP_SESSION_ID_LENGTH); pos = _olm_unpickle_ed25519_key_pair(pos, end, &(session->signing_key));
if (end != pos) { if (end != pos) {
/* We had the wrong number of bytes in the input. */ /* We had the wrong number of bytes in the input. */
@ -148,7 +148,8 @@ size_t olm_init_outbound_group_session_random_length(
/* we need data to initialize the megolm ratchet, plus some more for the /* we need data to initialize the megolm ratchet, plus some more for the
* session id. * session id.
*/ */
return MEGOLM_RATCHET_LENGTH + SESSION_ID_RANDOM_BYTES; return MEGOLM_RATCHET_LENGTH +
ED25519_RANDOM_LENGTH;
} }
size_t olm_init_outbound_group_session( size_t olm_init_outbound_group_session(
@ -164,12 +165,8 @@ size_t olm_init_outbound_group_session(
megolm_init(&(session->ratchet), random, 0); megolm_init(&(session->ratchet), random, 0);
random += MEGOLM_RATCHET_LENGTH; random += MEGOLM_RATCHET_LENGTH;
/* initialise the session id. This just has to be unique. We use the _olm_crypto_ed25519_generate_key(random, &(session->signing_key));
* current time plus some random data. random += ED25519_RANDOM_LENGTH;
*/
gettimeofday((struct timeval *)(session->session_id), NULL);
memcpy((session->session_id) + sizeof(struct timeval),
random, SESSION_ID_RANDOM_BYTES);
return 0; return 0;
} }
@ -188,7 +185,8 @@ static size_t raw_message_length(
return _olm_encode_group_message_length( return _olm_encode_group_message_length(
session->ratchet.counter, session->ratchet.counter,
ciphertext_length, mac_length); ciphertext_length, mac_length, ED25519_SIGNATURE_LENGTH
);
} }
size_t olm_group_encrypt_message_length( size_t olm_group_encrypt_message_length(
@ -241,6 +239,13 @@ static size_t _encrypt(
megolm_advance(&(session->ratchet)); 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; return result;
} }
@ -293,7 +298,9 @@ size_t olm_outbound_group_session_id(
return (size_t)-1; return (size_t)-1;
} }
return _olm_encode_base64(session->session_id, GROUP_SESSION_ID_LENGTH, id); return _olm_encode_base64(
session->signing_key.public_key.public_key, GROUP_SESSION_ID_LENGTH, id
);
} }
uint32_t olm_outbound_group_session_message_index( uint32_t olm_outbound_group_session_message_index(
@ -302,23 +309,53 @@ uint32_t olm_outbound_group_session_message_index(
return session->ratchet.counter; 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( size_t olm_outbound_group_session_key_length(
const OlmOutboundGroupSession *session const OlmOutboundGroupSession *session
) { ) {
return _olm_encode_base64_length(MEGOLM_RATCHET_LENGTH); return _olm_encode_base64_length(SESSION_KEY_RAW_LENGTH);
} }
size_t olm_outbound_group_session_key( size_t olm_outbound_group_session_key(
OlmOutboundGroupSession *session, OlmOutboundGroupSession *session,
uint8_t * key, size_t key_length uint8_t * key, size_t key_length
) { ) {
if (key_length < olm_outbound_group_session_key_length(session)) { 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; session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL;
return (size_t)-1; return (size_t)-1;
} }
return _olm_encode_base64( /* put the raw data at the end of the output buffer. */
megolm_get_data(&session->ratchet), raw = ptr = key + encoded_length - SESSION_KEY_RAW_LENGTH;
MEGOLM_RATCHET_LENGTH, key *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);
} }

View file

@ -71,7 +71,7 @@ std::uint8_t const * olm::unpickle_bytes(
std::size_t olm::pickle_length( std::size_t olm::pickle_length(
const olm::Curve25519PublicKey & value const _olm_curve25519_public_key & value
) { ) {
return sizeof(value.public_key); return sizeof(value.public_key);
} }
@ -79,7 +79,7 @@ std::size_t olm::pickle_length(
std::uint8_t * olm::pickle( std::uint8_t * olm::pickle(
std::uint8_t * pos, std::uint8_t * pos,
const olm::Curve25519PublicKey & value const _olm_curve25519_public_key & value
) { ) {
pos = olm::pickle_bytes( pos = olm::pickle_bytes(
pos, value.public_key, sizeof(value.public_key) pos, value.public_key, sizeof(value.public_key)
@ -90,7 +90,7 @@ std::uint8_t * olm::pickle(
std::uint8_t const * olm::unpickle( std::uint8_t const * olm::unpickle(
std::uint8_t const * pos, std::uint8_t const * end, std::uint8_t const * pos, std::uint8_t const * end,
olm::Curve25519PublicKey & value _olm_curve25519_public_key & value
) { ) {
pos = olm::unpickle_bytes( pos = olm::unpickle_bytes(
pos, end, value.public_key, sizeof(value.public_key) pos, end, value.public_key, sizeof(value.public_key)
@ -101,21 +101,24 @@ std::uint8_t const * olm::unpickle(
std::size_t olm::pickle_length( 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 * olm::pickle(
std::uint8_t * pos, std::uint8_t * pos,
const olm::Curve25519KeyPair & value const _olm_curve25519_key_pair & value
) { ) {
pos = olm::pickle_bytes( 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 = 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; return pos;
} }
@ -123,83 +126,89 @@ std::uint8_t * olm::pickle(
std::uint8_t const * olm::unpickle( std::uint8_t const * olm::unpickle(
std::uint8_t const * pos, std::uint8_t const * end, std::uint8_t const * pos, std::uint8_t const * end,
olm::Curve25519KeyPair & value _olm_curve25519_key_pair & value
) { ) {
pos = olm::unpickle_bytes( 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 = 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
) {
return sizeof(value.public_key);
}
std::uint8_t * olm::pickle(
std::uint8_t * pos,
const olm::Ed25519PublicKey & value
) {
pos = olm::pickle_bytes(
pos, value.public_key, sizeof(value.public_key)
);
return pos;
}
std::uint8_t const * olm::unpickle(
std::uint8_t const * pos, std::uint8_t const * end,
olm::Ed25519PublicKey & value
) {
pos = olm::unpickle_bytes(
pos, end, value.public_key, sizeof(value.public_key)
);
return pos;
}
std::size_t olm::pickle_length(
const olm::Ed25519KeyPair & value
) {
return sizeof(value.public_key) + sizeof(value.private_key);
}
std::uint8_t * olm::pickle(
std::uint8_t * pos,
const olm::Ed25519KeyPair & value
) {
pos = olm::pickle_bytes(
pos, value.public_key, sizeof(value.public_key)
);
pos = olm::pickle_bytes(
pos, value.private_key, sizeof(value.private_key)
);
return pos;
}
std::uint8_t const * olm::unpickle(
std::uint8_t const * pos, std::uint8_t const * end,
olm::Ed25519KeyPair & value
) {
pos = olm::unpickle_bytes(
pos, end, value.public_key, sizeof(value.public_key)
);
pos = olm::unpickle_bytes(
pos, end, value.private_key, sizeof(value.private_key)
); );
return pos; return pos;
} }
////// pickle.h implementations ////// pickle.h implementations
std::size_t _olm_pickle_ed25519_public_key_length(
const _olm_ed25519_public_key * value
) {
return sizeof(value->public_key);
}
std::uint8_t * _olm_pickle_ed25519_public_key(
std::uint8_t * pos,
const _olm_ed25519_public_key *value
) {
pos = olm::pickle_bytes(
pos, value->public_key, sizeof(value->public_key)
);
return pos;
}
std::uint8_t const * _olm_unpickle_ed25519_public_key(
std::uint8_t const * pos, std::uint8_t const * end,
_olm_ed25519_public_key * value
) {
pos = olm::unpickle_bytes(
pos, end, value->public_key, sizeof(value->public_key)
);
return pos;
}
std::size_t _olm_pickle_ed25519_key_pair_length(
const _olm_ed25519_key_pair *value
) {
return sizeof(value->public_key.public_key)
+ sizeof(value->private_key.private_key);
}
std::uint8_t * _olm_pickle_ed25519_key_pair(
std::uint8_t * pos,
const _olm_ed25519_key_pair *value
) {
pos = olm::pickle_bytes(
pos, value->public_key.public_key,
sizeof(value->public_key.public_key)
);
pos = olm::pickle_bytes(
pos, value->private_key.private_key,
sizeof(value->private_key.private_key)
);
return pos;
}
std::uint8_t const * _olm_unpickle_ed25519_key_pair(
std::uint8_t const * pos, std::uint8_t const * end,
_olm_ed25519_key_pair *value
) {
pos = olm::unpickle_bytes(
pos, end, value->public_key.public_key,
sizeof(value->public_key.public_key)
);
pos = olm::unpickle_bytes(
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) { uint8_t * _olm_pickle_uint32(uint8_t * pos, uint32_t value) {
return olm::pickle(pos, value); return olm::pickle(pos, value);
} }

View file

@ -44,15 +44,15 @@ static const std::size_t MAX_MESSAGE_GAP = 2000;
*/ */
static void create_chain_key( static void create_chain_key(
olm::SharedKey const & root_key, olm::SharedKey const & root_key,
olm::Curve25519KeyPair const & our_key, _olm_curve25519_key_pair const & our_key,
olm::Curve25519PublicKey const & their_key, _olm_curve25519_public_key const & their_key,
olm::KdfInfo const & info, olm::KdfInfo const & info,
olm::SharedKey & new_root_key, olm::SharedKey & new_root_key,
olm::ChainKey & new_chain_key olm::ChainKey & new_chain_key
) { ) {
olm::SharedKey secret; olm::SharedKey secret;
olm::curve25519_shared_secret(our_key, their_key, secret); _olm_crypto_curve25519_shared_secret(&our_key, &their_key, secret);
std::uint8_t derived_secrets[2 * olm::KEY_LENGTH]; std::uint8_t derived_secrets[2 * olm::OLM_SHARED_KEY_LENGTH];
_olm_crypto_hkdf_sha256( _olm_crypto_hkdf_sha256(
secret, sizeof(secret), secret, sizeof(secret),
root_key, sizeof(root_key), root_key, sizeof(root_key),
@ -203,9 +203,9 @@ olm::Ratchet::Ratchet(
void olm::Ratchet::initialise_as_bob( void olm::Ratchet::initialise_as_bob(
std::uint8_t const * shared_secret, std::size_t shared_secret_length, 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]; std::uint8_t derived_secrets[2 * olm::OLM_SHARED_KEY_LENGTH];
_olm_crypto_hkdf_sha256( _olm_crypto_hkdf_sha256(
shared_secret, shared_secret_length, shared_secret, shared_secret_length,
nullptr, 0, nullptr, 0,
@ -226,9 +226,9 @@ void olm::Ratchet::initialise_as_bob(
void olm::Ratchet::initialise_as_alice( void olm::Ratchet::initialise_as_alice(
std::uint8_t const * shared_secret, std::size_t shared_secret_length, 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]; std::uint8_t derived_secrets[2 * olm::OLM_SHARED_KEY_LENGTH];
_olm_crypto_hkdf_sha256( _olm_crypto_hkdf_sha256(
shared_secret, shared_secret_length, shared_secret, shared_secret_length,
nullptr, 0, nullptr, 0,
@ -252,7 +252,7 @@ namespace olm {
static std::size_t pickle_length( static std::size_t pickle_length(
const olm::SharedKey & value const olm::SharedKey & value
) { ) {
return olm::KEY_LENGTH; return olm::OLM_SHARED_KEY_LENGTH;
} }
@ -260,7 +260,7 @@ static std::uint8_t * pickle(
std::uint8_t * pos, std::uint8_t * pos,
const olm::SharedKey & value const olm::SharedKey & value
) { ) {
return olm::pickle_bytes(pos, value, olm::KEY_LENGTH); return olm::pickle_bytes(pos, value, olm::OLM_SHARED_KEY_LENGTH);
} }
@ -268,7 +268,7 @@ static std::uint8_t const * unpickle(
std::uint8_t const * pos, std::uint8_t const * end, std::uint8_t const * pos, std::uint8_t const * end,
olm::SharedKey & value 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);
} }
@ -377,7 +377,7 @@ std::size_t olm::pickle_length(
olm::Ratchet const & value olm::Ratchet const & value
) { ) {
std::size_t length = 0; 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.sender_chain);
length += olm::pickle_length(value.receiver_chains); length += olm::pickle_length(value.receiver_chains);
length += olm::pickle_length(value.skipped_message_keys); length += olm::pickle_length(value.skipped_message_keys);
@ -428,13 +428,13 @@ std::size_t olm::Ratchet::encrypt_output_length(
plaintext_length plaintext_length
); );
return olm::encode_message_length( return olm::encode_message_length(
counter, olm::KEY_LENGTH, padded, ratchet_cipher->ops->mac_length(ratchet_cipher) counter, CURVE25519_KEY_LENGTH, padded, ratchet_cipher->ops->mac_length(ratchet_cipher)
); );
} }
std::size_t olm::Ratchet::encrypt_random_length() { std::size_t olm::Ratchet::encrypt_random_length() {
return sender_chain.empty() ? olm::KEY_LENGTH : 0; return sender_chain.empty() ? CURVE25519_RANDOM_LENGTH : 0;
} }
@ -456,10 +456,15 @@ std::size_t olm::Ratchet::encrypt(
if (sender_chain.empty()) { if (sender_chain.empty()) {
sender_chain.insert(); sender_chain.insert();
olm::curve25519_generate_key(random, sender_chain[0].ratchet_key); _olm_crypto_curve25519_generate_key(random, &sender_chain[0].ratchet_key);
olm_logf(OLM_LOG_DEBUG, LOG_CATEGORY, "Created new ratchet key T(%i) %s", olm_logf(
chain_index + 1, OLM_LOG_DEBUG, LOG_CATEGORY, "Created new ratchet key T(%i) %s",
sender_chain[0].ratchet_key.to_string().c_str()); chain_index + 1,
olm::bytes_to_string(
std::begin(sender_chain[0].ratchet_key.public_key.public_key),
std::end(sender_chain[0].ratchet_key.public_key.public_key)
).c_str()
);
create_chain_key( create_chain_key(
root_key, root_key,
sender_chain[0].ratchet_key, sender_chain[0].ratchet_key,
@ -481,12 +486,14 @@ std::size_t olm::Ratchet::encrypt(
plaintext_length plaintext_length
); );
std::uint32_t counter = keys.index; 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::MessageWriter writer;
olm::encode_message( olm::encode_message(
writer, PROTOCOL_VERSION, counter, olm::KEY_LENGTH, ciphertext_length, writer, PROTOCOL_VERSION, counter, CURVE25519_KEY_LENGTH,
ciphertext_length,
output output
); );
@ -500,12 +507,13 @@ std::size_t olm::Ratchet::encrypt(
output, output_length output, output_length
); );
olm_logf(OLM_LOG_TRACE, LOG_CATEGORY, olm_logf(
"Encoded message ver=%i ratchet_key=%s chain_idx=%i ciphertext=%s", OLM_LOG_TRACE, LOG_CATEGORY,
PROTOCOL_VERSION, "Encoded message ver=%i ratchet_key=%s chain_idx=%i ciphertext=%s",
olm::bytes_to_string(writer.ratchet_key, olm::KEY_LENGTH).c_str(), PROTOCOL_VERSION,
counter, olm::bytes_to_string(writer.ratchet_key, CURVE25519_KEY_LENGTH).c_str(),
olm::bytes_to_string(writer.ciphertext, ciphertext_length).c_str() counter,
olm::bytes_to_string(writer.ciphertext, ciphertext_length).c_str()
); );
olm::unset(keys); olm::unset(keys);
@ -566,7 +574,7 @@ std::size_t olm::Ratchet::decrypt(
return std::size_t(-1); return std::size_t(-1);
} }
if (reader.ratchet_key_length != olm::KEY_LENGTH) { if (reader.ratchet_key_length != CURVE25519_KEY_LENGTH) {
last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT; last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT;
return std::size_t(-1); return std::size_t(-1);
} }
@ -582,7 +590,7 @@ std::size_t olm::Ratchet::decrypt(
for (olm::ReceiverChain & receiver_chain : receiver_chains) { for (olm::ReceiverChain & receiver_chain : receiver_chains) {
if (0 == std::memcmp( if (0 == std::memcmp(
receiver_chain.ratchet_key.public_key, reader.ratchet_key, receiver_chain.ratchet_key.public_key, reader.ratchet_key,
olm::KEY_LENGTH CURVE25519_KEY_LENGTH
)) { )) {
chain = &receiver_chain; chain = &receiver_chain;
break; break;
@ -605,7 +613,7 @@ std::size_t olm::Ratchet::decrypt(
if (reader.counter == skipped.message_key.index if (reader.counter == skipped.message_key.index
&& 0 == std::memcmp( && 0 == std::memcmp(
skipped.ratchet_key.public_key, reader.ratchet_key, skipped.ratchet_key.public_key, reader.ratchet_key,
olm::KEY_LENGTH CURVE25519_KEY_LENGTH
) )
) { ) {
/* Found the key for this message. Check the MAC. */ /* Found the key for this message. Check the MAC. */

View file

@ -14,7 +14,7 @@
*/ */
#include "olm/session.hh" #include "olm/session.hh"
#include "olm/cipher.h" #include "olm/cipher.h"
#include "olm/crypto.hh" #include "olm/crypto.h"
#include "olm/account.hh" #include "olm/account.hh"
#include "olm/logging.h" #include "olm/logging.h"
#include "olm/memory.hh" #include "olm/memory.hh"
@ -41,6 +41,23 @@ static const olm::KdfInfo OLM_KDF_INFO = {
static const struct _olm_cipher_aes_sha_256 OLM_CIPHER = static const struct _olm_cipher_aes_sha_256 OLM_CIPHER =
OLM_CIPHER_INIT_AES_SHA_256(CIPHER_KDF_INFO); OLM_CIPHER_INIT_AES_SHA_256(CIPHER_KDF_INFO);
static std::string to_string(const _olm_curve25519_public_key *key) {
return olm::bytes_to_string(
std::begin(key->public_key),
std::end(key->public_key)
);
};
static std::string to_string(const _olm_curve25519_key_pair *key) {
return olm::bytes_to_string(
std::begin(key->public_key.public_key),
std::end(key->public_key.public_key)
);
};
} // namespace } // namespace
olm::Session::Session( olm::Session::Session(
@ -52,14 +69,14 @@ olm::Session::Session(
std::size_t olm::Session::new_outbound_session_random_length() { 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( std::size_t olm::Session::new_outbound_session(
olm::Account const & local_account, olm::Account const & local_account,
olm::Curve25519PublicKey const & identity_key, _olm_curve25519_public_key const & identity_key,
olm::Curve25519PublicKey const & one_time_key, _olm_curve25519_public_key const & one_time_key,
std::uint8_t const * random, std::size_t random_length std::uint8_t const * random, std::size_t random_length
) { ) {
if (random_length < new_outbound_session_random_length()) { if (random_length < new_outbound_session_random_length()) {
@ -69,37 +86,38 @@ std::size_t olm::Session::new_outbound_session(
olm_logf(OLM_LOG_DEBUG, LOG_CATEGORY, olm_logf(OLM_LOG_DEBUG, LOG_CATEGORY,
"Creating new outbound session to receiver identity IB %s, " "Creating new outbound session to receiver identity IB %s, "
"receiver ephemeral EB %s", identity_key.to_string().c_str(), "receiver ephemeral EB %s", to_string(&identity_key).c_str(),
one_time_key.to_string().c_str() to_string(&one_time_key).c_str()
); );
olm::Curve25519KeyPair base_key; _olm_curve25519_key_pair base_key;
olm::curve25519_generate_key(random, base_key); _olm_crypto_curve25519_generate_key(random, &base_key);
olm_logf(OLM_LOG_DEBUG, LOG_CATEGORY, "Created new ephemeral key EA %s", olm_logf(OLM_LOG_DEBUG, LOG_CATEGORY, "Created new ephemeral key EA %s",
base_key.to_string().c_str()); to_string(&base_key).c_str());
olm::Curve25519KeyPair ratchet_key; _olm_curve25519_key_pair ratchet_key;
olm::curve25519_generate_key(random + olm::KEY_LENGTH, ratchet_key); _olm_crypto_curve25519_generate_key(random + CURVE25519_RANDOM_LENGTH, &ratchet_key);
olm_logf(OLM_LOG_DEBUG, LOG_CATEGORY, "Created new ratchet key T(0) %s", olm_logf(OLM_LOG_DEBUG, LOG_CATEGORY, "Created new ratchet key T(0) %s",
ratchet_key.to_string().c_str()); to_string(&ratchet_key).c_str());
olm::Curve25519KeyPair const & alice_identity_key_pair = ( _olm_curve25519_key_pair const & alice_identity_key_pair = (
local_account.identity_keys.curve25519_key local_account.identity_keys.curve25519_key
); );
received_message = false; received_message = false;
alice_identity_key = alice_identity_key_pair; alice_identity_key = alice_identity_key_pair.public_key;
alice_base_key = base_key; alice_base_key = base_key.public_key;
bob_one_time_key = one_time_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; std::uint8_t * pos = secret;
olm::curve25519_shared_secret(alice_identity_key_pair, one_time_key, pos); _olm_crypto_curve25519_shared_secret(&alice_identity_key_pair, &one_time_key, pos);
pos += olm::KEY_LENGTH; pos += CURVE25519_SHARED_SECRET_LENGTH;
olm::curve25519_shared_secret(base_key, identity_key, pos); _olm_crypto_curve25519_shared_secret(&base_key, &identity_key, pos);
pos += olm::KEY_LENGTH; pos += CURVE25519_SHARED_SECRET_LENGTH;
olm::curve25519_shared_secret(base_key, one_time_key, pos); _olm_crypto_curve25519_shared_secret(&base_key, &one_time_key, pos);
ratchet.initialise_as_alice(secret, sizeof(secret), ratchet_key); ratchet.initialise_as_alice(secret, sizeof(secret), ratchet_key);
@ -119,13 +137,13 @@ static bool check_message_fields(
bool ok = true; bool ok = true;
ok = ok && (have_their_identity_key || reader.identity_key); ok = ok && (have_their_identity_key || reader.identity_key);
if (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.message;
ok = ok && reader.base_key; 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;
ok = ok && reader.one_time_key_length == olm::KEY_LENGTH; ok = ok && reader.one_time_key_length == CURVE25519_KEY_LENGTH;
return ok; return ok;
} }
@ -134,7 +152,7 @@ static bool check_message_fields(
std::size_t olm::Session::new_inbound_session( std::size_t olm::Session::new_inbound_session(
olm::Account & local_account, 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 std::uint8_t const * one_time_key_message, std::size_t message_length
) { ) {
olm::PreKeyMessageReader reader; olm::PreKeyMessageReader reader;
@ -147,16 +165,17 @@ std::size_t olm::Session::new_inbound_session(
if (reader.identity_key && their_identity_key) { if (reader.identity_key && their_identity_key) {
bool same = 0 == std::memcmp( 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) { if (!same) {
olm_logf(OLM_LOG_INFO, LOG_CATEGORY, olm_logf(OLM_LOG_INFO, LOG_CATEGORY,
"Identity key on received message is incorrect " "Identity key on received message is incorrect "
"(expected %s, got %s)", "(expected %s, got %s)",
their_identity_key->to_string().c_str(), to_string(their_identity_key).c_str(),
olm::bytes_to_string(reader.identity_key, olm::bytes_to_string(
reader.identity_key + olm::KEY_LENGTH) reader.identity_key,
.c_str()); reader.identity_key + CURVE25519_KEY_LENGTH
).c_str());
last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID; last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID;
return std::size_t(-1); return std::size_t(-1);
} }
@ -169,9 +188,9 @@ std::size_t olm::Session::new_inbound_session(
olm_logf(OLM_LOG_DEBUG, LOG_CATEGORY, olm_logf(OLM_LOG_DEBUG, LOG_CATEGORY,
"Creating new inbound session from sender identity IA %s, " "Creating new inbound session from sender identity IA %s, "
"sender ephemeral EA %s, our ephemeral EB %s", "sender ephemeral EA %s, our ephemeral EB %s",
alice_identity_key.to_string().c_str(), to_string(&alice_identity_key).c_str(),
alice_base_key.to_string().c_str(), to_string(&alice_base_key).c_str(),
bob_one_time_key.to_string().c_str()); to_string(&bob_one_time_key).c_str());
olm::MessageReader message_reader; olm::MessageReader message_reader;
decode_message( decode_message(
@ -180,16 +199,16 @@ std::size_t olm::Session::new_inbound_session(
); );
if (!message_reader.ratchet_key if (!message_reader.ratchet_key
|| message_reader.ratchet_key_length != olm::KEY_LENGTH) { || message_reader.ratchet_key_length != CURVE25519_KEY_LENGTH) {
last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT; last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT;
return std::size_t(-1); return std::size_t(-1);
} }
olm::Curve25519PublicKey ratchet_key; _olm_curve25519_public_key ratchet_key;
olm::load_array(ratchet_key.public_key, message_reader.ratchet_key); olm::load_array(ratchet_key.public_key, message_reader.ratchet_key);
olm_logf(OLM_LOG_DEBUG, LOG_CATEGORY, olm_logf(OLM_LOG_DEBUG, LOG_CATEGORY,
"Received ratchet key T(0) %s", ratchet_key.to_string().c_str()); "Received ratchet key T(0) %s", to_string(&ratchet_key).c_str());
olm::OneTimeKey const * our_one_time_key = local_account.lookup_key( olm::OneTimeKey const * our_one_time_key = local_account.lookup_key(
bob_one_time_key bob_one_time_key
@ -198,23 +217,24 @@ std::size_t olm::Session::new_inbound_session(
if (!our_one_time_key) { if (!our_one_time_key) {
olm_logf(OLM_LOG_INFO, LOG_CATEGORY, olm_logf(OLM_LOG_INFO, LOG_CATEGORY,
"Session uses unknown ephemeral key %s", "Session uses unknown ephemeral key %s",
bob_one_time_key.to_string().c_str()); to_string(&bob_one_time_key).c_str());
last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID; last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID;
return std::size_t(-1); 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 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; std::uint8_t * pos = secret;
olm::curve25519_shared_secret(bob_one_time_key, alice_identity_key, pos); _olm_crypto_curve25519_shared_secret(&bob_one_time_key, &alice_identity_key, pos);
pos += olm::KEY_LENGTH; pos += CURVE25519_SHARED_SECRET_LENGTH;
olm::curve25519_shared_secret(bob_identity_key, alice_base_key, pos); _olm_crypto_curve25519_shared_secret(&bob_identity_key, &alice_base_key, pos);
pos += olm::KEY_LENGTH; pos += CURVE25519_SHARED_SECRET_LENGTH;
olm::curve25519_shared_secret(bob_one_time_key, alice_base_key, pos); _olm_crypto_curve25519_shared_secret(&bob_one_time_key, &alice_base_key, pos);
ratchet.initialise_as_bob(secret, sizeof(secret), ratchet_key); ratchet.initialise_as_bob(secret, sizeof(secret), ratchet_key);
@ -237,7 +257,7 @@ std::size_t olm::Session::session_id(
last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL;
return std::size_t(-1); 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; std::uint8_t * pos = tmp;
pos = olm::store_array(pos, alice_identity_key.public_key); 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, alice_base_key.public_key);
@ -248,7 +268,7 @@ std::size_t olm::Session::session_id(
bool olm::Session::matches_inbound_session( 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 std::uint8_t const * one_time_key_message, std::size_t message_length
) { ) {
olm::PreKeyMessageReader reader; olm::PreKeyMessageReader reader;
@ -261,20 +281,20 @@ bool olm::Session::matches_inbound_session(
bool same = true; bool same = true;
if (reader.identity_key) { if (reader.identity_key) {
same = same && 0 == std::memcmp( 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) { if (their_identity_key) {
same = same && 0 == std::memcmp( same = same && 0 == std::memcmp(
their_identity_key->public_key, alice_identity_key.public_key, their_identity_key->public_key, alice_identity_key.public_key,
olm::KEY_LENGTH CURVE25519_KEY_LENGTH
); );
} }
same = same && 0 == std::memcmp( 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( 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; return same;
} }
@ -301,9 +321,9 @@ std::size_t olm::Session::encrypt_message_length(
} }
return encode_one_time_key_message_length( return encode_one_time_key_message_length(
olm::KEY_LENGTH, CURVE25519_KEY_LENGTH,
olm::KEY_LENGTH, CURVE25519_KEY_LENGTH,
olm::KEY_LENGTH, CURVE25519_KEY_LENGTH,
message_length message_length
); );
} }
@ -338,9 +358,9 @@ std::size_t olm::Session::encrypt(
encode_one_time_key_message( encode_one_time_key_message(
writer, writer,
PROTOCOL_VERSION, PROTOCOL_VERSION,
olm::KEY_LENGTH, CURVE25519_KEY_LENGTH,
olm::KEY_LENGTH, CURVE25519_KEY_LENGTH,
olm::KEY_LENGTH, CURVE25519_KEY_LENGTH,
message_body_length, message_body_length,
message message
); );
@ -354,9 +374,9 @@ std::size_t olm::Session::encrypt(
"Encoded pre-key message ver=%i one_time_key[Eb]=%s " "Encoded pre-key message ver=%i one_time_key[Eb]=%s "
"base_key[Ea]=%s identity_key[Ia]=%s", "base_key[Ea]=%s identity_key[Ia]=%s",
PROTOCOL_VERSION, PROTOCOL_VERSION,
olm::bytes_to_string(writer.one_time_key, olm::KEY_LENGTH).c_str(), olm::bytes_to_string(writer.one_time_key, CURVE25519_KEY_LENGTH).c_str(),
olm::bytes_to_string(writer.base_key, olm::KEY_LENGTH).c_str(), olm::bytes_to_string(writer.base_key, CURVE25519_KEY_LENGTH).c_str(),
olm::bytes_to_string(writer.identity_key, olm::KEY_LENGTH).c_str() olm::bytes_to_string(writer.identity_key, CURVE25519_KEY_LENGTH).c_str()
); );
} }

View file

@ -14,7 +14,7 @@
*/ */
#include "olm/utility.hh" #include "olm/utility.hh"
#include "olm/crypto.hh" #include "olm/crypto.h"
olm::Utility::Utility( olm::Utility::Utility(
@ -41,15 +41,15 @@ size_t olm::Utility::sha256(
size_t olm::Utility::ed25519_verify( 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 * message, std::size_t message_length,
std::uint8_t const * signature, std::size_t signature_length std::uint8_t const * signature, std::size_t signature_length
) { ) {
if (signature_length < olm::SIGNATURE_LENGTH) { if (signature_length < ED25519_SIGNATURE_LENGTH) {
last_error = OlmErrorCode::OLM_BAD_MESSAGE_MAC; last_error = OlmErrorCode::OLM_BAD_MESSAGE_MAC;
return std::size_t(-1); return std::size_t(-1);
} }
if (!olm::ed25519_verify(key, message, message_length, signature)) { if (!_olm_crypto_ed25519_verify(&key, message, message_length, signature)) {
last_error = OlmErrorCode::OLM_BAD_MESSAGE_MAC; last_error = OlmErrorCode::OLM_BAD_MESSAGE_MAC;
return std::size_t(-1); return std::size_t(-1);
} }

View file

@ -12,7 +12,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#include "olm/crypto.hh" #include "olm/crypto.h"
#include "unittest.hh" #include "unittest.hh"
@ -58,60 +58,31 @@ std::uint8_t expected_agreement[32] = {
0x76, 0xF0, 0x9B, 0x3C, 0x1E, 0x16, 0x17, 0x42 0x76, 0xF0, 0x9B, 0x3C, 0x1E, 0x16, 0x17, 0x42
}; };
olm::Curve25519KeyPair alice_pair; _olm_curve25519_key_pair alice_pair;
olm::curve25519_generate_key(alice_private, alice_pair); _olm_crypto_curve25519_generate_key(alice_private, &alice_pair);
assert_equals(alice_private, alice_pair.private_key, 32); assert_equals(alice_private, alice_pair.private_key.private_key, 32);
assert_equals(alice_public, alice_pair.public_key, 32); assert_equals(alice_public, alice_pair.public_key.public_key, 32);
olm::Curve25519KeyPair bob_pair; _olm_curve25519_key_pair bob_pair;
olm::curve25519_generate_key(bob_private, bob_pair); _olm_crypto_curve25519_generate_key(bob_private, &bob_pair);
assert_equals(bob_private, bob_pair.private_key, 32); assert_equals(bob_private, bob_pair.private_key.private_key, 32);
assert_equals(bob_public, bob_pair.public_key, 32); assert_equals(bob_public, bob_pair.public_key.public_key, 32);
std::uint8_t actual_agreement[olm::KEY_LENGTH] = {}; std::uint8_t actual_agreement[CURVE25519_SHARED_SECRET_LENGTH] = {};
olm::curve25519_shared_secret(alice_pair, bob_pair, actual_agreement); _olm_crypto_curve25519_shared_secret(&alice_pair, &bob_pair.public_key, actual_agreement);
assert_equals(expected_agreement, actual_agreement, 32); assert_equals(expected_agreement, actual_agreement, 32);
olm::curve25519_shared_secret(bob_pair, alice_pair, actual_agreement); _olm_crypto_curve25519_shared_secret(&bob_pair, &alice_pair.public_key, actual_agreement);
assert_equals(expected_agreement, actual_agreement, 32); assert_equals(expected_agreement, actual_agreement, 32);
} /* Curve25529 Test Case 1 */ } /* Curve25529 Test Case 1 */
{ /* Curve25519 Signature Test Case 1 */
TestCase test_case("Curve25519 Signature Test Case 1");
std::uint8_t private_key[33] = "This key is a string of 32 bytes";
std::uint8_t message[] = "message";
std::size_t message_length = sizeof(message) - 1;
olm::Curve25519KeyPair key_pair;
olm::curve25519_generate_key(private_key, key_pair);
std::uint8_t signature[64];
olm::curve25519_sign(
key_pair, message, message_length, signature
);
bool result = olm::curve25519_verify(
key_pair, message, message_length, signature
);
assert_equals(true, result);
message[0] = 'n';
result = olm::curve25519_verify(
key_pair, message, message_length, signature
);
assert_equals(false, result);
} /* Curve25519 Signature Test Case 1 */
{ {
TestCase test_case("Ed25519 Signature Test Case 1"); TestCase test_case("Ed25519 Signature Test Case 1");
std::uint8_t private_key[33] = "This key is a string of 32 bytes"; std::uint8_t private_key[33] = "This key is a string of 32 bytes";
@ -119,22 +90,22 @@ std::uint8_t private_key[33] = "This key is a string of 32 bytes";
std::uint8_t message[] = "Hello, World"; std::uint8_t message[] = "Hello, World";
std::size_t message_length = sizeof(message) - 1; std::size_t message_length = sizeof(message) - 1;
olm::Ed25519KeyPair key_pair; _olm_ed25519_key_pair key_pair;
olm::ed25519_generate_key(private_key, key_pair); _olm_crypto_ed25519_generate_key(private_key, &key_pair);
std::uint8_t signature[64]; std::uint8_t signature[64];
olm::ed25519_sign( _olm_crypto_ed25519_sign(
key_pair, message, message_length, signature &key_pair, message, message_length, signature
); );
bool result = olm::ed25519_verify( bool result = _olm_crypto_ed25519_verify(
key_pair, message, message_length, signature &key_pair.public_key, message, message_length, signature
); );
assert_equals(true, result); assert_equals(true, result);
message[0] = 'n'; message[0] = 'n';
result = olm::ed25519_verify( result = _olm_crypto_ed25519_verify(
key_pair, message, message_length, signature &key_pair.public_key, message, message_length, signature
); );
assert_equals(false, result); assert_equals(false, result);
} }
@ -144,8 +115,8 @@ assert_equals(false, result);
TestCase test_case("AES Test Case 1"); TestCase test_case("AES Test Case 1");
olm::Aes256Key key = {}; _olm_aes256_key key = {};
olm::Aes256Iv iv = {}; _olm_aes256_iv iv = {};
std::uint8_t input[16] = {}; std::uint8_t input[16] = {};
std::uint8_t expected[32] = { std::uint8_t expected[32] = {
@ -155,16 +126,16 @@ std::uint8_t expected[32] = {
0x4B, 0xAE, 0xDF, 0xFC, 0x3D, 0x21, 0x4C, 0x38 0x4B, 0xAE, 0xDF, 0xFC, 0x3D, 0x21, 0x4C, 0x38
}; };
std::size_t length = olm::aes_encrypt_cbc_length(sizeof(input)); std::size_t length = _olm_crypto_aes_encrypt_cbc_length(sizeof(input));
assert_equals(std::size_t(32), length); assert_equals(std::size_t(32), length);
std::uint8_t actual[32] = {}; std::uint8_t actual[32] = {};
olm::aes_encrypt_cbc(key, iv, input, sizeof(input), actual); _olm_crypto_aes_encrypt_cbc(&key, &iv, input, sizeof(input), actual);
assert_equals(expected, actual, 32); assert_equals(expected, actual, 32);
length = olm::aes_decrypt_cbc(key, iv, expected, sizeof(expected), actual); length = _olm_crypto_aes_decrypt_cbc(&key, &iv, expected, sizeof(expected), actual);
assert_equals(std::size_t(16), length); assert_equals(std::size_t(16), length);
assert_equals(input, actual, length); assert_equals(input, actual, length);

View file

@ -80,7 +80,6 @@ int main() {
assert_equals(pickle1, pickle2, pickle_length); assert_equals(pickle1, pickle2, pickle_length);
} }
{ {
TestCase test_case("Group message send/receive"); TestCase test_case("Group message send/receive");
@ -89,6 +88,7 @@ int main() {
"0123456789ABDEF0123456789ABCDEF" "0123456789ABDEF0123456789ABCDEF"
"0123456789ABDEF0123456789ABCDEF" "0123456789ABDEF0123456789ABCDEF"
"0123456789ABDEF0123456789ABCDEF" "0123456789ABDEF0123456789ABCDEF"
"0123456789ABDEF0123456789ABCDEF"
"0123456789ABDEF0123456789ABCDEF"; "0123456789ABDEF0123456789ABCDEF";
@ -97,7 +97,7 @@ int main() {
uint8_t memory[size]; uint8_t memory[size];
OlmOutboundGroupSession *session = olm_outbound_group_session(memory); OlmOutboundGroupSession *session = olm_outbound_group_session(memory);
assert_equals((size_t)132, assert_equals((size_t)160,
olm_init_outbound_group_session_random_length(session)); olm_init_outbound_group_session_random_length(session));
size_t res = olm_init_outbound_group_session( size_t res = olm_init_outbound_group_session(
@ -109,7 +109,6 @@ int main() {
uint8_t session_key[session_key_len]; uint8_t session_key[session_key_len];
olm_outbound_group_session_key(session, session_key, session_key_len); olm_outbound_group_session_key(session, session_key, session_key_len);
/* encode the message */ /* encode the message */
uint8_t plaintext[] = "Message"; uint8_t plaintext[] = "Message";
size_t plaintext_length = sizeof(plaintext) - 1; size_t plaintext_length = sizeof(plaintext) - 1;
@ -131,9 +130,29 @@ int main() {
olm_inbound_group_session(inbound_session_memory); olm_inbound_group_session(inbound_session_memory);
res = olm_init_inbound_group_session( res = olm_init_inbound_group_session(
inbound_session, 0U, session_key, session_key_len); inbound_session, session_key, session_key_len);
assert_equals((size_t)0, res); assert_equals((size_t)0, res);
/* Check the session ids */
size_t out_session_id_len = olm_outbound_group_session_id_length(session);
uint8_t out_session_id[out_session_id_len];
assert_equals(out_session_id_len, olm_outbound_group_session_id(
session, out_session_id, out_session_id_len
));
size_t in_session_id_len = olm_inbound_group_session_id_length(
inbound_session
);
uint8_t in_session_id[in_session_id_len];
assert_equals(in_session_id_len, olm_inbound_group_session_id(
inbound_session, in_session_id, in_session_id_len
));
assert_equals(in_session_id_len, out_session_id_len);
assert_equals(out_session_id, in_session_id, in_session_id_len);
/* decode the message */ /* decode the message */
/* olm_group_decrypt_max_plaintext_length destroys the input so we have to /* olm_group_decrypt_max_plaintext_length destroys the input so we have to
@ -148,4 +167,74 @@ int main() {
assert_equals(plaintext, plaintext_buf, res); assert_equals(plaintext, plaintext_buf, res);
} }
{
TestCase test_case("Invalid signature group message");
uint8_t plaintext[] = "Message";
size_t plaintext_length = sizeof(plaintext) - 1;
uint8_t session_key[] =
"AgAAAAAwMTIzNDU2Nzg5QUJERUYwMTIzNDU2Nzg5QUJDREVGMDEyMzQ1Njc4OUFCREVGM"
"DEyMzQ1Njc4OUFCQ0RFRjAxMjM0NTY3ODlBQkRFRjAxMjM0NTY3ODlBQkNERUYwMTIzND"
"U2Nzg5QUJERUYwMTIzNDU2Nzg5QUJDREVGMDEyMztqJ7zOtqQtYqOo0CpvDXNlMhV3HeJ"
"DpjrASKGLWdop4lx1cSN3Xv1TgfLPW8rhGiW+hHiMxd36nRuxscNv9k4oJA/KP+o0mi1w"
"v44StrEJ1wwx9WZHBUIWkQbaBSuBDw";
uint8_t message[] =
"AwgAEhAcbh6UpbByoyZxufQ+h2B+8XHMjhR69G8nP4pNZGl/3QMgrzCZPmP+F2aPLyKPz"
"xRPBMUkeXRJ6Iqm5NeOdx2eERgTW7P20CM+lL3Xpk+ZUOOPvsSQNaAL";
size_t msglen = sizeof(message)-1;
/* build the inbound session */
size_t size = olm_inbound_group_session_size();
uint8_t inbound_session_memory[size];
OlmInboundGroupSession *inbound_session =
olm_inbound_group_session(inbound_session_memory);
size_t res = olm_init_inbound_group_session(
inbound_session, session_key, sizeof(session_key)-1
);
assert_equals((size_t)0, res);
/* decode the message */
/* olm_group_decrypt_max_plaintext_length destroys the input so we have to
copy it. */
uint8_t msgcopy[msglen];
memcpy(msgcopy, message, msglen);
size = olm_group_decrypt_max_plaintext_length(
inbound_session, msgcopy, msglen
);
memcpy(msgcopy, message, msglen);
uint8_t plaintext_buf[size];
res = olm_group_decrypt(
inbound_session, msgcopy, msglen, plaintext_buf, size
);
assert_equals(plaintext_length, res);
assert_equals(plaintext, plaintext_buf, res);
/* now twiddle the signature */
message[msglen-1] = 'E';
memcpy(msgcopy, message, msglen);
assert_equals(
size,
olm_group_decrypt_max_plaintext_length(
inbound_session, msgcopy, msglen
)
);
memcpy(msgcopy, message, msglen);
res = olm_group_decrypt(
inbound_session, msgcopy, msglen,
plaintext_buf, size
);
assert_equals((size_t)-1, res);
assert_equals(
std::string("BAD_SIGNATURE"),
std::string(olm_inbound_group_session_last_error(inbound_session))
);
}
} }

View file

@ -67,8 +67,8 @@ assert_equals(message2, output, 35);
TestCase test_case("Group message encode test"); TestCase test_case("Group message encode test");
size_t length = _olm_encode_group_message_length(200, 10, 8); size_t length = _olm_encode_group_message_length(200, 10, 8, 64);
size_t expected_length = 1 + (1+2) + (2+10) + 8; size_t expected_length = 1 + (1+2) + (2+10) + 8 + 64;
assert_equals(expected_length, length); assert_equals(expected_length, length);
uint8_t output[50]; uint8_t output[50];
@ -99,9 +99,10 @@ assert_equals(message2, output, 35);
"\x03" "\x03"
"\x08\xC8\x01" "\x08\xC8\x01"
"\x12\x0A" "ciphertext" "\x12\x0A" "ciphertext"
"hmacsha2"; "hmacsha2"
"ed25519signature";
_olm_decode_group_message(message, sizeof(message)-1, 8, &results); _olm_decode_group_message(message, sizeof(message)-1, 8, 16, &results);
assert_equals(std::uint8_t(3), results.version); assert_equals(std::uint8_t(3), results.version);
assert_equals(1, results.has_message_index); assert_equals(1, results.has_message_index);
assert_equals(std::uint32_t(200), results.message_index); assert_equals(std::uint32_t(200), results.message_index);

View file

@ -65,6 +65,33 @@ assert_equals(pickle1, pickle2, pickle_length);
} }
{
TestCase test_case("Old account unpickle test");
// this uses the old pickle format, which did not use enough space
// for the Ed25519 key. We should reject it.
std::uint8_t pickle[] =
"x3h9er86ygvq56pM1yesdAxZou4ResPQC9Rszk/fhEL9JY/umtZ2N/foL/SUgVXS"
"v0IxHHZTafYjDdzJU9xr8dQeBoOTGfV9E/lCqDGBnIlu7SZndqjEKXtzGyQr4sP4"
"K/A/8TOu9iK2hDFszy6xETiousHnHgh2ZGbRUh4pQx+YMm8ZdNZeRnwFGLnrWyf9"
"O5TmXua1FcU";
std::uint8_t account_buffer[::olm_account_size()];
::OlmAccount *account = ::olm_account(account_buffer);
assert_equals(
std::size_t(-1),
::olm_unpickle_account(
account, "", 0, pickle, sizeof(pickle)-1
)
);
assert_equals(
std::string("BAD_LEGACY_ACCOUNT_PICKLE"),
std::string(::olm_account_last_error(account))
);
}
{ /** Pickle session test */ { /** Pickle session test */
TestCase test_case("Pickle session test"); TestCase test_case("Pickle session test");

View file

@ -32,8 +32,8 @@ _olm_cipher_aes_sha_256 cipher0 = OLM_CIPHER_INIT_AES_SHA_256(message_info);
_olm_cipher *cipher = OLM_CIPHER_BASE(&cipher0); _olm_cipher *cipher = OLM_CIPHER_BASE(&cipher0);
std::uint8_t random_bytes[] = "0123456789ABDEF0123456789ABCDEF"; std::uint8_t random_bytes[] = "0123456789ABDEF0123456789ABCDEF";
olm::Curve25519KeyPair alice_key; _olm_curve25519_key_pair alice_key;
olm::curve25519_generate_key(random_bytes, alice_key); _olm_crypto_curve25519_generate_key(random_bytes, &alice_key);
std::uint8_t shared_secret[] = "A secret"; std::uint8_t shared_secret[] = "A secret";
@ -44,7 +44,7 @@ olm::Ratchet alice(kdf_info, cipher);
olm::Ratchet bob(kdf_info, cipher); olm::Ratchet bob(kdf_info, cipher);
alice.initialise_as_alice(shared_secret, sizeof(shared_secret) - 1, alice_key); alice.initialise_as_alice(shared_secret, sizeof(shared_secret) - 1, alice_key);
bob.initialise_as_bob(shared_secret, sizeof(shared_secret) - 1, alice_key); bob.initialise_as_bob(shared_secret, sizeof(shared_secret) - 1, alice_key.public_key);
std::uint8_t plaintext[] = "Message"; std::uint8_t plaintext[] = "Message";
std::size_t plaintext_length = sizeof(plaintext) - 1; std::size_t plaintext_length = sizeof(plaintext) - 1;
@ -113,7 +113,7 @@ olm::Ratchet alice(kdf_info, cipher);
olm::Ratchet bob(kdf_info, cipher); olm::Ratchet bob(kdf_info, cipher);
alice.initialise_as_alice(shared_secret, sizeof(shared_secret) - 1, alice_key); alice.initialise_as_alice(shared_secret, sizeof(shared_secret) - 1, alice_key);
bob.initialise_as_bob(shared_secret, sizeof(shared_secret) - 1, alice_key); bob.initialise_as_bob(shared_secret, sizeof(shared_secret) - 1, alice_key.public_key);
std::uint8_t plaintext_1[] = "First Message"; std::uint8_t plaintext_1[] = "First Message";
std::size_t plaintext_1_length = sizeof(plaintext_1) - 1; std::size_t plaintext_1_length = sizeof(plaintext_1) - 1;
@ -185,7 +185,7 @@ olm::Ratchet alice(kdf_info, cipher);
olm::Ratchet bob(kdf_info, cipher); olm::Ratchet bob(kdf_info, cipher);
alice.initialise_as_alice(shared_secret, sizeof(shared_secret) - 1, alice_key); alice.initialise_as_alice(shared_secret, sizeof(shared_secret) - 1, alice_key);
bob.initialise_as_bob(shared_secret, sizeof(shared_secret) - 1, alice_key); bob.initialise_as_bob(shared_secret, sizeof(shared_secret) - 1, alice_key.public_key);
std::uint8_t plaintext[] = "These 15 bytes"; std::uint8_t plaintext[] = "These 15 bytes";
assert_equals(std::size_t(15), sizeof(plaintext)); assert_equals(std::size_t(15), sizeof(plaintext));

View file

@ -33,12 +33,12 @@ void check_session(const olm::Session &session) {
assert_equals( assert_equals(
decode_hex("f77a03eaa9b301fa7d2a5aa6b50286906de12cc96044f526dbbcb12839ad7003"), decode_hex("f77a03eaa9b301fa7d2a5aa6b50286906de12cc96044f526dbbcb12839ad7003"),
session.ratchet.sender_chain[0].ratchet_key.public_key, 32 session.ratchet.sender_chain[0].ratchet_key.public_key.public_key, 32
); );
assert_equals( assert_equals(
decode_hex("d945c6ed4c7c277117adf11fb133a7936d287afe97c0b3ac989644b4490d4f31"), decode_hex("d945c6ed4c7c277117adf11fb133a7936d287afe97c0b3ac989644b4490d4f31"),
session.ratchet.sender_chain[0].ratchet_key.private_key, 32 session.ratchet.sender_chain[0].ratchet_key.private_key.private_key, 32
); );
assert_equals( assert_equals(