From 06cdb3f75e27aae06c16b10e936ddd908ea4e5c5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 4 Jul 2016 22:38:04 +0100 Subject: [PATCH 01/36] Specify the soname in the library and its name This provides users of this library the guarantee that the ABI will stay stable when MAJOR will reach 1, and will stay backwards compatible for the entire duration of the 1.x.y branch. It does require the maintainers to always update the version in the Makefile at every ABI change. --- Makefile | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 0d5ddd6..accc760 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,9 @@ #!/usr/bin/make -f +MAJOR := 0 +MINOR := 1 +PATCH := 0 +VERSION := $(MAJOR).$(MINOR).$(PATCH) BUILD_DIR := build RELEASE_OPTIMIZE_FLAGS ?= -g -O3 DEBUG_OPTIMIZE_FLAGS ?= -g -O0 @@ -9,8 +13,10 @@ CC = gcc EMCC = emcc AFL_CC = afl-gcc AFL_CXX = afl-g++ -RELEASE_TARGET := $(BUILD_DIR)/libolm.so -DEBUG_TARGET := $(BUILD_DIR)/libolm_debug.so +RELEASE_TARGET := $(BUILD_DIR)/libolm.so.$(VERSION) +RELEASE_SYMLINKS := $(BUILD_DIR)/libolm.so.$(MAJOR) $(BUILD_DIR)/libolm.so +DEBUG_TARGET := $(BUILD_DIR)/libolm_debug.so.$(VERSION) +DEBUG_SYMLINKS := $(BUILD_DIR)/libolm_debug.so.$(MAJOR) $(BUILD_DIR)/libolm_debug.so JS_TARGET := javascript/olm.js JS_EXPORTED_FUNCTIONS := javascript/exported_functions.json @@ -87,22 +93,30 @@ $(JS_TARGET): LDFLAGS += $(JS_OPTIMIZE_FLAGS) ### top-level targets -lib: $(RELEASE_TARGET) +lib: $(RELEASE_TARGET) $(RELEASE_SYMLINKS) .PHONY: lib $(RELEASE_TARGET): $(RELEASE_OBJECTS) $(CXX) $(LDFLAGS) --shared -fPIC \ + -Wl,-soname,libolm.so.$(MAJOR) \ -Wl,--version-script,version_script.ver \ $(OUTPUT_OPTION) $(RELEASE_OBJECTS) -debug: $(DEBUG_TARGET) +$(RELEASE_SYMLINKS): + ln -s libolm.so.$(VERSION) $@ + +debug: $(DEBUG_TARGET) $(DEBUG_SYMLINKS) .PHONY: debug $(DEBUG_TARGET): $(DEBUG_OBJECTS) $(CXX) $(LDFLAGS) --shared -fPIC \ + -Wl,-soname,libolm_debug.so.$(MAJOR) \ -Wl,--version-script,version_script.ver \ $(OUTPUT_OPTION) $(DEBUG_OBJECTS) +$(DEBUG_SYMLINKS): + ln -s libolm_debug.so.$(VERSION) $@ + js: $(JS_TARGET) .PHONY: js From c8d00665b37af44e1c62b56407ed8e2e84fee830 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 4 Jul 2016 22:38:05 +0100 Subject: [PATCH 02/36] Add an install and an install-debug targets This makes the user able to use the familiar `make install` syntax, and allows overriding of the default directories using the DESTDIR and PREFIX variables, for example: make DESTDIR=packaging PREFIX=/usr install --- Makefile | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Makefile b/Makefile index accc760..18230f7 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ MAJOR := 0 MINOR := 1 PATCH := 0 VERSION := $(MAJOR).$(MINOR).$(PATCH) +PREFIX ?= /usr/local BUILD_DIR := build RELEASE_OPTIMIZE_FLAGS ?= -g -O3 DEBUG_OPTIMIZE_FLAGS ?= -g -O0 @@ -145,6 +146,22 @@ $(JS_EXPORTED_FUNCTIONS): $(PUBLIC_HEADERS) all: test js lib debug .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:; rm -rf $(BUILD_DIR) .PHONY: clean From 939aa747dd64bdfb3b2138ffccd4792c18f6ab91 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 5 Jul 2016 23:01:23 +0100 Subject: [PATCH 03/36] JS: make sure returned strings are null-terminated It turns out that the 'length' argument to 'Pointer_stringify' doesn't work if the input includes characters >= 128. Rather than try to figure out which methods can return UTF-8, and which always return plain ascii, replace all uses of Pointer_stringify with a 'length' argument with the version that expects a NULL-terminated input, and extend the buffer by a byte to allow space for a null-terminator. In the case of decrypt, we need to add the null ourself. Fixes https://github.com/vector-im/vector-web/issues/1719. --- javascript/olm_post.js | 56 ++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/javascript/olm_post.js b/javascript/olm_post.js index 5173f7f..f423b93 100644 --- a/javascript/olm_post.js +++ b/javascript/olm_post.js @@ -4,6 +4,16 @@ var free = Module['_free']; var Pointer_stringify = Module['Pointer_stringify']; var OLM_ERROR = Module['_olm_error'](); +/* The 'length' argument to Pointer_stringify doesn't work if the input includes + * cahracters >= 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) { 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( Module['_olm_account_identity_keys_length'] )(this.ptr); - var keys = stack(keys_length); + var keys = stack(keys_length + NULL_BYTE_PADDING_LENGTH); account_method(Module['_olm_account_identity_keys'])( this.ptr, keys, keys_length ); - return Pointer_stringify(keys, keys_length); + return Pointer_stringify(keys); }); Account.prototype['sign'] = restore_stack(function(message) { @@ -81,24 +91,24 @@ Account.prototype['sign'] = restore_stack(function(message) { )(this.ptr); var message_array = array_from_string(message); 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'])( this.ptr, message_buffer, message_array.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() { var keys_length = account_method( Module['_olm_account_one_time_keys_length'] )(this.ptr); - var keys = stack(keys_length); + var keys = stack(keys_length + NULL_BYTE_PADDING_LENGTH); account_method(Module['_olm_account_one_time_keys'])( this.ptr, keys, keys_length ); - return Pointer_stringify(keys, keys_length); + return Pointer_stringify(keys); }); 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'] )(this.ptr); 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'])( 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) { @@ -183,11 +193,11 @@ Session.prototype['pickle'] = restore_stack(function(key) { Module['_olm_pickle_session_length'] )(this.ptr); 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'])( 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) { @@ -246,11 +256,11 @@ Session.prototype['create_inbound_from'] = restore_stack(function( Session.prototype['session_id'] = restore_stack(function() { 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'])( this.ptr, id_buffer, id_length ); - return Pointer_stringify(id_buffer, id_length); + return Pointer_stringify(id_buffer); }); Session.prototype['matches_inbound'] = restore_stack(function( @@ -292,7 +302,7 @@ Session.prototype['encrypt'] = restore_stack(function( )(this.ptr, plaintext_array.length); var random = random_stack(random_length); 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'])( this.ptr, plaintext_buffer, plaintext_array.length, @@ -301,7 +311,7 @@ Session.prototype['encrypt'] = restore_stack(function( ); return { "type": message_type, - "body": Pointer_stringify(message_buffer, message_length) + "body": Pointer_stringify(message_buffer) }; }); @@ -316,13 +326,23 @@ Session.prototype['decrypt'] = restore_stack(function( // caculating the length destroys the input buffer. // So we copy the array to a new buffer 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"])( this.ptr, message_type, message_buffer, message.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() { @@ -353,13 +373,13 @@ Utility.prototype['sha256'] = restore_stack(function(input) { var output_length = utility_method(Module['_olm_sha256_length'])(this.ptr); var input_array = array_from_string(input); 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'])( this.ptr, input_buffer, input_array.length(), output_buffer, output_length ); - return Pointer_stringify(output_buffer, output_length); + return Pointer_stringify(output_buffer); }); Utility.prototype['ed25519_verify'] = restore_stack(function( From f3b9c3bbbbdeab9ec5e5f0bf8af7f0694926e495 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 11 Jul 2016 12:50:11 +0100 Subject: [PATCH 04/36] Prepare 1.0.0 release --- CHANGELOG.rst | 19 +++++++++++++++++++ Makefile | 4 ++-- javascript/package.json | 2 +- 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 CHANGELOG.rst diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 0000000..38a8675 --- /dev/null +++ b/CHANGELOG.rst @@ -0,0 +1,19 @@ +Changes in `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 +`_ 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). diff --git a/Makefile b/Makefile index 18230f7..2bfd2ab 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ #!/usr/bin/make -f -MAJOR := 0 -MINOR := 1 +MAJOR := 1 +MINOR := 0 PATCH := 0 VERSION := $(MAJOR).$(MINOR).$(PATCH) PREFIX ?= /usr/local diff --git a/javascript/package.json b/javascript/package.json index 710d46d..17c2096 100644 --- a/javascript/package.json +++ b/javascript/package.json @@ -1,6 +1,6 @@ { "name": "olm", - "version": "0.1.0", + "version": "1.0.0", "description": "An implementation of a well known cryptographic ratchet", "main": "olm.js", "files": [ From d30db7b5c0a36fcf944ed7babbd8fb11193a1fcd Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 11 Jul 2016 14:50:49 +0100 Subject: [PATCH 05/36] Makefile rules to build the rst into html --- .gitignore | 6 +++++- Makefile | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 00f42a6..aece1fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ -build +/build +/CHANGELOG.html +/docs/olm.html /olm-*.tgz +/README.html +/tracing/README.html \ No newline at end of file diff --git a/Makefile b/Makefile index 2bfd2ab..1af9b6b 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,10 @@ JS_PRE := $(wildcard javascript/*pre.js) JS_POST := javascript/olm_outbound_group_session.js \ javascript/olm_inbound_group_session.js \ javascript/olm_post.js +DOCS := tracing/README.html \ + docs/olm.html \ + README.html \ + CHANGELOG.html CPPFLAGS += -Iinclude -Ilib # we rely on , which was introduced in C99 @@ -143,7 +147,7 @@ $(JS_EXPORTED_FUNCTIONS): $(PUBLIC_HEADERS) perl -MJSON -ne '$$f{"_$$1"}=1 if /(olm_[^( ]*)\(/; END { @f=sort keys %f; print encode_json \@f }' $^ > $@.tmp mv $@.tmp $@ -all: test js lib debug +all: test js lib debug doc .PHONY: all install-debug: debug @@ -163,9 +167,12 @@ install: lib .PHONY: install clean:; - rm -rf $(BUILD_DIR) + rm -rf $(BUILD_DIR) $(DOCS) .PHONY: clean +doc: $(DOCS) +.PHONY: doc + ### rules for building objects $(BUILD_DIR)/release/%.o: %.c mkdir -p $(dir $@) @@ -219,6 +226,9 @@ $(BUILD_DIR)/fuzzers/debug_%: fuzzers/fuzz_%.c $(DEBUG_OBJECTS) $(BUILD_DIR)/fuzzers/debug_%: fuzzers/fuzz_%.cpp $(DEBUG_OBJECTS) $(LINK.cc) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@ +%.html: %.rst + rst2html $< $@ + ### dependencies -include $(RELEASE_OBJECTS:.o=.d) From f03febb772c47855ab748ed6dee2f29db9f9c5ba Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 11 Jul 2016 14:51:04 +0100 Subject: [PATCH 06/36] Document the release process --- README.rst | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 0f3ae4f..828416b 100644 --- a/README.rst +++ b/README.rst @@ -27,14 +27,23 @@ To build the javascript bindings, install emscripten from http://kripken.github. .. code:: bash 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 +# Bump version numbers in ``Makefile`` and ``javascript/package.json`` +# Prepare changelog +# ``git commit`` +# ``make test`` +# ``make js`` +# ``npm pack javascript`` +# ``scp olm-x.y.z.tgz packages@ldc-prd-matrix-001:/sites/matrix/packages/npm/olm/`` +# ``git tag x.y.z`` +# ``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. - git tag x.y.z - git push --tags Design ------ From f2906ac0e7a3168a1206beaa1fdd6ba1dd44b62d Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 16 Jul 2016 20:56:29 +0100 Subject: [PATCH 07/36] refer to Double Ratchet in readme --- README.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 828416b..1a3663a 100644 --- a/README.rst +++ b/README.rst @@ -1,8 +1,9 @@ Olm === -An implementation of the cryptographic ratchet described by -https://github.com/trevp/axolotl/wiki, written in C++11 and exposed as a C API +An implementation of the Double Ratchet cryptographic ratchet described by +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 https://matrix.org/docs/spec/olm.html From 0c462cff112589fc52d13da6c919f881cb6d3f8c Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 1 Sep 2016 13:35:23 +0100 Subject: [PATCH 08/36] Fix Ed25519 keypair generation Ed25519 private keys, it turns out, have 64 bytes, not 32. We were previously generating only 32 bytes (which is all that is required to generate the public key), and then using the public key as the upper 32 bytes when generating the per-message session key. This meant that everything appeared to work, but the security of the private key was severely compromised. By way of fixes: * Use the correct algorithm for generating the Ed25519 private key, and store all 512 bits of it. * Update the account pickle format and refuse to load the old format (since we should consider it compromised). * Bump the library version, and add a function to retrieve the library version, so that applications can verify that they are linked against a fixed version of the library. * Remove the curve25519_{sign, verify} functions which were unused and of dubious quality. --- CHANGELOG.rst | 12 ++++++++++++ Makefile | 21 +++++++++----------- include/olm/crypto.hh | 21 ++------------------ include/olm/error.h | 7 +++++++ include/olm/olm.h | 5 +++++ javascript/olm_post.js | 10 ++++++++++ lib/ed25519_additions.c | 43 ----------------------------------------- lib/ed25519_additions.h | 24 ----------------------- python/olm/_base.py | 2 +- src/account.cpp | 16 +++++++++++---- src/crypto.cpp | 43 ++++------------------------------------- src/ed25519.c | 2 +- src/error.c | 1 + src/olm.cpp | 5 +++++ tests/test_crypto.cpp | 29 --------------------------- tests/test_olm.cpp | 27 ++++++++++++++++++++++++++ 16 files changed, 96 insertions(+), 172 deletions(-) delete mode 100644 lib/ed25519_additions.c delete mode 100644 lib/ed25519_additions.h diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 38a8675..9a3f3d7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,15 @@ +Changes in `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 +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 `_ =============================================================== diff --git a/Makefile b/Makefile index 1af9b6b..92e3285 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ #!/usr/bin/make -f MAJOR := 1 -MINOR := 0 +MINOR := 1 PATCH := 0 VERSION := $(MAJOR).$(MINOR).$(PATCH) PREFIX ?= /usr/local @@ -15,9 +15,7 @@ EMCC = emcc AFL_CC = afl-gcc AFL_CXX = afl-g++ RELEASE_TARGET := $(BUILD_DIR)/libolm.so.$(VERSION) -RELEASE_SYMLINKS := $(BUILD_DIR)/libolm.so.$(MAJOR) $(BUILD_DIR)/libolm.so DEBUG_TARGET := $(BUILD_DIR)/libolm_debug.so.$(VERSION) -DEBUG_SYMLINKS := $(BUILD_DIR)/libolm_debug.so.$(MAJOR) $(BUILD_DIR)/libolm_debug.so JS_TARGET := javascript/olm.js JS_EXPORTED_FUNCTIONS := javascript/exported_functions.json @@ -49,7 +47,10 @@ DOCS := tracing/README.html \ README.html \ CHANGELOG.html -CPPFLAGS += -Iinclude -Ilib +CPPFLAGS += -Iinclude -Ilib \ + -DOLMLIB_VERSION_MAJOR=$(MAJOR) -DOLMLIB_VERSION_MINOR=$(MINOR) \ + -DOLMLIB_VERSION_PATCH=$(PATCH) + # we rely on , which was introduced in C99 CFLAGS += -Wall -Werror -std=c99 -fPIC CXXFLAGS += -Wall -Werror -std=c++11 -fPIC @@ -98,7 +99,7 @@ $(JS_TARGET): LDFLAGS += $(JS_OPTIMIZE_FLAGS) ### top-level targets -lib: $(RELEASE_TARGET) $(RELEASE_SYMLINKS) +lib: $(RELEASE_TARGET) .PHONY: lib $(RELEASE_TARGET): $(RELEASE_OBJECTS) @@ -106,11 +107,9 @@ $(RELEASE_TARGET): $(RELEASE_OBJECTS) -Wl,-soname,libolm.so.$(MAJOR) \ -Wl,--version-script,version_script.ver \ $(OUTPUT_OPTION) $(RELEASE_OBJECTS) + ldconfig -l $@ -$(RELEASE_SYMLINKS): - ln -s libolm.so.$(VERSION) $@ - -debug: $(DEBUG_TARGET) $(DEBUG_SYMLINKS) +debug: $(DEBUG_TARGET) .PHONY: debug $(DEBUG_TARGET): $(DEBUG_OBJECTS) @@ -118,9 +117,7 @@ $(DEBUG_TARGET): $(DEBUG_OBJECTS) -Wl,-soname,libolm_debug.so.$(MAJOR) \ -Wl,--version-script,version_script.ver \ $(OUTPUT_OPTION) $(DEBUG_OBJECTS) - -$(DEBUG_SYMLINKS): - ln -s libolm_debug.so.$(VERSION) $@ + ldconfig -l $@ js: $(JS_TARGET) .PHONY: js diff --git a/include/olm/crypto.hh b/include/olm/crypto.hh index 64e8f7d..484dc83 100644 --- a/include/olm/crypto.hh +++ b/include/olm/crypto.hh @@ -25,6 +25,7 @@ namespace olm { +static const std::size_t ED25519_PRIVATE_KEY_LENGTH = 64; static const std::size_t KEY_LENGTH = 32; static const std::size_t SIGNATURE_LENGTH = 64; static const std::size_t IV_LENGTH = 16; @@ -45,7 +46,7 @@ struct Ed25519PublicKey { struct Ed25519KeyPair : public Ed25519PublicKey { - std::uint8_t private_key[KEY_LENGTH]; + std::uint8_t private_key[ED25519_PRIVATE_KEY_LENGTH]; }; @@ -65,24 +66,6 @@ void curve25519_shared_secret( ); -/** 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, diff --git a/include/olm/error.h b/include/olm/error.h index 98d2cf5..1c44de8 100644 --- a/include/olm/error.h +++ b/include/olm/error.h @@ -39,6 +39,13 @@ enum OlmErrorCode { * 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, + /* remember to update the list of string constants in error.c when updating * this list. */ }; diff --git a/include/olm/olm.h b/include/olm/olm.h index dbaf71e..0886fa9 100644 --- a/include/olm/olm.h +++ b/include/olm/olm.h @@ -33,6 +33,11 @@ typedef struct OlmAccount OlmAccount; typedef struct OlmSession OlmSession; 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 */ size_t olm_account_size(); diff --git a/javascript/olm_post.js b/javascript/olm_post.js index f423b93..955d68d 100644 --- a/javascript/olm_post.js +++ b/javascript/olm_post.js @@ -402,4 +402,14 @@ Utility.prototype['ed25519_verify'] = restore_stack(function( olm_exports["Account"] = Account; olm_exports["Session"] = Session; olm_exports["Utility"] = Utility; + +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'), + ]; +}); }(); diff --git a/lib/ed25519_additions.c b/lib/ed25519_additions.c deleted file mode 100644 index 5fa0c68..0000000 --- a/lib/ed25519_additions.c +++ /dev/null @@ -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); -} diff --git a/lib/ed25519_additions.h b/lib/ed25519_additions.h deleted file mode 100644 index e5f93a1..0000000 --- a/lib/ed25519_additions.h +++ /dev/null @@ -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 diff --git a/python/olm/_base.py b/python/olm/_base.py index 8238957..bc5c206 100644 --- a/python/olm/_base.py +++ b/python/olm/_base.py @@ -7,7 +7,7 @@ def read_random(n): return f.read(n) 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 = [] diff --git a/src/account.cpp b/src/account.cpp index c8e6e40..ec763f8 100644 --- a/src/account.cpp +++ b/src/account.cpp @@ -326,7 +326,9 @@ static std::uint8_t const * unpickle( } // namespace olm namespace { -static const std::uint32_t ACCOUNT_PICKLE_VERSION = 1; +// pickle version 1 used only 32 bytes for the ed25519 private key. +// Any keys thus used should be considered compromised. +static const std::uint32_t ACCOUNT_PICKLE_VERSION = 2; } @@ -360,9 +362,15 @@ std::uint8_t const * olm::unpickle( ) { uint32_t pickle_version; pos = olm::unpickle(pos, end, pickle_version); - if (pickle_version != ACCOUNT_PICKLE_VERSION) { - value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION; - return end; + switch (pickle_version) { + case ACCOUNT_PICKLE_VERSION: + break; + case 1: + value.last_error = OlmErrorCode::OLM_BAD_LEGACY_ACCOUNT_PICKLE; + return end; + default: + value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION; + return end; } pos = olm::unpickle(pos, end, value.identity_keys); pos = olm::unpickle(pos, end, value.one_time_keys); diff --git a/src/crypto.cpp b/src/crypto.cpp index 4fa92f1..83493be 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -25,7 +25,6 @@ extern "C" { } #include "ed25519/src/ed25519.h" -#include "ed25519_additions.h" #include "curve25519-donna.h" namespace { @@ -121,48 +120,14 @@ void olm::curve25519_shared_secret( } -void olm::curve25519_sign( - 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, olm::Ed25519KeyPair & key_pair ) { - std::memcpy(key_pair.private_key, random_32_bytes, KEY_LENGTH); - ::ed25519_keypair(key_pair.private_key, key_pair.public_key); + ::ed25519_create_keypair( + key_pair.public_key, key_pair.private_key, + random_32_bytes + ); } diff --git a/src/ed25519.c b/src/ed25519.c index f4f910d..c7a1a8e 100644 --- a/src/ed25519.c +++ b/src/ed25519.c @@ -16,7 +16,7 @@ #include "ed25519/src/fe.c" #include "ed25519/src/sc.c" #include "ed25519/src/ge.c" +#include "ed25519/src/keypair.c" #include "ed25519/src/sha512.c" #include "ed25519/src/verify.c" #include "ed25519/src/sign.c" -#include "ed25519_additions.c" diff --git a/src/error.c b/src/error.c index bd8a39d..b742197 100644 --- a/src/error.c +++ b/src/error.c @@ -29,6 +29,7 @@ static const char * ERRORS[] = { "CORRUPTED_PICKLE", "BAD_SESSION_KEY", "UNKNOWN_MESSAGE_INDEX", + "BAD_LEGACY_ACCOUNT_PICKLE", }; const char * _olm_error_to_string(enum OlmErrorCode error) diff --git a/src/olm.cpp b/src/olm.cpp index 0a4a734..682a84c 100644 --- a/src/olm.cpp +++ b/src/olm.cpp @@ -98,6 +98,11 @@ std::size_t b64_input( extern "C" { +void olm_get_library_version(uint8_t *major, uint8_t *minor, uint8_t *patch) { + if (major != NULL) *major = OLMLIB_VERSION_MAJOR; + if (minor != NULL) *minor = OLMLIB_VERSION_MINOR; + if (patch != NULL) *patch = OLMLIB_VERSION_PATCH; +} size_t olm_error() { return std::size_t(-1); diff --git a/tests/test_crypto.cpp b/tests/test_crypto.cpp index 1041538..175f66f 100644 --- a/tests/test_crypto.cpp +++ b/tests/test_crypto.cpp @@ -83,35 +83,6 @@ assert_equals(expected_agreement, actual_agreement, 32); } /* 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"); std::uint8_t private_key[33] = "This key is a string of 32 bytes"; diff --git a/tests/test_olm.cpp b/tests/test_olm.cpp index de7e236..af2c9f7 100644 --- a/tests/test_olm.cpp +++ b/tests/test_olm.cpp @@ -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 */ TestCase test_case("Pickle session test"); From f7c428d62c71c5d623687aa9cecfc3fc375e0caf Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 1 Sep 2016 14:00:26 +0100 Subject: [PATCH 09/36] update changelog pre-1.0.0 was broken too --- CHANGELOG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9a3f3d7..96296e5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,7 +3,7 @@ Changes in `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 -should be considered compromised. +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), From 0c3f527dfd46d3056d5b3690836c102f0e0adfb4 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 1 Sep 2016 14:31:34 +0100 Subject: [PATCH 10/36] Bump version in package.json ... to match the Makefile --- javascript/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/package.json b/javascript/package.json index 17c2096..22232ac 100644 --- a/javascript/package.json +++ b/javascript/package.json @@ -1,6 +1,6 @@ { "name": "olm", - "version": "1.0.0", + "version": "1.1.0", "description": "An implementation of a well known cryptographic ratchet", "main": "olm.js", "files": [ From 39212987bdef8e16794e756e3c78b531be25b70a Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 2 Sep 2016 15:11:14 +0100 Subject: [PATCH 11/36] Create new constants for key lengths, etc We were using olm::KEY_LENGTH for everything under the sun which happened to be 32 bytes long, and making a bunch of assumptions in the process. Create a bunch of new constants (as C #defines rather than C++ consts so that I can use them in another forthcoming refactor). --- include/olm/crypto.h | 31 ++++++++++++++++++++++++- include/olm/crypto.hh | 15 +++++-------- include/olm/ratchet.hh | 7 +++++- src/account.cpp | 10 ++++----- src/cipher.cpp | 12 ++++++---- src/crypto.cpp | 4 ++-- src/olm.cpp | 10 ++++----- src/ratchet.cpp | 27 +++++++++++----------- src/session.cpp | 51 +++++++++++++++++++++--------------------- src/utility.cpp | 2 +- tests/test_crypto.cpp | 2 +- 11 files changed, 103 insertions(+), 68 deletions(-) diff --git a/include/olm/crypto.h b/include/olm/crypto.h index 31b9b60..325080e 100644 --- a/include/olm/crypto.h +++ b/include/olm/crypto.h @@ -27,7 +27,36 @@ extern "C" { #endif -const size_t SHA256_OUTPUT_LENGTH = 32; +/** length of a sha256 hash */ +#define SHA256_OUTPUT_LENGTH 32 + +/** length of a public or private Curve25519 key */ +#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 + /** Computes SHA-256 of the input. The output buffer must be a least 32 * bytes long. */ diff --git a/include/olm/crypto.hh b/include/olm/crypto.hh index 484dc83..13fd7e9 100644 --- a/include/olm/crypto.hh +++ b/include/olm/crypto.hh @@ -25,23 +25,18 @@ namespace olm { -static const std::size_t ED25519_PRIVATE_KEY_LENGTH = 64; -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::uint8_t public_key[CURVE25519_KEY_LENGTH]; }; struct Curve25519KeyPair : public Curve25519PublicKey { - std::uint8_t private_key[KEY_LENGTH]; + std::uint8_t private_key[CURVE25519_KEY_LENGTH]; }; struct Ed25519PublicKey { - std::uint8_t public_key[KEY_LENGTH]; + std::uint8_t public_key[ED25519_PUBLIC_KEY_LENGTH]; }; @@ -93,12 +88,12 @@ bool ed25519_verify( struct Aes256Key { - std::uint8_t key[KEY_LENGTH]; + std::uint8_t key[AES256_KEY_LENGTH]; }; struct Aes256Iv { - std::uint8_t iv[IV_LENGTH]; + std::uint8_t iv[AES256_IV_LENGTH]; }; diff --git a/include/olm/ratchet.hh b/include/olm/ratchet.hh index 13f7097..e91d634 100644 --- a/include/olm/ratchet.hh +++ b/include/olm/ratchet.hh @@ -21,8 +21,13 @@ struct _olm_cipher; 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 { std::uint32_t index; diff --git a/src/account.cpp b/src/account.cpp index ec763f8..9512068 100644 --- a/src/account.cpp +++ b/src/account.cpp @@ -49,7 +49,7 @@ std::size_t olm::Account::remove_key( } std::size_t olm::Account::new_account_random_length() { - return 2 * olm::KEY_LENGTH; + return ED25519_RANDOM_LENGTH + CURVE25519_RANDOM_LENGTH; } std::size_t olm::Account::new_account( @@ -61,7 +61,7 @@ std::size_t olm::Account::new_account( } olm::ed25519_generate_key(random, identity_keys.ed25519_key); - random += KEY_LENGTH; + random += ED25519_RANDOM_LENGTH; olm::curve25519_generate_key(random, identity_keys.curve25519_key); return 0; @@ -137,7 +137,7 @@ std::size_t olm::Account::get_identity_json( std::size_t olm::Account::signature_length( ) { - return olm::SIGNATURE_LENGTH; + return ED25519_SIGNATURE_LENGTH; } @@ -238,7 +238,7 @@ std::size_t olm::Account::max_number_of_one_time_keys( std::size_t olm::Account::generate_one_time_keys_random_length( std::size_t number_of_keys ) { - return olm::KEY_LENGTH * number_of_keys; + return CURVE25519_RANDOM_LENGTH * number_of_keys; } std::size_t olm::Account::generate_one_time_keys( @@ -254,7 +254,7 @@ std::size_t olm::Account::generate_one_time_keys( key.id = ++next_one_time_key_id; key.published = false; olm::curve25519_generate_key(random, key.key); - random += olm::KEY_LENGTH; + random += CURVE25519_RANDOM_LENGTH; } return number_of_keys; } diff --git a/src/cipher.cpp b/src/cipher.cpp index 8c3de92..8e3d7a5 100644 --- a/src/cipher.cpp +++ b/src/cipher.cpp @@ -17,11 +17,13 @@ #include "olm/memory.hh" #include +const std::size_t HMAC_KEY_LENGTH = 32; + namespace { struct DerivedKeys { olm::Aes256Key aes_key; - std::uint8_t mac_key[olm::KEY_LENGTH]; + std::uint8_t mac_key[HMAC_KEY_LENGTH]; olm::Aes256Iv aes_iv; }; @@ -31,7 +33,9 @@ static void derive_keys( std::uint8_t const * key, std::size_t key_length, DerivedKeys & keys ) { - std::uint8_t derived_secrets[2 * olm::KEY_LENGTH + olm::IV_LENGTH]; + std::uint8_t derived_secrets[ + AES256_KEY_LENGTH + HMAC_KEY_LENGTH + AES256_IV_LENGTH + ]; _olm_crypto_hkdf_sha256( key, key_length, nullptr, 0, @@ -81,7 +85,7 @@ size_t aes_sha_256_cipher_encrypt( ); _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); @@ -113,7 +117,7 @@ size_t aes_sha_256_cipher_decrypt( derive_keys(c->kdf_info, c->kdf_info_length, key, key_length, keys); _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; diff --git a/src/crypto.cpp b/src/crypto.cpp index 83493be..0b08c54 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -31,7 +31,7 @@ namespace { static const std::uint8_t CURVE25519_BASEPOINT[32] = {9}; static const std::size_t AES_KEY_SCHEDULE_LENGTH = 60; -static const std::size_t AES_KEY_BITS = 8 * olm::KEY_LENGTH; +static const std::size_t AES_KEY_BITS = 8 * AES256_KEY_LENGTH; static const std::size_t AES_BLOCK_LENGTH = 16; static const std::size_t SHA256_BLOCK_LENGTH = 64; static const std::uint8_t HKDF_DEFAULT_SALT[32] = {}; @@ -104,7 +104,7 @@ 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, random_32_bytes, CURVE25519_KEY_LENGTH); ::curve25519_donna( key_pair.public_key, key_pair.private_key, CURVE25519_BASEPOINT ); diff --git a/src/olm.cpp b/src/olm.cpp index 682a84c..10e00fa 100644 --- a/src/olm.cpp +++ b/src/olm.cpp @@ -436,8 +436,8 @@ size_t olm_create_outbound_session( std::size_t id_key_length = their_identity_key_length; std::size_t ot_key_length = their_one_time_key_length; - if (olm::decode_base64_length(id_key_length) != olm::KEY_LENGTH - || olm::decode_base64_length(ot_key_length) != olm::KEY_LENGTH + if (olm::decode_base64_length(id_key_length) != CURVE25519_KEY_LENGTH + || olm::decode_base64_length(ot_key_length) != CURVE25519_KEY_LENGTH ) { from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64; return std::size_t(-1); @@ -483,7 +483,7 @@ size_t olm_create_inbound_session_from( std::uint8_t const * id_key = from_c(their_identity_key); std::size_t id_key_length = their_identity_key_length; - if (olm::decode_base64_length(id_key_length) != olm::KEY_LENGTH) { + if (olm::decode_base64_length(id_key_length) != CURVE25519_KEY_LENGTH) { from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64; return std::size_t(-1); } @@ -554,7 +554,7 @@ size_t olm_matches_inbound_session_from( std::uint8_t const * id_key = from_c(their_identity_key); std::size_t id_key_length = their_identity_key_length; - if (olm::decode_base64_length(id_key_length) != olm::KEY_LENGTH) { + if (olm::decode_base64_length(id_key_length) != CURVE25519_KEY_LENGTH) { from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64; return std::size_t(-1); } @@ -710,7 +710,7 @@ size_t olm_ed25519_verify( void const * message, size_t message_length, void * signature, size_t signature_length ) { - if (olm::decode_base64_length(key_length) != olm::KEY_LENGTH) { + if (olm::decode_base64_length(key_length) != CURVE25519_KEY_LENGTH) { from_c(utility)->last_error = OlmErrorCode::OLM_INVALID_BASE64; return std::size_t(-1); } diff --git a/src/ratchet.cpp b/src/ratchet.cpp index aab5fbb..57cb385 100644 --- a/src/ratchet.cpp +++ b/src/ratchet.cpp @@ -49,7 +49,7 @@ static void create_chain_key( ) { olm::SharedKey secret; olm::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( secret, sizeof(secret), root_key, sizeof(root_key), @@ -191,7 +191,7 @@ void olm::Ratchet::initialise_as_bob( std::uint8_t const * shared_secret, std::size_t shared_secret_length, olm::Curve25519PublicKey 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( shared_secret, shared_secret_length, nullptr, 0, @@ -212,7 +212,7 @@ void olm::Ratchet::initialise_as_alice( std::uint8_t const * shared_secret, std::size_t shared_secret_length, olm::Curve25519KeyPair 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( shared_secret, shared_secret_length, nullptr, 0, @@ -234,7 +234,7 @@ namespace olm { static std::size_t pickle_length( const olm::SharedKey & value ) { - return olm::KEY_LENGTH; + return olm::OLM_SHARED_KEY_LENGTH; } @@ -242,7 +242,7 @@ static std::uint8_t * pickle( std::uint8_t * pos, const olm::SharedKey & value ) { - return olm::pickle_bytes(pos, value, olm::KEY_LENGTH); + return olm::pickle_bytes(pos, value, olm::OLM_SHARED_KEY_LENGTH); } @@ -250,7 +250,7 @@ static std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, olm::SharedKey & value ) { - return olm::unpickle_bytes(pos, end, value, olm::KEY_LENGTH); + return olm::unpickle_bytes(pos, end, value, olm::OLM_SHARED_KEY_LENGTH); } @@ -359,7 +359,7 @@ std::size_t olm::pickle_length( olm::Ratchet const & value ) { std::size_t length = 0; - length += olm::KEY_LENGTH; + length += olm::OLM_SHARED_KEY_LENGTH; length += olm::pickle_length(value.sender_chain); length += olm::pickle_length(value.receiver_chains); length += olm::pickle_length(value.skipped_message_keys); @@ -409,13 +409,13 @@ std::size_t olm::Ratchet::encrypt_output_length( plaintext_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() { - return sender_chain.empty() ? olm::KEY_LENGTH : 0; + return sender_chain.empty() ? CURVE25519_RANDOM_LENGTH : 0; } @@ -461,7 +461,8 @@ std::size_t olm::Ratchet::encrypt( olm::MessageWriter writer; olm::encode_message( - writer, PROTOCOL_VERSION, counter, olm::KEY_LENGTH, ciphertext_length, + writer, PROTOCOL_VERSION, counter, CURVE25519_KEY_LENGTH, + ciphertext_length, output ); @@ -529,7 +530,7 @@ std::size_t olm::Ratchet::decrypt( 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; return std::size_t(-1); } @@ -539,7 +540,7 @@ std::size_t olm::Ratchet::decrypt( for (olm::ReceiverChain & receiver_chain : receiver_chains) { if (0 == std::memcmp( receiver_chain.ratchet_key.public_key, reader.ratchet_key, - olm::KEY_LENGTH + CURVE25519_KEY_LENGTH )) { chain = &receiver_chain; break; @@ -559,7 +560,7 @@ std::size_t olm::Ratchet::decrypt( if (reader.counter == skipped.message_key.index && 0 == std::memcmp( skipped.ratchet_key.public_key, reader.ratchet_key, - olm::KEY_LENGTH + CURVE25519_KEY_LENGTH ) ) { /* Found the key for this message. Check the MAC. */ diff --git a/src/session.cpp b/src/session.cpp index 6ca7f36..7bde5d1 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -49,7 +49,7 @@ olm::Session::Session( std::size_t olm::Session::new_outbound_session_random_length() { - return olm::KEY_LENGTH * 2; + return CURVE25519_RANDOM_LENGTH * 2; } @@ -68,7 +68,7 @@ std::size_t olm::Session::new_outbound_session( olm::curve25519_generate_key(random, base_key); olm::Curve25519KeyPair ratchet_key; - olm::curve25519_generate_key(random + olm::KEY_LENGTH, ratchet_key); + olm::curve25519_generate_key(random + CURVE25519_RANDOM_LENGTH, ratchet_key); olm::Curve25519KeyPair const & alice_identity_key_pair = ( local_account.identity_keys.curve25519_key @@ -79,13 +79,13 @@ std::size_t olm::Session::new_outbound_session( alice_base_key = base_key; bob_one_time_key = one_time_key; - std::uint8_t secret[3 * olm::KEY_LENGTH]; + // Calculate the shared secret S via triple DH + std::uint8_t secret[3 * CURVE25519_SHARED_SECRET_LENGTH]; std::uint8_t * pos = secret; - olm::curve25519_shared_secret(alice_identity_key_pair, one_time_key, pos); - pos += olm::KEY_LENGTH; + pos += CURVE25519_SHARED_SECRET_LENGTH; olm::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); ratchet.initialise_as_alice(secret, sizeof(secret), ratchet_key); @@ -105,13 +105,13 @@ static bool check_message_fields( bool ok = true; ok = ok && (have_their_identity_key || reader.identity_key); if (reader.identity_key) { - ok = ok && reader.identity_key_length == olm::KEY_LENGTH; + ok = ok && reader.identity_key_length == CURVE25519_KEY_LENGTH; } ok = ok && reader.message; ok = ok && reader.base_key; - ok = ok && reader.base_key_length == olm::KEY_LENGTH; + ok = ok && reader.base_key_length == CURVE25519_KEY_LENGTH; ok = ok && reader.one_time_key; - ok = ok && reader.one_time_key_length == olm::KEY_LENGTH; + ok = ok && reader.one_time_key_length == CURVE25519_KEY_LENGTH; return ok; } @@ -133,7 +133,7 @@ std::size_t olm::Session::new_inbound_session( if (reader.identity_key && their_identity_key) { bool same = 0 == std::memcmp( - their_identity_key->public_key, reader.identity_key, olm::KEY_LENGTH + their_identity_key->public_key, reader.identity_key, CURVE25519_KEY_LENGTH ); if (!same) { last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID; @@ -152,7 +152,7 @@ std::size_t olm::Session::new_inbound_session( ); 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; return std::size_t(-1); } @@ -174,12 +174,13 @@ std::size_t olm::Session::new_inbound_session( ); olm::Curve25519KeyPair const & bob_one_time_key = our_one_time_key->key; - std::uint8_t secret[olm::KEY_LENGTH * 3]; + // Calculate the shared secret S via triple DH + std::uint8_t secret[CURVE25519_SHARED_SECRET_LENGTH * 3]; std::uint8_t * pos = secret; olm::curve25519_shared_secret(bob_one_time_key, alice_identity_key, pos); - pos += olm::KEY_LENGTH; + pos += CURVE25519_SHARED_SECRET_LENGTH; olm::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); ratchet.initialise_as_bob(secret, sizeof(secret), ratchet_key); @@ -202,7 +203,7 @@ std::size_t olm::Session::session_id( last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } - std::uint8_t tmp[olm::KEY_LENGTH * 3]; + std::uint8_t tmp[CURVE25519_KEY_LENGTH * 3]; std::uint8_t * pos = tmp; pos = olm::store_array(pos, alice_identity_key.public_key); pos = olm::store_array(pos, alice_base_key.public_key); @@ -226,20 +227,20 @@ bool olm::Session::matches_inbound_session( bool same = true; if (reader.identity_key) { same = same && 0 == std::memcmp( - reader.identity_key, alice_identity_key.public_key, olm::KEY_LENGTH + reader.identity_key, alice_identity_key.public_key, CURVE25519_KEY_LENGTH ); } if (their_identity_key) { same = same && 0 == std::memcmp( their_identity_key->public_key, alice_identity_key.public_key, - olm::KEY_LENGTH + CURVE25519_KEY_LENGTH ); } same = same && 0 == std::memcmp( - reader.base_key, alice_base_key.public_key, olm::KEY_LENGTH + reader.base_key, alice_base_key.public_key, CURVE25519_KEY_LENGTH ); same = same && 0 == std::memcmp( - reader.one_time_key, bob_one_time_key.public_key, olm::KEY_LENGTH + reader.one_time_key, bob_one_time_key.public_key, CURVE25519_KEY_LENGTH ); return same; } @@ -266,9 +267,9 @@ std::size_t olm::Session::encrypt_message_length( } return encode_one_time_key_message_length( - olm::KEY_LENGTH, - olm::KEY_LENGTH, - olm::KEY_LENGTH, + CURVE25519_KEY_LENGTH, + CURVE25519_KEY_LENGTH, + CURVE25519_KEY_LENGTH, message_length ); } @@ -300,9 +301,9 @@ std::size_t olm::Session::encrypt( encode_one_time_key_message( writer, PROTOCOL_VERSION, - olm::KEY_LENGTH, - olm::KEY_LENGTH, - olm::KEY_LENGTH, + CURVE25519_KEY_LENGTH, + CURVE25519_KEY_LENGTH, + CURVE25519_KEY_LENGTH, message_body_length, message ); diff --git a/src/utility.cpp b/src/utility.cpp index 67029c9..e33351c 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -45,7 +45,7 @@ size_t olm::Utility::ed25519_verify( std::uint8_t const * message, std::size_t message_length, std::uint8_t const * signature, std::size_t signature_length ) { - if (signature_length < olm::SIGNATURE_LENGTH) { + if (signature_length < ED25519_SIGNATURE_LENGTH) { last_error = OlmErrorCode::OLM_BAD_MESSAGE_MAC; return std::size_t(-1); } diff --git a/tests/test_crypto.cpp b/tests/test_crypto.cpp index 175f66f..56abdcd 100644 --- a/tests/test_crypto.cpp +++ b/tests/test_crypto.cpp @@ -70,7 +70,7 @@ olm::curve25519_generate_key(bob_private, bob_pair); assert_equals(bob_private, bob_pair.private_key, 32); assert_equals(bob_public, bob_pair.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); From 2e9021c2e73786e9045c0634c43efa86f18ac8de Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Sun, 4 Sep 2016 23:34:24 +0100 Subject: [PATCH 12/36] OlmSession.has_received_message I find myself wanting to know if an OlmSession is in the pre-key state or not, to help debugging at the application level. --- include/olm/olm.h | 4 ++++ javascript/olm_post.js | 7 +++++++ src/olm.cpp | 6 ++++++ 3 files changed, 17 insertions(+) diff --git a/include/olm/olm.h b/include/olm/olm.h index 0886fa9..81b6602 100644 --- a/include/olm/olm.h +++ b/include/olm/olm.h @@ -300,6 +300,10 @@ size_t olm_session_id( 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 * if multiple messages are sent to this account before this account sends a * message in reply. Returns olm_error() on failure. If the base64 diff --git a/javascript/olm_post.js b/javascript/olm_post.js index 955d68d..6c99030 100644 --- a/javascript/olm_post.js +++ b/javascript/olm_post.js @@ -263,6 +263,13 @@ Session.prototype['session_id'] = restore_stack(function() { 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( one_time_key_message ) { diff --git a/src/olm.cpp b/src/olm.cpp index 682a84c..7f68ad0 100644 --- a/src/olm.cpp +++ b/src/olm.cpp @@ -529,6 +529,12 @@ size_t olm_session_id( } +int olm_session_has_received_message( + OlmSession * session +) { + return from_c(session)->received_message; +} + size_t olm_matches_inbound_session( OlmSession * session, void * one_time_key_message, size_t message_length From 1d4c13c798639c925825c70150b138553f8dff49 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 5 Sep 2016 00:49:36 +0100 Subject: [PATCH 13/36] Fix megolm decryption of UTF-8 Repeat the fix from b10f90d for megolm messages. It turns out that the 'length' argument to 'Pointer_stringify' doesn't work if the input includes characters >= 128. Rather than try to figure out which methods can return UTF-8, and which always return plain ascii, replace all uses of Pointer_stringify with a 'length' argument with the version that expects a NULL-terminated input, and extend the buffer by a byte to allow space for a null-terminator. In the case of decrypt, we need to add the null ourself. Fixes https://github.com/vector-im/vector-web/issues/2078. --- javascript/olm_inbound_group_session.js | 22 ++++++++++++++++++---- javascript/olm_outbound_group_session.js | 22 ++++++++++++++-------- javascript/olm_post.js | 2 +- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/javascript/olm_inbound_group_session.js b/javascript/olm_inbound_group_session.js index 9d526c4..aac2c70 100644 --- a/javascript/olm_inbound_group_session.js +++ b/javascript/olm_inbound_group_session.js @@ -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() { var size = Module['_olm_inbound_group_session_size'](); this.buf = malloc(size); @@ -28,11 +34,11 @@ InboundGroupSession.prototype['pickle'] = restore_stack(function(key) { Module['_olm_pickle_inbound_group_session_length'] )(this.ptr); 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'])( 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) { @@ -66,13 +72,21 @@ InboundGroupSession.prototype['decrypt'] = restore_stack(function( // caculating the length destroys the input buffer. // So we copy the array to a new buffer 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"])( this.ptr, message_buffer, message_array.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); }); olm_exports['InboundGroupSession'] = InboundGroupSession; diff --git a/javascript/olm_outbound_group_session.js b/javascript/olm_outbound_group_session.js index 277a882..e59c8bb 100644 --- a/javascript/olm_outbound_group_session.js +++ b/javascript/olm_outbound_group_session.js @@ -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() { 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'] )(this.ptr); 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'])( 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) { @@ -63,35 +69,35 @@ OutboundGroupSession.prototype['encrypt'] = restore_stack(function(plaintext) { Module['_olm_group_encrypt_message_length'] )(this.ptr, plaintext_array.length); 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'])( this.ptr, plaintext_buffer, plaintext_array.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) { var length = outbound_group_session_method( Module['_olm_outbound_group_session_id_length'] )(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'])( this.ptr, session_id, length ); - return Pointer_stringify(session_id, length); + return Pointer_stringify(session_id); }); OutboundGroupSession.prototype['session_key'] = restore_stack(function(key) { var key_length = outbound_group_session_method( Module['_olm_outbound_group_session_key_length'] )(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'])( this.ptr, key, key_length ); - return Pointer_stringify(key, key_length); + return Pointer_stringify(key); }); OutboundGroupSession.prototype['message_index'] = function() { diff --git a/javascript/olm_post.js b/javascript/olm_post.js index 955d68d..dac89f6 100644 --- a/javascript/olm_post.js +++ b/javascript/olm_post.js @@ -5,7 +5,7 @@ var Pointer_stringify = Module['Pointer_stringify']; var OLM_ERROR = Module['_olm_error'](); /* The 'length' argument to Pointer_stringify doesn't work if the input includes - * cahracters >= 128; we therefore need to add a NULL character to all of our + * 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; From f0acf6582f88ca66b3fabf7d622278da51a94c10 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 2 Sep 2016 15:13:24 +0100 Subject: [PATCH 14/36] Convert Ed25519 and Curve25519 functions to plain C --- include/olm/account.hh | 10 +++--- include/olm/crypto.h | 79 ++++++++++++++++++++++++++++++++++++++++-- include/olm/crypto.hh | 61 -------------------------------- include/olm/pickle.hh | 24 ++++++------- include/olm/ratchet.hh | 10 +++--- include/olm/session.hh | 14 ++++---- include/olm/utility.hh | 6 ++-- src/account.cpp | 28 +++++++-------- src/crypto.cpp | 42 ++++++++++++---------- src/olm.cpp | 10 +++--- src/pickle.cpp | 54 +++++++++++++++++------------ src/ratchet.cpp | 15 ++++---- src/session.cpp | 41 +++++++++++----------- src/utility.cpp | 4 +-- tests/test_crypto.cpp | 36 +++++++++---------- tests/test_ratchet.cpp | 10 +++--- tests/test_session.cpp | 4 +-- 17 files changed, 239 insertions(+), 209 deletions(-) diff --git a/include/olm/account.hh b/include/olm/account.hh index 6ea0d19..4b7b190 100644 --- a/include/olm/account.hh +++ b/include/olm/account.hh @@ -25,14 +25,14 @@ namespace olm { struct IdentityKeys { - Ed25519KeyPair ed25519_key; - Curve25519KeyPair curve25519_key; + _olm_ed25519_key_pair ed25519_key; + _olm_curve25519_key_pair curve25519_key; }; struct OneTimeKey { std::uint32_t id; 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 */ 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 */ std::size_t remove_key( - Curve25519PublicKey const & public_key + _olm_curve25519_public_key const & public_key ); }; diff --git a/include/olm/crypto.h b/include/olm/crypto.h index 325080e..9fc3842 100644 --- a/include/olm/crypto.h +++ b/include/olm/crypto.h @@ -57,9 +57,35 @@ extern "C" { /** length of an aes256 initialisation vector */ #define AES256_IV_LENGTH 16 +struct _olm_curve25519_public_key { + uint8_t public_key[CURVE25519_KEY_LENGTH]; +}; -/** Computes SHA-256 of the input. The output buffer must be a least 32 - * bytes long. */ +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; +}; + + +/** Computes SHA-256 of the input. The output buffer must be a least + * SHA256_OUTPUT_LENGTH (32) bytes long. */ void _olm_crypto_sha256( uint8_t const * input, size_t input_length, uint8_t * output @@ -68,7 +94,7 @@ void _olm_crypto_sha256( /** HMAC: Keyed-Hashing for Message Authentication * http://tools.ietf.org/html/rfc2104 * 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( uint8_t const * key, size_t key_length, uint8_t const * input, size_t input_length, @@ -87,6 +113,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 } // extern "C" #endif diff --git a/include/olm/crypto.hh b/include/olm/crypto.hh index 13fd7e9..e3098cc 100644 --- a/include/olm/crypto.hh +++ b/include/olm/crypto.hh @@ -25,67 +25,6 @@ namespace olm { -struct Curve25519PublicKey { - std::uint8_t public_key[CURVE25519_KEY_LENGTH]; -}; - - -struct Curve25519KeyPair : public Curve25519PublicKey { - std::uint8_t private_key[CURVE25519_KEY_LENGTH]; -}; - - -struct Ed25519PublicKey { - std::uint8_t public_key[ED25519_PUBLIC_KEY_LENGTH]; -}; - - -struct Ed25519KeyPair : public Ed25519PublicKey { - std::uint8_t private_key[ED25519_PRIVATE_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 -); - - -/** 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[AES256_KEY_LENGTH]; diff --git a/include/olm/pickle.hh b/include/olm/pickle.hh index 13e6b01..c0b6ebf 100644 --- a/include/olm/pickle.hh +++ b/include/olm/pickle.hh @@ -109,70 +109,70 @@ std::uint8_t const * unpickle_bytes( std::size_t pickle_length( - const Curve25519PublicKey & value + const _olm_curve25519_public_key & value ); std::uint8_t * pickle( std::uint8_t * pos, - const Curve25519PublicKey & value + const _olm_curve25519_public_key & value ); std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, - Curve25519PublicKey & value + _olm_curve25519_public_key & value ); std::size_t pickle_length( - const Curve25519KeyPair & value + const _olm_curve25519_key_pair & value ); std::uint8_t * pickle( std::uint8_t * pos, - const Curve25519KeyPair & value + const _olm_curve25519_key_pair & value ); std::uint8_t const * unpickle( 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 + const _olm_ed25519_public_key & value ); std::uint8_t * pickle( std::uint8_t * pos, - const Ed25519PublicKey & value + const _olm_ed25519_public_key & value ); std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, - Ed25519PublicKey & value + _olm_ed25519_public_key & value ); std::size_t pickle_length( - const Ed25519KeyPair & value + const _olm_ed25519_key_pair & value ); std::uint8_t * pickle( std::uint8_t * pos, - const Ed25519KeyPair & value + const _olm_ed25519_key_pair & value ); std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, - Ed25519KeyPair & value + _olm_ed25519_key_pair & value ); } // namespace olm diff --git a/include/olm/ratchet.hh b/include/olm/ratchet.hh index e91d634..cdcba6b 100644 --- a/include/olm/ratchet.hh +++ b/include/olm/ratchet.hh @@ -41,19 +41,19 @@ struct MessageKey { struct SenderChain { - Curve25519KeyPair ratchet_key; + _olm_curve25519_key_pair ratchet_key; ChainKey chain_key; }; struct ReceiverChain { - Curve25519PublicKey ratchet_key; + _olm_curve25519_public_key ratchet_key; ChainKey chain_key; }; struct SkippedMessageKey { - Curve25519PublicKey ratchet_key; + _olm_curve25519_public_key ratchet_key; MessageKey message_key; }; @@ -108,14 +108,14 @@ struct Ratchet { * remote's first ratchet key */ void initialise_as_bob( 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 * pair for the first ratchet key */ void initialise_as_alice( 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 diff --git a/include/olm/session.hh b/include/olm/session.hh index 5b91cb1..9d44816 100644 --- a/include/olm/session.hh +++ b/include/olm/session.hh @@ -35,9 +35,9 @@ struct Session { bool received_message; - Curve25519PublicKey alice_identity_key; - Curve25519PublicKey alice_base_key; - Curve25519PublicKey bob_one_time_key; + _olm_curve25519_public_key alice_identity_key; + _olm_curve25519_public_key alice_base_key; + _olm_curve25519_public_key bob_one_time_key; /** 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. */ @@ -48,8 +48,8 @@ struct Session { * NOT_ENOUGH_RANDOM if the number of random bytes was too small. */ std::size_t new_outbound_session( Account const & local_account, - Curve25519PublicKey const & identity_key, - Curve25519PublicKey const & one_time_key, + _olm_curve25519_public_key const & identity_key, + _olm_curve25519_public_key const & one_time_key, std::uint8_t const * random, std::size_t random_length ); @@ -59,7 +59,7 @@ struct Session { * the message headers could not be decoded. */ std::size_t new_inbound_session( 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 ); @@ -82,7 +82,7 @@ struct Session { * session does not match or the pre-key message could not be decoded. */ 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 ); diff --git a/include/olm/utility.hh b/include/olm/utility.hh index 1339fe5..d650abc 100644 --- a/include/olm/utility.hh +++ b/include/olm/utility.hh @@ -21,9 +21,9 @@ #include #include -namespace olm { +struct _olm_ed25519_public_key; -struct Ed25519PublicKey; +namespace olm { struct Utility { @@ -48,7 +48,7 @@ struct Utility { * 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. */ 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 * signature, std::size_t signature_length ); diff --git a/src/account.cpp b/src/account.cpp index 9512068..c77f95c 100644 --- a/src/account.cpp +++ b/src/account.cpp @@ -24,10 +24,10 @@ olm::Account::Account( olm::OneTimeKey const * olm::Account::lookup_key( - olm::Curve25519PublicKey const & public_key + _olm_curve25519_public_key const & public_key ) { for (olm::OneTimeKey const & key : one_time_keys) { - if (olm::array_equal(key.key.public_key, public_key.public_key)) { + if (olm::array_equal(key.key.public_key.public_key, public_key.public_key)) { return &key; } } @@ -35,11 +35,11 @@ olm::OneTimeKey const * olm::Account::lookup_key( } std::size_t olm::Account::remove_key( - olm::Curve25519PublicKey const & public_key + _olm_curve25519_public_key const & public_key ) { OneTimeKey * i; for (i = one_time_keys.begin(); i != one_time_keys.end(); ++i) { - if (olm::array_equal(i->key.public_key, public_key.public_key)) { + if (olm::array_equal(i->key.public_key.public_key, public_key.public_key)) { std::uint32_t id = i->id; one_time_keys.erase(i); return id; @@ -60,9 +60,9 @@ std::size_t olm::Account::new_account( 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 += ED25519_RANDOM_LENGTH; - olm::curve25519_generate_key(random, identity_keys.curve25519_key); + _olm_crypto_curve25519_generate_key(random, &identity_keys.curve25519_key); return 0; } @@ -118,16 +118,16 @@ std::size_t olm::Account::get_identity_json( pos = write_string(pos, KEY_JSON_CURVE25519); *(pos++) = '\"'; pos = olm::encode_base64( - identity_keys.curve25519_key.public_key, - sizeof(identity_keys.curve25519_key.public_key), + identity_keys.curve25519_key.public_key.public_key, + sizeof(identity_keys.curve25519_key.public_key.public_key), pos ); *(pos++) = '\"'; *(pos++) = ','; pos = write_string(pos, KEY_JSON_ED25519); *(pos++) = '\"'; pos = olm::encode_base64( - identity_keys.ed25519_key.public_key, - sizeof(identity_keys.ed25519_key.public_key), + identity_keys.ed25519_key.public_key.public_key, + sizeof(identity_keys.ed25519_key.public_key.public_key), pos ); *(pos++) = '\"'; *(pos++) = '}'; @@ -149,8 +149,8 @@ std::size_t olm::Account::sign( last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } - olm::ed25519_sign( - identity_keys.ed25519_key, message, message_length, signature + _olm_crypto_ed25519_sign( + &identity_keys.ed25519_key, message, message_length, signature ); return this->signature_length(); } @@ -202,7 +202,7 @@ std::size_t olm::Account::get_one_time_keys_json( pos = olm::encode_base64(key_id, sizeof(key_id), pos); *(pos++) = '\"'; *(pos++) = ':'; *(pos++) = '\"'; pos = olm::encode_base64( - key.key.public_key, sizeof(key.key.public_key), pos + key.key.public_key.public_key, sizeof(key.key.public_key.public_key), pos ); *(pos++) = '\"'; sep = ','; @@ -253,7 +253,7 @@ std::size_t olm::Account::generate_one_time_keys( OneTimeKey & key = *one_time_keys.insert(one_time_keys.begin()); key.id = ++next_one_time_key_id; key.published = false; - olm::curve25519_generate_key(random, key.key); + _olm_crypto_curve25519_generate_key(random, &key.key); random += CURVE25519_RANDOM_LENGTH; } return number_of_keys; diff --git a/src/crypto.cpp b/src/crypto.cpp index 0b08c54..89d9d72 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -100,59 +100,65 @@ inline static void hmac_sha256_final( } // namespace -void olm::curve25519_generate_key( - std::uint8_t const * random_32_bytes, - olm::Curve25519KeyPair & key_pair +void _olm_crypto_curve25519_generate_key( + uint8_t const * random_32_bytes, + struct _olm_curve25519_key_pair *key_pair ) { - std::memcpy(key_pair.private_key, random_32_bytes, CURVE25519_KEY_LENGTH); + std::memcpy( + key_pair->private_key.private_key, random_32_bytes, + CURVE25519_KEY_LENGTH + ); ::curve25519_donna( - key_pair.public_key, key_pair.private_key, CURVE25519_BASEPOINT + key_pair->public_key.public_key, + key_pair->private_key.private_key, + CURVE25519_BASEPOINT ); } -void olm::curve25519_shared_secret( - olm::Curve25519KeyPair const & our_key, - olm::Curve25519PublicKey const & their_key, +void _olm_crypto_curve25519_shared_secret( + const struct _olm_curve25519_key_pair *our_key, + const struct _olm_curve25519_public_key * their_key, std::uint8_t * output ) { - ::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::ed25519_generate_key( +void _olm_crypto_ed25519_generate_key( std::uint8_t const * random_32_bytes, - olm::Ed25519KeyPair & key_pair + struct _olm_ed25519_key_pair *key_pair ) { ::ed25519_create_keypair( - key_pair.public_key, key_pair.private_key, + key_pair->public_key.public_key, key_pair->private_key.private_key, random_32_bytes ); } -void olm::ed25519_sign( - olm::Ed25519KeyPair const & our_key, +void _olm_crypto_ed25519_sign( + const struct _olm_ed25519_key_pair *our_key, std::uint8_t const * message, std::size_t message_length, std::uint8_t * output ) { ::ed25519_sign( output, message, message_length, - our_key.public_key, our_key.private_key + our_key->public_key.public_key, + our_key->private_key.private_key ); } -bool olm::ed25519_verify( - olm::Ed25519PublicKey const & their_key, +int _olm_crypto_ed25519_verify( + const struct _olm_ed25519_public_key *their_key, std::uint8_t const * message, std::size_t message_length, std::uint8_t const * signature ) { return 0 != ::ed25519_verify( signature, message, message_length, - their_key.public_key + their_key->public_key ); } diff --git a/src/olm.cpp b/src/olm.cpp index 3fe9c5e..d3af19c 100644 --- a/src/olm.cpp +++ b/src/olm.cpp @@ -442,8 +442,8 @@ size_t olm_create_outbound_session( from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64; return std::size_t(-1); } - olm::Curve25519PublicKey identity_key; - olm::Curve25519PublicKey one_time_key; + _olm_curve25519_public_key identity_key; + _olm_curve25519_public_key one_time_key; olm::decode_base64(id_key, id_key_length, identity_key.public_key); olm::decode_base64(ot_key, ot_key_length, one_time_key.public_key); @@ -487,7 +487,7 @@ size_t olm_create_inbound_session_from( from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64; return std::size_t(-1); } - olm::Curve25519PublicKey identity_key; + _olm_curve25519_public_key identity_key; olm::decode_base64(id_key, id_key_length, identity_key.public_key); std::size_t raw_length = b64_input( @@ -564,7 +564,7 @@ size_t olm_matches_inbound_session_from( from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64; return std::size_t(-1); } - olm::Curve25519PublicKey identity_key; + _olm_curve25519_public_key identity_key; olm::decode_base64(id_key, id_key_length, identity_key.public_key); std::size_t raw_length = b64_input( @@ -720,7 +720,7 @@ size_t olm_ed25519_verify( from_c(utility)->last_error = OlmErrorCode::OLM_INVALID_BASE64; return std::size_t(-1); } - olm::Ed25519PublicKey verify_key; + _olm_ed25519_public_key verify_key; olm::decode_base64(from_c(key), key_length, verify_key.public_key); std::size_t raw_signature_length = b64_input( from_c(signature), signature_length, from_c(utility)->last_error diff --git a/src/pickle.cpp b/src/pickle.cpp index fc3e2b4..e9708b7 100644 --- a/src/pickle.cpp +++ b/src/pickle.cpp @@ -71,7 +71,7 @@ std::uint8_t const * olm::unpickle_bytes( std::size_t olm::pickle_length( - const olm::Curve25519PublicKey & value + const _olm_curve25519_public_key & value ) { return sizeof(value.public_key); } @@ -79,7 +79,7 @@ std::size_t olm::pickle_length( std::uint8_t * olm::pickle( std::uint8_t * pos, - const olm::Curve25519PublicKey & value + const _olm_curve25519_public_key & value ) { pos = olm::pickle_bytes( pos, value.public_key, sizeof(value.public_key) @@ -90,7 +90,7 @@ std::uint8_t * olm::pickle( std::uint8_t const * olm::unpickle( std::uint8_t const * pos, std::uint8_t const * end, - olm::Curve25519PublicKey & value + _olm_curve25519_public_key & value ) { pos = olm::unpickle_bytes( pos, end, value.public_key, sizeof(value.public_key) @@ -101,21 +101,24 @@ std::uint8_t const * olm::unpickle( std::size_t olm::pickle_length( - const olm::Curve25519KeyPair & value + const _olm_curve25519_key_pair & value ) { - return sizeof(value.public_key) + sizeof(value.private_key); + return sizeof(value.public_key.public_key) + + sizeof(value.private_key.private_key); } std::uint8_t * olm::pickle( std::uint8_t * pos, - const olm::Curve25519KeyPair & value + const _olm_curve25519_key_pair & value ) { pos = olm::pickle_bytes( - pos, value.public_key, sizeof(value.public_key) + pos, value.public_key.public_key, + sizeof(value.public_key.public_key) ); pos = olm::pickle_bytes( - pos, value.private_key, sizeof(value.private_key) + pos, value.private_key.private_key, + sizeof(value.private_key.private_key) ); return pos; } @@ -123,19 +126,21 @@ std::uint8_t * olm::pickle( std::uint8_t const * olm::unpickle( std::uint8_t const * pos, std::uint8_t const * end, - olm::Curve25519KeyPair & value + _olm_curve25519_key_pair & value ) { pos = olm::unpickle_bytes( - pos, end, value.public_key, sizeof(value.public_key) + pos, end, value.public_key.public_key, + sizeof(value.public_key.public_key) ); pos = olm::unpickle_bytes( - pos, end, value.private_key, sizeof(value.private_key) + pos, end, value.private_key.private_key, + sizeof(value.private_key.private_key) ); return pos; } std::size_t olm::pickle_length( - const olm::Ed25519PublicKey & value + const _olm_ed25519_public_key & value ) { return sizeof(value.public_key); } @@ -143,7 +148,7 @@ std::size_t olm::pickle_length( std::uint8_t * olm::pickle( std::uint8_t * pos, - const olm::Ed25519PublicKey & value + const _olm_ed25519_public_key & value ) { pos = olm::pickle_bytes( pos, value.public_key, sizeof(value.public_key) @@ -154,7 +159,7 @@ std::uint8_t * olm::pickle( std::uint8_t const * olm::unpickle( std::uint8_t const * pos, std::uint8_t const * end, - olm::Ed25519PublicKey & value + _olm_ed25519_public_key & value ) { pos = olm::unpickle_bytes( pos, end, value.public_key, sizeof(value.public_key) @@ -165,21 +170,24 @@ std::uint8_t const * olm::unpickle( std::size_t olm::pickle_length( - const olm::Ed25519KeyPair & value + const _olm_ed25519_key_pair & value ) { - return sizeof(value.public_key) + sizeof(value.private_key); + return sizeof(value.public_key.public_key) + + sizeof(value.private_key.private_key); } std::uint8_t * olm::pickle( std::uint8_t * pos, - const olm::Ed25519KeyPair & value + const _olm_ed25519_key_pair & value ) { pos = olm::pickle_bytes( - pos, value.public_key, sizeof(value.public_key) + pos, value.public_key.public_key, + sizeof(value.public_key.public_key) ); pos = olm::pickle_bytes( - pos, value.private_key, sizeof(value.private_key) + pos, value.private_key.private_key, + sizeof(value.private_key.private_key) ); return pos; } @@ -187,13 +195,15 @@ std::uint8_t * olm::pickle( std::uint8_t const * olm::unpickle( std::uint8_t const * pos, std::uint8_t const * end, - olm::Ed25519KeyPair & value + _olm_ed25519_key_pair & value ) { pos = olm::unpickle_bytes( - pos, end, value.public_key, sizeof(value.public_key) + pos, end, value.public_key.public_key, + sizeof(value.public_key.public_key) ); pos = olm::unpickle_bytes( - pos, end, value.private_key, sizeof(value.private_key) + pos, end, value.private_key.private_key, + sizeof(value.private_key.private_key) ); return pos; } diff --git a/src/ratchet.cpp b/src/ratchet.cpp index 57cb385..279c4c0 100644 --- a/src/ratchet.cpp +++ b/src/ratchet.cpp @@ -41,14 +41,14 @@ static const std::size_t MAX_MESSAGE_GAP = 2000; */ static void create_chain_key( olm::SharedKey const & root_key, - olm::Curve25519KeyPair const & our_key, - olm::Curve25519PublicKey const & their_key, + _olm_curve25519_key_pair const & our_key, + _olm_curve25519_public_key const & their_key, olm::KdfInfo const & info, olm::SharedKey & new_root_key, olm::ChainKey & new_chain_key ) { olm::SharedKey secret; - olm::curve25519_shared_secret(our_key, their_key, secret); + _olm_crypto_curve25519_shared_secret(&our_key, &their_key, secret); std::uint8_t derived_secrets[2 * olm::OLM_SHARED_KEY_LENGTH]; _olm_crypto_hkdf_sha256( secret, sizeof(secret), @@ -189,7 +189,7 @@ olm::Ratchet::Ratchet( void olm::Ratchet::initialise_as_bob( std::uint8_t const * shared_secret, std::size_t shared_secret_length, - olm::Curve25519PublicKey const & their_ratchet_key + _olm_curve25519_public_key const & their_ratchet_key ) { std::uint8_t derived_secrets[2 * olm::OLM_SHARED_KEY_LENGTH]; _olm_crypto_hkdf_sha256( @@ -210,7 +210,7 @@ void olm::Ratchet::initialise_as_bob( void olm::Ratchet::initialise_as_alice( std::uint8_t const * shared_secret, std::size_t shared_secret_length, - olm::Curve25519KeyPair const & our_ratchet_key + _olm_curve25519_key_pair const & our_ratchet_key ) { std::uint8_t derived_secrets[2 * olm::OLM_SHARED_KEY_LENGTH]; _olm_crypto_hkdf_sha256( @@ -437,7 +437,7 @@ std::size_t olm::Ratchet::encrypt( if (sender_chain.empty()) { sender_chain.insert(); - olm::curve25519_generate_key(random, sender_chain[0].ratchet_key); + _olm_crypto_curve25519_generate_key(random, &sender_chain[0].ratchet_key); create_chain_key( root_key, sender_chain[0].ratchet_key, @@ -456,7 +456,8 @@ std::size_t olm::Ratchet::encrypt( plaintext_length ); std::uint32_t counter = keys.index; - Curve25519PublicKey const & ratchet_key = sender_chain[0].ratchet_key; + _olm_curve25519_public_key const & ratchet_key = + sender_chain[0].ratchet_key.public_key; olm::MessageWriter writer; diff --git a/src/session.cpp b/src/session.cpp index 7bde5d1..72e2be8 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -55,8 +55,8 @@ std::size_t olm::Session::new_outbound_session_random_length() { std::size_t olm::Session::new_outbound_session( olm::Account const & local_account, - olm::Curve25519PublicKey const & identity_key, - olm::Curve25519PublicKey const & one_time_key, + _olm_curve25519_public_key const & identity_key, + _olm_curve25519_public_key const & one_time_key, std::uint8_t const * random, std::size_t random_length ) { if (random_length < new_outbound_session_random_length()) { @@ -64,29 +64,30 @@ std::size_t olm::Session::new_outbound_session( return std::size_t(-1); } - olm::Curve25519KeyPair base_key; - olm::curve25519_generate_key(random, base_key); + _olm_curve25519_key_pair base_key; + _olm_crypto_curve25519_generate_key(random, &base_key); - olm::Curve25519KeyPair ratchet_key; - olm::curve25519_generate_key(random + CURVE25519_RANDOM_LENGTH, ratchet_key); + _olm_curve25519_key_pair ratchet_key; + _olm_crypto_curve25519_generate_key(random + CURVE25519_RANDOM_LENGTH, &ratchet_key); - olm::Curve25519KeyPair const & alice_identity_key_pair = ( + _olm_curve25519_key_pair const & alice_identity_key_pair = ( local_account.identity_keys.curve25519_key ); received_message = false; - alice_identity_key = alice_identity_key_pair; - alice_base_key = base_key; + alice_identity_key = alice_identity_key_pair.public_key; + alice_base_key = base_key.public_key; bob_one_time_key = one_time_key; // Calculate the shared secret S via triple DH std::uint8_t secret[3 * CURVE25519_SHARED_SECRET_LENGTH]; std::uint8_t * pos = secret; - olm::curve25519_shared_secret(alice_identity_key_pair, one_time_key, pos); + + _olm_crypto_curve25519_shared_secret(&alice_identity_key_pair, &one_time_key, pos); 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 += 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); @@ -120,7 +121,7 @@ static bool check_message_fields( std::size_t olm::Session::new_inbound_session( olm::Account & local_account, - olm::Curve25519PublicKey const * their_identity_key, + _olm_curve25519_public_key const * their_identity_key, std::uint8_t const * one_time_key_message, std::size_t message_length ) { olm::PreKeyMessageReader reader; @@ -157,7 +158,7 @@ std::size_t olm::Session::new_inbound_session( 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::OneTimeKey const * our_one_time_key = local_account.lookup_key( @@ -169,19 +170,19 @@ std::size_t olm::Session::new_inbound_session( return std::size_t(-1); } - olm::Curve25519KeyPair const & bob_identity_key = ( + _olm_curve25519_key_pair const & bob_identity_key = ( local_account.identity_keys.curve25519_key ); - olm::Curve25519KeyPair const & bob_one_time_key = our_one_time_key->key; + _olm_curve25519_key_pair const & bob_one_time_key = our_one_time_key->key; // Calculate the shared secret S via triple DH std::uint8_t secret[CURVE25519_SHARED_SECRET_LENGTH * 3]; std::uint8_t * pos = secret; - olm::curve25519_shared_secret(bob_one_time_key, alice_identity_key, pos); + _olm_crypto_curve25519_shared_secret(&bob_one_time_key, &alice_identity_key, pos); 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 += 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); @@ -214,7 +215,7 @@ std::size_t olm::Session::session_id( bool olm::Session::matches_inbound_session( - olm::Curve25519PublicKey const * their_identity_key, + _olm_curve25519_public_key const * their_identity_key, std::uint8_t const * one_time_key_message, std::size_t message_length ) { olm::PreKeyMessageReader reader; diff --git a/src/utility.cpp b/src/utility.cpp index e33351c..43d8e16 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -41,7 +41,7 @@ size_t olm::Utility::sha256( size_t olm::Utility::ed25519_verify( - Ed25519PublicKey const & key, + _olm_ed25519_public_key const & key, std::uint8_t const * message, std::size_t message_length, std::uint8_t const * signature, std::size_t signature_length ) { @@ -49,7 +49,7 @@ size_t olm::Utility::ed25519_verify( last_error = OlmErrorCode::OLM_BAD_MESSAGE_MAC; 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; return std::size_t(-1); } diff --git a/tests/test_crypto.cpp b/tests/test_crypto.cpp index 56abdcd..12715d0 100644 --- a/tests/test_crypto.cpp +++ b/tests/test_crypto.cpp @@ -58,25 +58,25 @@ std::uint8_t expected_agreement[32] = { 0x76, 0xF0, 0x9B, 0x3C, 0x1E, 0x16, 0x17, 0x42 }; -olm::Curve25519KeyPair alice_pair; -olm::curve25519_generate_key(alice_private, alice_pair); +_olm_curve25519_key_pair alice_pair; +_olm_crypto_curve25519_generate_key(alice_private, &alice_pair); -assert_equals(alice_private, alice_pair.private_key, 32); -assert_equals(alice_public, alice_pair.public_key, 32); +assert_equals(alice_private, alice_pair.private_key.private_key, 32); +assert_equals(alice_public, alice_pair.public_key.public_key, 32); -olm::Curve25519KeyPair bob_pair; -olm::curve25519_generate_key(bob_private, bob_pair); +_olm_curve25519_key_pair bob_pair; +_olm_crypto_curve25519_generate_key(bob_private, &bob_pair); -assert_equals(bob_private, bob_pair.private_key, 32); -assert_equals(bob_public, bob_pair.public_key, 32); +assert_equals(bob_private, bob_pair.private_key.private_key, 32); +assert_equals(bob_public, bob_pair.public_key.public_key, 32); 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); -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); @@ -90,22 +90,22 @@ std::uint8_t private_key[33] = "This key is a string of 32 bytes"; std::uint8_t message[] = "Hello, World"; std::size_t message_length = sizeof(message) - 1; -olm::Ed25519KeyPair key_pair; -olm::ed25519_generate_key(private_key, key_pair); +_olm_ed25519_key_pair key_pair; +_olm_crypto_ed25519_generate_key(private_key, &key_pair); std::uint8_t signature[64]; -olm::ed25519_sign( - key_pair, message, message_length, signature +_olm_crypto_ed25519_sign( + &key_pair, message, message_length, signature ); -bool result = olm::ed25519_verify( - key_pair, message, message_length, signature +bool result = _olm_crypto_ed25519_verify( + &key_pair.public_key, message, message_length, signature ); assert_equals(true, result); message[0] = 'n'; -result = olm::ed25519_verify( - key_pair, message, message_length, signature +result = _olm_crypto_ed25519_verify( + &key_pair.public_key, message, message_length, signature ); assert_equals(false, result); } diff --git a/tests/test_ratchet.cpp b/tests/test_ratchet.cpp index 2f8412e..fb60ba9 100644 --- a/tests/test_ratchet.cpp +++ b/tests/test_ratchet.cpp @@ -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); std::uint8_t random_bytes[] = "0123456789ABDEF0123456789ABCDEF"; -olm::Curve25519KeyPair alice_key; -olm::curve25519_generate_key(random_bytes, alice_key); +_olm_curve25519_key_pair alice_key; +_olm_crypto_curve25519_generate_key(random_bytes, &alice_key); std::uint8_t shared_secret[] = "A secret"; @@ -44,7 +44,7 @@ olm::Ratchet alice(kdf_info, cipher); olm::Ratchet bob(kdf_info, cipher); 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::size_t plaintext_length = sizeof(plaintext) - 1; @@ -113,7 +113,7 @@ olm::Ratchet alice(kdf_info, cipher); olm::Ratchet bob(kdf_info, cipher); 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::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); 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"; assert_equals(std::size_t(15), sizeof(plaintext)); diff --git a/tests/test_session.cpp b/tests/test_session.cpp index c4c5b2a..e2c3199 100644 --- a/tests/test_session.cpp +++ b/tests/test_session.cpp @@ -33,12 +33,12 @@ void check_session(const olm::Session &session) { assert_equals( 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( 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( From 69f269ffaf88515f6d5c0b34178bf0096cf5773b Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 2 Sep 2016 15:35:04 +0100 Subject: [PATCH 15/36] Convert AES functions to plain C --- include/olm/account.hh | 2 +- include/olm/crypto.h | 35 +++++++++++++++++++++ include/olm/crypto.hh | 69 ------------------------------------------ include/olm/pickle.hh | 2 +- include/olm/ratchet.hh | 4 ++- src/cipher.cpp | 16 +++++----- src/crypto.cpp | 24 +++++++-------- src/session.cpp | 2 +- src/utility.cpp | 2 +- tests/test_crypto.cpp | 12 ++++---- 10 files changed, 68 insertions(+), 100 deletions(-) delete mode 100644 include/olm/crypto.hh diff --git a/include/olm/account.hh b/include/olm/account.hh index 4b7b190..7e58ca3 100644 --- a/include/olm/account.hh +++ b/include/olm/account.hh @@ -16,7 +16,7 @@ #define OLM_ACCOUNT_HH_ #include "olm/list.hh" -#include "olm/crypto.hh" +#include "olm/crypto.h" #include "olm/error.h" #include diff --git a/include/olm/crypto.h b/include/olm/crypto.h index 9fc3842..dbf78ed 100644 --- a/include/olm/crypto.h +++ b/include/olm/crypto.h @@ -57,6 +57,15 @@ extern "C" { /** 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]; }; @@ -84,6 +93,32 @@ struct _olm_ed25519_key_pair { }; +/** 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( diff --git a/include/olm/crypto.hh b/include/olm/crypto.hh deleted file mode 100644 index e3098cc..0000000 --- a/include/olm/crypto.hh +++ /dev/null @@ -1,69 +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 -#include - -// 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 { - - -struct Aes256Key { - std::uint8_t key[AES256_KEY_LENGTH]; -}; - - -struct Aes256Iv { - std::uint8_t iv[AES256_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_ */ diff --git a/include/olm/pickle.hh b/include/olm/pickle.hh index c0b6ebf..a09b8a1 100644 --- a/include/olm/pickle.hh +++ b/include/olm/pickle.hh @@ -16,7 +16,7 @@ #define OLM_PICKLE_HH_ #include "olm/list.hh" -#include "olm/crypto.hh" +#include "olm/crypto.h" #include #include diff --git a/include/olm/ratchet.hh b/include/olm/ratchet.hh index cdcba6b..2e87e35 100644 --- a/include/olm/ratchet.hh +++ b/include/olm/ratchet.hh @@ -13,7 +13,9 @@ * limitations under the License. */ -#include "olm/crypto.hh" +#include + +#include "olm/crypto.h" #include "olm/list.hh" #include "olm/error.h" diff --git a/src/cipher.cpp b/src/cipher.cpp index 8e3d7a5..6b53690 100644 --- a/src/cipher.cpp +++ b/src/cipher.cpp @@ -13,7 +13,7 @@ * limitations under the License. */ #include "olm/cipher.h" -#include "olm/crypto.hh" +#include "olm/crypto.h" #include "olm/memory.hh" #include @@ -22,9 +22,9 @@ const std::size_t HMAC_KEY_LENGTH = 32; namespace { struct DerivedKeys { - olm::Aes256Key aes_key; + _olm_aes256_key aes_key; std::uint8_t mac_key[HMAC_KEY_LENGTH]; - olm::Aes256Iv aes_iv; + _olm_aes256_iv aes_iv; }; @@ -58,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( 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( @@ -80,8 +80,8 @@ size_t aes_sha_256_cipher_encrypt( derive_keys(c->kdf_info, c->kdf_info_length, key, key_length, keys); - olm::aes_encrypt_cbc( - keys.aes_key, keys.aes_iv, plaintext, plaintext_length, ciphertext + _olm_crypto_aes_encrypt_cbc( + &keys.aes_key, &keys.aes_iv, plaintext, plaintext_length, ciphertext ); _olm_crypto_hmac_sha256( @@ -126,8 +126,8 @@ size_t aes_sha_256_cipher_decrypt( return std::size_t(-1); } - std::size_t plaintext_length = olm::aes_decrypt_cbc( - keys.aes_key, keys.aes_iv, ciphertext, ciphertext_length, plaintext + std::size_t plaintext_length = _olm_crypto_aes_decrypt_cbc( + &keys.aes_key, &keys.aes_iv, ciphertext, ciphertext_length, plaintext ); olm::unset(keys); diff --git a/src/crypto.cpp b/src/crypto.cpp index 89d9d72..5095c79 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "olm/crypto.hh" +#include "olm/crypto.h" #include "olm/memory.hh" #include @@ -163,23 +163,23 @@ int _olm_crypto_ed25519_verify( } -std::size_t olm::aes_encrypt_cbc_length( +std::size_t _olm_crypto_aes_encrypt_cbc_length( std::size_t input_length ) { return input_length + AES_BLOCK_LENGTH - input_length % AES_BLOCK_LENGTH; } -void olm::aes_encrypt_cbc( - olm::Aes256Key const & key, - olm::Aes256Iv const & iv, +void _olm_crypto_aes_encrypt_cbc( + _olm_aes256_key const *key, + _olm_aes256_iv const *iv, std::uint8_t const * input, std::size_t input_length, std::uint8_t * output ) { std::uint32_t key_schedule[AES_KEY_SCHEDULE_LENGTH]; - ::aes_key_setup(key.key, key_schedule, AES_KEY_BITS); + ::aes_key_setup(key->key, key_schedule, AES_KEY_BITS); std::uint8_t input_block[AES_BLOCK_LENGTH]; - std::memcpy(input_block, iv.iv, AES_BLOCK_LENGTH); + std::memcpy(input_block, iv->iv, AES_BLOCK_LENGTH); while (input_length >= AES_BLOCK_LENGTH) { xor_block(input_block, input); ::aes_encrypt(input_block, output, key_schedule, AES_KEY_BITS); @@ -201,17 +201,17 @@ void olm::aes_encrypt_cbc( } -std::size_t olm::aes_decrypt_cbc( - olm::Aes256Key const & key, - olm::Aes256Iv const & iv, +std::size_t _olm_crypto_aes_decrypt_cbc( + _olm_aes256_key const *key, + _olm_aes256_iv const *iv, std::uint8_t const * input, std::size_t input_length, std::uint8_t * output ) { std::uint32_t key_schedule[AES_KEY_SCHEDULE_LENGTH]; - ::aes_key_setup(key.key, key_schedule, AES_KEY_BITS); + ::aes_key_setup(key->key, key_schedule, AES_KEY_BITS); std::uint8_t block1[AES_BLOCK_LENGTH]; std::uint8_t block2[AES_BLOCK_LENGTH]; - std::memcpy(block1, iv.iv, AES_BLOCK_LENGTH); + std::memcpy(block1, iv->iv, AES_BLOCK_LENGTH); for (std::size_t i = 0; i < input_length; i += AES_BLOCK_LENGTH) { std::memcpy(block2, &input[i], AES_BLOCK_LENGTH); ::aes_decrypt(&input[i], &output[i], key_schedule, AES_KEY_BITS); diff --git a/src/session.cpp b/src/session.cpp index 72e2be8..f1bc5a7 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -14,7 +14,7 @@ */ #include "olm/session.hh" #include "olm/cipher.h" -#include "olm/crypto.hh" +#include "olm/crypto.h" #include "olm/account.hh" #include "olm/memory.hh" #include "olm/message.hh" diff --git a/src/utility.cpp b/src/utility.cpp index 43d8e16..e9688de 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -14,7 +14,7 @@ */ #include "olm/utility.hh" -#include "olm/crypto.hh" +#include "olm/crypto.h" olm::Utility::Utility( diff --git a/tests/test_crypto.cpp b/tests/test_crypto.cpp index 12715d0..7dad892 100644 --- a/tests/test_crypto.cpp +++ b/tests/test_crypto.cpp @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "olm/crypto.hh" +#include "olm/crypto.h" #include "unittest.hh" @@ -115,8 +115,8 @@ assert_equals(false, result); TestCase test_case("AES Test Case 1"); -olm::Aes256Key key = {}; -olm::Aes256Iv iv = {}; +_olm_aes256_key key = {}; +_olm_aes256_iv iv = {}; std::uint8_t input[16] = {}; std::uint8_t expected[32] = { @@ -126,16 +126,16 @@ std::uint8_t expected[32] = { 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); 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); -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(input, actual, length); From c09aa77c4a8dd27d98d2fe454fa1b589fee47977 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 5 Sep 2016 11:56:53 +0100 Subject: [PATCH 16/36] Avoid ldconfig in the Makefile ... because OSX doesn't support it. --- Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 92e3285..ad97c5d 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,7 @@ CC = gcc EMCC = emcc AFL_CC = afl-gcc AFL_CXX = afl-g++ + RELEASE_TARGET := $(BUILD_DIR)/libolm.so.$(VERSION) DEBUG_TARGET := $(BUILD_DIR)/libolm_debug.so.$(VERSION) JS_TARGET := javascript/olm.js @@ -107,7 +108,7 @@ $(RELEASE_TARGET): $(RELEASE_OBJECTS) -Wl,-soname,libolm.so.$(MAJOR) \ -Wl,--version-script,version_script.ver \ $(OUTPUT_OPTION) $(RELEASE_OBJECTS) - ldconfig -l $@ + ln -sf libolm.so.$(VERSION) $(BUILD_DIR)/libolm.so.$(MAJOR) debug: $(DEBUG_TARGET) .PHONY: debug @@ -117,7 +118,7 @@ $(DEBUG_TARGET): $(DEBUG_OBJECTS) -Wl,-soname,libolm_debug.so.$(MAJOR) \ -Wl,--version-script,version_script.ver \ $(OUTPUT_OPTION) $(DEBUG_OBJECTS) - ldconfig -l $@ + ln -sf libolm_debug.so.$(VERSION) $(BUILD_DIR)/libolm_debug.so.$(MAJOR) js: $(JS_TARGET) .PHONY: js From 833ecd3c736cea71a693cd9dedec4f1bffa87000 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 5 Sep 2016 12:59:12 +0100 Subject: [PATCH 17/36] Convert ed25519 pickling functions to C ... so that I can use them from the group session bits. --- include/olm/pickle.h | 40 ++++++++++++ include/olm/pickle.hh | 34 ----------- src/account.cpp | 7 ++- src/pickle.cpp | 137 +++++++++++++++++++++--------------------- 4 files changed, 112 insertions(+), 106 deletions(-) diff --git a/include/olm/pickle.h b/include/olm/pickle.h index c1e8192..0e668bb 100644 --- a/include/olm/pickle.h +++ b/include/olm/pickle.h @@ -21,6 +21,10 @@ extern "C" { #endif +struct _olm_ed25519_public_key; +struct _olm_ed25519_key_pair; + + #define _olm_pickle_uint32_length(value) 4 uint8_t * _olm_pickle_uint32(uint8_t * pos, uint32_t value); 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); +/** 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 } // extern "C" #endif diff --git a/include/olm/pickle.hh b/include/olm/pickle.hh index a09b8a1..4cf3f36 100644 --- a/include/olm/pickle.hh +++ b/include/olm/pickle.hh @@ -141,40 +141,6 @@ std::uint8_t const * unpickle( _olm_curve25519_key_pair & value ); - -std::size_t pickle_length( - const _olm_ed25519_public_key & value -); - - -std::uint8_t * pickle( - std::uint8_t * pos, - const _olm_ed25519_public_key & value -); - - -std::uint8_t const * unpickle( - std::uint8_t const * pos, std::uint8_t const * end, - _olm_ed25519_public_key & value -); - - -std::size_t pickle_length( - const _olm_ed25519_key_pair & value -); - - -std::uint8_t * pickle( - std::uint8_t * pos, - const _olm_ed25519_key_pair & value -); - - -std::uint8_t const * unpickle( - std::uint8_t const * pos, std::uint8_t const * end, - _olm_ed25519_key_pair & value -); - } // namespace olm diff --git a/src/account.cpp b/src/account.cpp index c77f95c..248f3d1 100644 --- a/src/account.cpp +++ b/src/account.cpp @@ -14,6 +14,7 @@ */ #include "olm/account.hh" #include "olm/base64.hh" +#include "olm/pickle.h" #include "olm/pickle.hh" #include "olm/memory.hh" @@ -265,7 +266,7 @@ static std::size_t pickle_length( olm::IdentityKeys const & value ) { size_t length = 0; - length += olm::pickle_length(value.ed25519_key); + length += _olm_pickle_ed25519_key_pair_length(&value.ed25519_key); length += olm::pickle_length(value.curve25519_key); return length; } @@ -275,7 +276,7 @@ static std::uint8_t * pickle( std::uint8_t * pos, olm::IdentityKeys const & value ) { - pos = olm::pickle(pos, value.ed25519_key); + pos = _olm_pickle_ed25519_key_pair(pos, &value.ed25519_key); pos = olm::pickle(pos, value.curve25519_key); return pos; } @@ -285,7 +286,7 @@ static std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, olm::IdentityKeys & value ) { - pos = olm::unpickle(pos, end, value.ed25519_key); + pos = _olm_unpickle_ed25519_key_pair(pos, end, &value.ed25519_key); pos = olm::unpickle(pos, end, value.curve25519_key); return pos; } diff --git a/src/pickle.cpp b/src/pickle.cpp index e9708b7..b6ee4c5 100644 --- a/src/pickle.cpp +++ b/src/pickle.cpp @@ -139,77 +139,76 @@ std::uint8_t const * olm::unpickle( return pos; } -std::size_t olm::pickle_length( - const _olm_ed25519_public_key & value -) { - return sizeof(value.public_key); -} - - -std::uint8_t * olm::pickle( - 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( - 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_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( - 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( - 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; -} - ////// 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) { return olm::pickle(pos, value); } From 099757dc8fec79afea56ca0e86faa96911fdb4b4 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 5 Sep 2016 16:35:22 +0100 Subject: [PATCH 18/36] Build the JS wrappers on jenkins --- jenkins.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/jenkins.sh b/jenkins.sh index c1efcb9..7cd96f2 100755 --- a/jenkins.sh +++ b/jenkins.sh @@ -5,5 +5,7 @@ set -e make clean make lib make test - ./python/test_olm.sh + +. ~/.emsdk_set_env.sh +make js From e7996e5984f208dd29532450687a38533ae50182 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 5 Sep 2016 18:40:37 +0100 Subject: [PATCH 19/36] pack the js after building --- jenkins.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/jenkins.sh b/jenkins.sh index 7cd96f2..9fa4324 100755 --- a/jenkins.sh +++ b/jenkins.sh @@ -9,3 +9,4 @@ make test . ~/.emsdk_set_env.sh make js +npm pack javascript From 617f9b1696015d42e8b31067d520ce76fd3c6425 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 5 Sep 2016 18:42:18 +0100 Subject: [PATCH 20/36] remove js package before building --- jenkins.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jenkins.sh b/jenkins.sh index 9fa4324..5c6eff7 100755 --- a/jenkins.sh +++ b/jenkins.sh @@ -3,6 +3,8 @@ set -e make clean +rm olm-*.tgz + make lib make test ./python/test_olm.sh From c2b51207ee9d6546a885746475e14141a29bcb36 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 5 Sep 2016 19:42:04 +0100 Subject: [PATCH 21/36] Fix error handling for group sessions Fix a couple of places where we were using the wrong method to get the last error. --- javascript/olm_inbound_group_session.js | 4 ++-- javascript/olm_outbound_group_session.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/javascript/olm_inbound_group_session.js b/javascript/olm_inbound_group_session.js index aac2c70..9722c31 100644 --- a/javascript/olm_inbound_group_session.js +++ b/javascript/olm_inbound_group_session.js @@ -66,14 +66,14 @@ InboundGroupSession.prototype['decrypt'] = restore_stack(function( ) { var message_array = array_from_string(message); 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'] )(this.ptr, message_buffer, message_array.length); // caculating the length destroys the input buffer. // So we copy the array to a new buffer var message_buffer = stack(message_array); 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, message_buffer, message_array.length, plaintext_buffer, max_plaintext_length diff --git a/javascript/olm_outbound_group_session.js b/javascript/olm_outbound_group_session.js index e59c8bb..71e1c91 100644 --- a/javascript/olm_outbound_group_session.js +++ b/javascript/olm_outbound_group_session.js @@ -54,7 +54,7 @@ OutboundGroupSession.prototype['unpickle'] = restore_stack(function(key, pickle) }); OutboundGroupSession.prototype['create'] = restore_stack(function(key) { - var random_length = session_method( + var random_length = outbound_group_session_method( Module['_olm_init_outbound_group_session_random_length'] )(this.ptr); var random = random_stack(random_length); From 50cd2b2a430b379bf6cee1259867faa08daea1b7 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 6 Sep 2016 14:06:43 +0100 Subject: [PATCH 22/36] Clean up some typos Remove redundant args from some js funcs, and fix a comment typo --- include/olm/outbound_group_session.h | 2 +- javascript/olm_outbound_group_session.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/olm/outbound_group_session.h b/include/olm/outbound_group_session.h index 90859e9..90ccca3 100644 --- a/include/olm/outbound_group_session.h +++ b/include/olm/outbound_group_session.h @@ -160,7 +160,7 @@ size_t olm_outbound_group_session_key_length( /** * 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. * * Returns the length of the ratchet key on success or olm_error() on diff --git a/javascript/olm_outbound_group_session.js b/javascript/olm_outbound_group_session.js index 71e1c91..88a441d 100644 --- a/javascript/olm_outbound_group_session.js +++ b/javascript/olm_outbound_group_session.js @@ -53,7 +53,7 @@ OutboundGroupSession.prototype['unpickle'] = restore_stack(function(key, pickle) ); }); -OutboundGroupSession.prototype['create'] = restore_stack(function(key) { +OutboundGroupSession.prototype['create'] = restore_stack(function() { var random_length = outbound_group_session_method( Module['_olm_init_outbound_group_session_random_length'] )(this.ptr); @@ -78,7 +78,7 @@ OutboundGroupSession.prototype['encrypt'] = restore_stack(function(plaintext) { 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( Module['_olm_outbound_group_session_id_length'] )(this.ptr); @@ -89,7 +89,7 @@ OutboundGroupSession.prototype['session_id'] = restore_stack(function(key) { 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( Module['_olm_outbound_group_session_key_length'] )(this.ptr); From 2fc83aa9aca1ce84b3c425a670cdf6a3a8886b34 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 5 Sep 2016 19:49:36 +0100 Subject: [PATCH 23/36] Sign megolm messages Add ed25519 keys to the inbound and outbound sessions, and use them to sign and verify megolm messages. We just stuff the ed25519 public key in alongside the megolm session key (and add a version byte), to save adding more boilerplate to the JS/python/etc layers. --- include/olm/error.h | 5 ++ include/olm/inbound_group_session.h | 2 +- include/olm/message.h | 8 ++-- src/error.c | 1 + src/inbound_group_session.c | 69 +++++++++++++++++++++++---- src/message.cpp | 12 +++-- src/outbound_group_session.c | 52 +++++++++++++++++--- tests/test_group_session.cpp | 74 +++++++++++++++++++++++++++-- tests/test_message.cpp | 9 ++-- 9 files changed, 201 insertions(+), 31 deletions(-) diff --git a/include/olm/error.h b/include/olm/error.h index 1c44de8..9d44a94 100644 --- a/include/olm/error.h +++ b/include/olm/error.h @@ -46,6 +46,11 @@ enum OlmErrorCode { */ 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 * this list. */ }; diff --git a/include/olm/inbound_group_session.h b/include/olm/inbound_group_session.h index e24f377..49992b2 100644 --- a/include/olm/inbound_group_session.h +++ b/include/olm/inbound_group_session.h @@ -97,7 +97,7 @@ size_t olm_init_inbound_group_session( OlmInboundGroupSession *session, uint32_t message_index, - /* base64-encoded key */ + /* base64-encoded keys */ uint8_t const * session_key, size_t session_key_length ); diff --git a/include/olm/message.h b/include/olm/message.h index 5eb504d..61012c9 100644 --- a/include/olm/message.h +++ b/include/olm/message.h @@ -37,7 +37,8 @@ extern "C" { size_t _olm_encode_group_message_length( uint32_t chain_index, 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 * olm_encode_group_message_length() bytes long. * 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. */ @@ -76,7 +78,7 @@ struct _OlmDecodeGroupMessageResults { */ void _olm_decode_group_message( 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 */ struct _OlmDecodeGroupMessageResults *results diff --git a/src/error.c b/src/error.c index b742197..f541a93 100644 --- a/src/error.c +++ b/src/error.c @@ -30,6 +30,7 @@ static const char * ERRORS[] = { "BAD_SESSION_KEY", "UNKNOWN_MESSAGE_INDEX", "BAD_LEGACY_ACCOUNT_PICKLE", + "BAD_SIGNATURE", }; const char * _olm_error_to_string(enum OlmErrorCode error) diff --git a/src/inbound_group_session.c b/src/inbound_group_session.c index ce26033..11e3dbe 100644 --- a/src/inbound_group_session.c +++ b/src/inbound_group_session.c @@ -19,6 +19,7 @@ #include "olm/base64.h" #include "olm/cipher.h" +#include "olm/crypto.h" #include "olm/error.h" #include "olm/megolm.h" #include "olm/memory.h" @@ -29,6 +30,7 @@ #define OLM_PROTOCOL_VERSION 3 #define PICKLE_VERSION 1 +#define SESSION_KEY_VERSION 1 struct OlmInboundGroupSession { /** our earliest known ratchet value */ @@ -37,6 +39,9 @@ struct OlmInboundGroupSession { /** The most recent ratchet value */ Megolm latest_ratchet; + /** The ed25519 signing key */ + struct _olm_ed25519_public_key signing_key; + enum OlmErrorCode last_error; }; @@ -65,30 +70,56 @@ size_t olm_clear_inbound_group_session( return sizeof(OlmInboundGroupSession); } +#define SESSION_KEY_RAW_LENGTH \ + (1 + MEGOLM_RATCHET_LENGTH + ED25519_PUBLIC_KEY_LENGTH) + +/** init the session keys from the un-base64-ed session keys */ +static size_t _init_group_session_keys( + OlmInboundGroupSession *session, + uint32_t message_index, + 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; + } + + megolm_init(&session->initial_ratchet, ptr, message_index); + megolm_init(&session->latest_ratchet, ptr, message_index); + ptr += MEGOLM_RATCHET_LENGTH; + memcpy( + session->signing_key.public_key, ptr, ED25519_PUBLIC_KEY_LENGTH + ); + ptr += ED25519_PUBLIC_KEY_LENGTH; + return 0; +} + size_t olm_init_inbound_group_session( OlmInboundGroupSession *session, uint32_t message_index, 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 result; if (raw_length == (size_t)-1) { session->last_error = OLM_INVALID_BASE64; return (size_t)-1; } - if (raw_length != MEGOLM_RATCHET_LENGTH) { + if (raw_length != SESSION_KEY_RAW_LENGTH) { session->last_error = OLM_BAD_SESSION_KEY; return (size_t)-1; } _olm_decode_base64(session_key, session_key_length, key_buf); - megolm_init(&session->initial_ratchet, key_buf, message_index); - megolm_init(&session->latest_ratchet, key_buf, message_index); - _olm_unset(key_buf, MEGOLM_RATCHET_LENGTH); - - return 0; + result = _init_group_session_keys(session, message_index, key_buf); + _olm_unset(key_buf, SESSION_KEY_RAW_LENGTH); + return result; } static size_t raw_pickle_length( @@ -98,6 +129,7 @@ static size_t raw_pickle_length( length += _olm_pickle_uint32_length(PICKLE_VERSION); length += megolm_pickle_length(&session->initial_ratchet); length += megolm_pickle_length(&session->latest_ratchet); + length += _olm_pickle_ed25519_public_key_length(&session->signing_key); return length; } @@ -124,6 +156,7 @@ size_t olm_pickle_inbound_group_session( pos = _olm_pickle_uint32(pos, PICKLE_VERSION); pos = megolm_pickle(&session->initial_ratchet, pos); pos = megolm_pickle(&session->latest_ratchet, pos); + pos = _olm_pickle_ed25519_public_key(pos, &session->signing_key); return _olm_enc_output(key, key_length, pickled, raw_length); } @@ -153,6 +186,7 @@ size_t olm_unpickle_inbound_group_session( } pos = megolm_unpickle(&session->initial_ratchet, pos, end); pos = megolm_unpickle(&session->latest_ratchet, pos, end); + pos = _olm_unpickle_ed25519_public_key(pos, end, &session->signing_key); if (end != pos) { /* We had the wrong number of bytes in the input. */ @@ -175,6 +209,7 @@ static size_t _decrypt_max_plaintext_length( _olm_decode_group_message( message, message_length, megolm_cipher->ops->mac_length(megolm_cipher), + ED25519_SIGNATURE_LENGTH, &decoded_results); if (decoded_results.version != OLM_PROTOCOL_VERSION) { @@ -224,6 +259,7 @@ static size_t _decrypt( _olm_decode_group_message( message, message_length, megolm_cipher->ops->mac_length(megolm_cipher), + ED25519_SIGNATURE_LENGTH, &decoded_results); if (decoded_results.version != OLM_PROTOCOL_VERSION) { @@ -231,11 +267,28 @@ static size_t _decrypt( 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; return (size_t)-1; } + /* verify the signature. We could do this before decoding the message, but + * we allow for the possibility of future protocol versions which use a + * different signing mechanism; we would rather throw "BAD_MESSAGE_VERSION" + * than "BAD_SIGNATURE" in this case. + */ + message_length -= ED25519_SIGNATURE_LENGTH; + r = _olm_crypto_ed25519_verify( + &session->signing_key, + message, message_length, + message + message_length + ); + if (!r) { + session->last_error = OLM_BAD_SIGNATURE; + return (size_t)-1; + } + + max_length = megolm_cipher->ops->decrypt_max_plaintext_length( megolm_cipher, decoded_results.ciphertext_length diff --git a/src/message.cpp b/src/message.cpp index ad26cb9..05fe2c7 100644 --- a/src/message.cpp +++ b/src/message.cpp @@ -334,12 +334,14 @@ static const std::uint8_t GROUP_CIPHERTEXT_TAG = 022; size_t _olm_encode_group_message_length( uint32_t message_index, size_t ciphertext_length, - size_t mac_length + size_t mac_length, + size_t signature_length ) { size_t length = VERSION_LENGTH; length += 1 + varint_length(message_index); length += 1 + varstring_length(ciphertext_length); length += mac_length; + length += signature_length; return length; } @@ -361,11 +363,12 @@ size_t _olm_encode_group_message( void _olm_decode_group_message( const uint8_t *input, size_t input_length, - size_t mac_length, + size_t mac_length, size_t signature_length, struct _OlmDecodeGroupMessageResults *results ) { 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; bool has_message_index = false; @@ -373,8 +376,7 @@ void _olm_decode_group_message( results->ciphertext = nullptr; results->ciphertext_length = 0; - if (pos == end) return; - if (input_length < mac_length) return; + if (input_length < trailer_length) return; results->version = *(pos++); while (pos != end) { diff --git a/src/outbound_group_session.c b/src/outbound_group_session.c index 2a6c220..a605fa6 100644 --- a/src/outbound_group_session.c +++ b/src/outbound_group_session.c @@ -20,6 +20,7 @@ #include "olm/base64.h" #include "olm/cipher.h" +#include "olm/crypto.h" #include "olm/error.h" #include "olm/megolm.h" #include "olm/memory.h" @@ -31,11 +32,15 @@ #define SESSION_ID_RANDOM_BYTES 4 #define GROUP_SESSION_ID_LENGTH (sizeof(struct timeval) + SESSION_ID_RANDOM_BYTES) #define PICKLE_VERSION 1 +#define SESSION_KEY_VERSION 1 struct OlmOutboundGroupSession { /** the Megolm ratchet providing the encryption keys */ Megolm ratchet; + /** The ed25519 keypair used for signing the messages */ + struct _olm_ed25519_key_pair signing_key; + /** unique identifier for this session */ uint8_t session_id[GROUP_SESSION_ID_LENGTH]; @@ -74,6 +79,7 @@ static size_t raw_pickle_length( size_t length = 0; length += _olm_pickle_uint32_length(PICKLE_VERSION); length += megolm_pickle_length(&(session->ratchet)); + length += _olm_pickle_ed25519_key_pair_length(&(session->signing_key)); length += _olm_pickle_bytes_length(session->session_id, GROUP_SESSION_ID_LENGTH); return length; @@ -101,6 +107,7 @@ size_t olm_pickle_outbound_group_session( pos = _olm_enc_output_pos(pickled, raw_length); pos = _olm_pickle_uint32(pos, PICKLE_VERSION); pos = megolm_pickle(&(session->ratchet), pos); + pos = _olm_pickle_ed25519_key_pair(pos, &(session->signing_key)); pos = _olm_pickle_bytes(pos, session->session_id, GROUP_SESSION_ID_LENGTH); return _olm_enc_output(key, key_length, pickled, raw_length); @@ -130,6 +137,7 @@ size_t olm_unpickle_outbound_group_session( return (size_t)-1; } pos = megolm_unpickle(&(session->ratchet), pos, end); + pos = _olm_unpickle_ed25519_key_pair(pos, end, &(session->signing_key)); pos = _olm_unpickle_bytes(pos, end, session->session_id, GROUP_SESSION_ID_LENGTH); if (end != pos) { @@ -148,7 +156,9 @@ size_t olm_init_outbound_group_session_random_length( /* we need data to initialize the megolm ratchet, plus some more for the * session id. */ - return MEGOLM_RATCHET_LENGTH + SESSION_ID_RANDOM_BYTES; + return MEGOLM_RATCHET_LENGTH + + ED25519_RANDOM_LENGTH + + SESSION_ID_RANDOM_BYTES; } size_t olm_init_outbound_group_session( @@ -164,6 +174,9 @@ size_t olm_init_outbound_group_session( megolm_init(&(session->ratchet), random, 0); random += MEGOLM_RATCHET_LENGTH; + _olm_crypto_ed25519_generate_key(random, &(session->signing_key)); + random += ED25519_RANDOM_LENGTH; + /* initialise the session id. This just has to be unique. We use the * current time plus some random data. */ @@ -188,7 +201,8 @@ static size_t raw_message_length( return _olm_encode_group_message_length( session->ratchet.counter, - ciphertext_length, mac_length); + ciphertext_length, mac_length, ED25519_SIGNATURE_LENGTH + ); } size_t olm_group_encrypt_message_length( @@ -241,6 +255,13 @@ static size_t _encrypt( 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; } @@ -302,23 +323,40 @@ uint32_t olm_outbound_group_session_message_index( return session->ratchet.counter; } +#define SESSION_KEY_RAW_LENGTH \ + (1 + MEGOLM_RATCHET_LENGTH + ED25519_PUBLIC_KEY_LENGTH) + size_t olm_outbound_group_session_key_length( 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( OlmOutboundGroupSession *session, 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; return (size_t)-1; } - return _olm_encode_base64( - megolm_get_data(&session->ratchet), - MEGOLM_RATCHET_LENGTH, key + /* put the raw data at the end of the output buffer. */ + raw = ptr = key + encoded_length - SESSION_KEY_RAW_LENGTH; + *ptr++ = SESSION_KEY_VERSION; + + 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; + + return _olm_encode_base64(raw, SESSION_KEY_RAW_LENGTH, key); } diff --git a/tests/test_group_session.cpp b/tests/test_group_session.cpp index 4a82154..7ac91c3 100644 --- a/tests/test_group_session.cpp +++ b/tests/test_group_session.cpp @@ -80,7 +80,6 @@ int main() { assert_equals(pickle1, pickle2, pickle_length); } - { TestCase test_case("Group message send/receive"); @@ -89,6 +88,7 @@ int main() { "0123456789ABDEF0123456789ABCDEF" "0123456789ABDEF0123456789ABCDEF" "0123456789ABDEF0123456789ABCDEF" + "0123456789ABDEF0123456789ABCDEF" "0123456789ABDEF0123456789ABCDEF"; @@ -97,7 +97,7 @@ int main() { uint8_t memory[size]; OlmOutboundGroupSession *session = olm_outbound_group_session(memory); - assert_equals((size_t)132, + assert_equals((size_t)164, olm_init_outbound_group_session_random_length(session)); size_t res = olm_init_outbound_group_session( @@ -109,7 +109,6 @@ int main() { uint8_t session_key[session_key_len]; olm_outbound_group_session_key(session, session_key, session_key_len); - /* encode the message */ uint8_t plaintext[] = "Message"; size_t plaintext_length = sizeof(plaintext) - 1; @@ -148,4 +147,73 @@ int main() { 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[] = + "ATAxMjM0NTY3ODlBQkRFRjAxMjM0NTY3ODlBQkNERUYwMTIzNDU2Nzg5QUJERUYw" + "MTIzNDU2Nzg5QUJDREVGMDEyMzQ1Njc4OUFCREVGMDEyMzQ1Njc4OUFCQ0RFRjAx" + "MjM0NTY3ODlBQkRFRjAxMjM0NTY3ODlBQkNERUYwMTIzDRt2DUEOrg/H+yUGjDTq" + "ryf8H1YF/BZjI04HwOVSZcY"; + + uint8_t message[] = + "AwgAEhAcbh6UpbByoyZxufQ+h2B+8XHMjhR69G8F4+qjMaFlnIXusJZX3r8LnROR" + "G9T3DXFdbVuvIWrLyRfm4i8QRbe8VPwGRFG57B1CtmxanuP8bHtnnYqlwPsD"; + 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, 0U, 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)) + ); +} + + } diff --git a/tests/test_message.cpp b/tests/test_message.cpp index 06b36dc..25693f5 100644 --- a/tests/test_message.cpp +++ b/tests/test_message.cpp @@ -67,8 +67,8 @@ assert_equals(message2, output, 35); TestCase test_case("Group message encode test"); - size_t length = _olm_encode_group_message_length(200, 10, 8); - size_t expected_length = 1 + (1+2) + (2+10) + 8; + size_t length = _olm_encode_group_message_length(200, 10, 8, 64); + size_t expected_length = 1 + (1+2) + (2+10) + 8 + 64; assert_equals(expected_length, length); uint8_t output[50]; @@ -99,9 +99,10 @@ assert_equals(message2, output, 35); "\x03" "\x08\xC8\x01" "\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(1, results.has_message_index); assert_equals(std::uint32_t(200), results.message_index); From b5c65bed0a5bd7c9a7345d4750ba90e7eb6f770b Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 6 Sep 2016 17:04:37 +0100 Subject: [PATCH 24/36] Prepare changelog for v1.2.0 --- CHANGELOG.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 96296e5..5b473f8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,25 @@ +Changes in `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 `_ =============================================================== From 49ca6aca98faa90732b9fb47d614209f74f3642d Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 6 Sep 2016 22:26:24 +0100 Subject: [PATCH 25/36] Bump version numbers to 1.2.0 --- Makefile | 2 +- javascript/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index ad97c5d..2299eeb 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ #!/usr/bin/make -f MAJOR := 1 -MINOR := 1 +MINOR := 2 PATCH := 0 VERSION := $(MAJOR).$(MINOR).$(PATCH) PREFIX ?= /usr/local diff --git a/javascript/package.json b/javascript/package.json index 22232ac..d896e19 100644 --- a/javascript/package.json +++ b/javascript/package.json @@ -1,6 +1,6 @@ { "name": "olm", - "version": "1.1.0", + "version": "1.2.0", "description": "An implementation of a well known cryptographic ratchet", "main": "olm.js", "files": [ From 576c6ad29292e171a88303e069b4a4a9680cb3bd Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 6 Sep 2016 22:46:58 +0100 Subject: [PATCH 26/36] call double ratchet what it is --- javascript/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/package.json b/javascript/package.json index d896e19..0624409 100644 --- a/javascript/package.json +++ b/javascript/package.json @@ -1,7 +1,7 @@ { "name": "olm", "version": "1.2.0", - "description": "An implementation of a well known cryptographic ratchet", + "description": "An implementation of the Double Ratchet cryptographic ratchet", "main": "olm.js", "files": [ "olm.js", From d62e344db708672dee58238be330382b2c903b5b Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Tue, 13 Sep 2016 15:42:47 +0100 Subject: [PATCH 27/36] Use the ed22519 public key as the group session id. Some clients expect the session id to be globally unique, so allowing the end devices to pick the session id will cause problems. Include the current ratchet index with the initial keys, this decreases the risk that the client will supply the wrong index causing problems. Sign the initial keys with the ratchet ed25519 key, this reduces the risk of a client claiming a session that they didn't create. --- src/inbound_group_session.c | 27 ++++++++++++++++++------ src/outbound_group_session.c | 41 ++++++++++++++++++------------------ tests/test_group_session.cpp | 15 +++++++------ 3 files changed, 48 insertions(+), 35 deletions(-) diff --git a/src/inbound_group_session.c b/src/inbound_group_session.c index 11e3dbe..f2a310a 100644 --- a/src/inbound_group_session.c +++ b/src/inbound_group_session.c @@ -30,7 +30,7 @@ #define OLM_PROTOCOL_VERSION 3 #define PICKLE_VERSION 1 -#define SESSION_KEY_VERSION 1 +#define SESSION_KEY_VERSION 2 struct OlmInboundGroupSession { /** our earliest known ratchet value */ @@ -71,12 +71,12 @@ size_t olm_clear_inbound_group_session( } #define SESSION_KEY_RAW_LENGTH \ - (1 + MEGOLM_RATCHET_LENGTH + ED25519_PUBLIC_KEY_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, - uint32_t message_index, const uint8_t *key_buf ) { const uint8_t *ptr = key_buf; @@ -87,13 +87,27 @@ static size_t _init_group_session_keys( return (size_t)-1; } - megolm_init(&session->initial_ratchet, ptr, message_index); - megolm_init(&session->latest_ratchet, ptr, message_index); + uint32_t counter = 0; + counter <<= 8; counter |= *ptr++; + counter <<= 8; counter |= *ptr++; + counter <<= 8; counter |= *ptr++; + 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; } @@ -117,7 +131,7 @@ size_t olm_init_inbound_group_session( } _olm_decode_base64(session_key, session_key_length, key_buf); - result = _init_group_session_keys(session, message_index, key_buf); + result = _init_group_session_keys(session, key_buf); _olm_unset(key_buf, SESSION_KEY_RAW_LENGTH); return result; } @@ -288,7 +302,6 @@ static size_t _decrypt( return (size_t)-1; } - max_length = megolm_cipher->ops->decrypt_max_plaintext_length( megolm_cipher, decoded_results.ciphertext_length diff --git a/src/outbound_group_session.c b/src/outbound_group_session.c index a605fa6..7116547 100644 --- a/src/outbound_group_session.c +++ b/src/outbound_group_session.c @@ -29,10 +29,9 @@ #include "olm/pickle_encoding.h" #define OLM_PROTOCOL_VERSION 3 -#define SESSION_ID_RANDOM_BYTES 4 -#define GROUP_SESSION_ID_LENGTH (sizeof(struct timeval) + SESSION_ID_RANDOM_BYTES) +#define GROUP_SESSION_ID_LENGTH ED25519_PUBLIC_KEY_LENGTH #define PICKLE_VERSION 1 -#define SESSION_KEY_VERSION 1 +#define SESSION_KEY_VERSION 2 struct OlmOutboundGroupSession { /** the Megolm ratchet providing the encryption keys */ @@ -41,9 +40,6 @@ struct OlmOutboundGroupSession { /** The ed25519 keypair used for signing the messages */ struct _olm_ed25519_key_pair signing_key; - /** unique identifier for this session */ - uint8_t session_id[GROUP_SESSION_ID_LENGTH]; - enum OlmErrorCode last_error; }; @@ -80,8 +76,6 @@ static size_t raw_pickle_length( length += _olm_pickle_uint32_length(PICKLE_VERSION); length += megolm_pickle_length(&(session->ratchet)); length += _olm_pickle_ed25519_key_pair_length(&(session->signing_key)); - length += _olm_pickle_bytes_length(session->session_id, - GROUP_SESSION_ID_LENGTH); return length; } @@ -108,7 +102,6 @@ size_t olm_pickle_outbound_group_session( pos = _olm_pickle_uint32(pos, PICKLE_VERSION); pos = megolm_pickle(&(session->ratchet), pos); pos = _olm_pickle_ed25519_key_pair(pos, &(session->signing_key)); - pos = _olm_pickle_bytes(pos, session->session_id, GROUP_SESSION_ID_LENGTH); return _olm_enc_output(key, key_length, pickled, raw_length); } @@ -138,7 +131,6 @@ size_t olm_unpickle_outbound_group_session( } pos = megolm_unpickle(&(session->ratchet), pos, end); pos = _olm_unpickle_ed25519_key_pair(pos, end, &(session->signing_key)); - pos = _olm_unpickle_bytes(pos, end, session->session_id, GROUP_SESSION_ID_LENGTH); if (end != pos) { /* We had the wrong number of bytes in the input. */ @@ -157,8 +149,7 @@ size_t olm_init_outbound_group_session_random_length( * session id. */ return MEGOLM_RATCHET_LENGTH + - ED25519_RANDOM_LENGTH + - SESSION_ID_RANDOM_BYTES; + ED25519_RANDOM_LENGTH; } size_t olm_init_outbound_group_session( @@ -177,13 +168,6 @@ size_t olm_init_outbound_group_session( _olm_crypto_ed25519_generate_key(random, &(session->signing_key)); random += ED25519_RANDOM_LENGTH; - /* initialise the session id. This just has to be unique. We use the - * current time plus some random data. - */ - gettimeofday((struct timeval *)(session->session_id), NULL); - memcpy((session->session_id) + sizeof(struct timeval), - random, SESSION_ID_RANDOM_BYTES); - return 0; } @@ -314,7 +298,9 @@ size_t olm_outbound_group_session_id( 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( @@ -324,7 +310,8 @@ uint32_t olm_outbound_group_session_message_index( } #define SESSION_KEY_RAW_LENGTH \ - (1 + MEGOLM_RATCHET_LENGTH + ED25519_PUBLIC_KEY_LENGTH) + (1 + 4 + MEGOLM_RATCHET_LENGTH + ED25519_PUBLIC_KEY_LENGTH\ + + ED25519_SIGNATURE_LENGTH) size_t olm_outbound_group_session_key_length( const OlmOutboundGroupSession *session @@ -349,6 +336,12 @@ size_t olm_outbound_group_session_key( raw = ptr = key + encoded_length - SESSION_KEY_RAW_LENGTH; *ptr++ = SESSION_KEY_VERSION; + uint32_t counter = session->ratchet.counter; + *ptr++ = 0xFF & (counter >> 24); counter <<= 8; + *ptr++ = 0xFF & (counter >> 24); counter <<= 8; + *ptr++ = 0xFF & (counter >> 24); counter <<= 8; + *ptr++ = 0xFF & (counter >> 24); counter <<= 8; + memcpy(ptr, megolm_get_data(&session->ratchet), MEGOLM_RATCHET_LENGTH); ptr += MEGOLM_RATCHET_LENGTH; @@ -358,5 +351,11 @@ size_t olm_outbound_group_session_key( ); ptr += ED25519_PUBLIC_KEY_LENGTH; + /* sign the whole thing with the ed25519 key. */ + _olm_crypto_ed25519_sign( + &(session->signing_key), + raw, ptr - raw, ptr + ); + return _olm_encode_base64(raw, SESSION_KEY_RAW_LENGTH, key); } diff --git a/tests/test_group_session.cpp b/tests/test_group_session.cpp index 7ac91c3..ce889ad 100644 --- a/tests/test_group_session.cpp +++ b/tests/test_group_session.cpp @@ -97,7 +97,7 @@ int main() { uint8_t memory[size]; OlmOutboundGroupSession *session = olm_outbound_group_session(memory); - assert_equals((size_t)164, + assert_equals((size_t)160, olm_init_outbound_group_session_random_length(session)); size_t res = olm_init_outbound_group_session( @@ -154,14 +154,15 @@ int main() { size_t plaintext_length = sizeof(plaintext) - 1; uint8_t session_key[] = - "ATAxMjM0NTY3ODlBQkRFRjAxMjM0NTY3ODlBQkNERUYwMTIzNDU2Nzg5QUJERUYw" - "MTIzNDU2Nzg5QUJDREVGMDEyMzQ1Njc4OUFCREVGMDEyMzQ1Njc4OUFCQ0RFRjAx" - "MjM0NTY3ODlBQkRFRjAxMjM0NTY3ODlBQkNERUYwMTIzDRt2DUEOrg/H+yUGjDTq" - "ryf8H1YF/BZjI04HwOVSZcY"; + "AgAAAAAwMTIzNDU2Nzg5QUJERUYwMTIzNDU2Nzg5QUJDREVGMDEyMzQ1Njc4OUFCREVGM" + "DEyMzQ1Njc4OUFCQ0RFRjAxMjM0NTY3ODlBQkRFRjAxMjM0NTY3ODlBQkNERUYwMTIzND" + "U2Nzg5QUJERUYwMTIzNDU2Nzg5QUJDREVGMDEyMztqJ7zOtqQtYqOo0CpvDXNlMhV3HeJ" + "DpjrASKGLWdop4lx1cSN3Xv1TgfLPW8rhGiW+hHiMxd36nRuxscNv9k4oJA/KP+o0mi1w" + "v44StrEJ1wwx9WZHBUIWkQbaBSuBDw"; uint8_t message[] = - "AwgAEhAcbh6UpbByoyZxufQ+h2B+8XHMjhR69G8F4+qjMaFlnIXusJZX3r8LnROR" - "G9T3DXFdbVuvIWrLyRfm4i8QRbe8VPwGRFG57B1CtmxanuP8bHtnnYqlwPsD"; + "AwgAEhAcbh6UpbByoyZxufQ+h2B+8XHMjhR69G8nP4pNZGl/3QMgrzCZPmP+F2aPLyKPz" + "xRPBMUkeXRJ6Iqm5NeOdx2eERgTW7P20CM+lL3Xpk+ZUOOPvsSQNaAL"; size_t msglen = sizeof(message)-1; /* build the inbound session */ From 5926a8fd29ecb997e6c4609e2195e68274d1f9df Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Tue, 13 Sep 2016 16:45:54 +0100 Subject: [PATCH 28/36] Comment on the encoding of the message counter. --- src/inbound_group_session.c | 8 ++++---- src/outbound_group_session.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/inbound_group_session.c b/src/inbound_group_session.c index f2a310a..82ff66f 100644 --- a/src/inbound_group_session.c +++ b/src/inbound_group_session.c @@ -88,10 +88,10 @@ static size_t _init_group_session_keys( } uint32_t counter = 0; - counter <<= 8; counter |= *ptr++; - counter <<= 8; counter |= *ptr++; - counter <<= 8; counter |= *ptr++; - counter <<= 8; counter |= *ptr++; + // 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); diff --git a/src/outbound_group_session.c b/src/outbound_group_session.c index 7116547..8686993 100644 --- a/src/outbound_group_session.c +++ b/src/outbound_group_session.c @@ -337,10 +337,10 @@ size_t olm_outbound_group_session_key( *ptr++ = SESSION_KEY_VERSION; uint32_t counter = session->ratchet.counter; - *ptr++ = 0xFF & (counter >> 24); counter <<= 8; - *ptr++ = 0xFF & (counter >> 24); counter <<= 8; - *ptr++ = 0xFF & (counter >> 24); counter <<= 8; - *ptr++ = 0xFF & (counter >> 24); counter <<= 8; + // Encode counter as a big endian 32-bit number. + if (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; From a89a169c8991c0b22655722b20cb4602dd7ba911 Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Tue, 13 Sep 2016 17:00:40 +0100 Subject: [PATCH 29/36] ``if`` is not the same as ``for`` --- src/outbound_group_session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/outbound_group_session.c b/src/outbound_group_session.c index 8686993..4e4561a 100644 --- a/src/outbound_group_session.c +++ b/src/outbound_group_session.c @@ -338,7 +338,7 @@ size_t olm_outbound_group_session_key( uint32_t counter = session->ratchet.counter; // Encode counter as a big endian 32-bit number. - if (unsigned i = 0; i < 4; i++) { + for (unsigned i = 0; i < 4; i++) { *ptr++ = 0xFF & (counter >> 24); counter <<= 8; } From 6971f54feac2464b53f2b5db8466d75075e0dc9e Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Tue, 13 Sep 2016 16:39:43 +0100 Subject: [PATCH 30/36] Add a olm_inbound_group_session_id method --- include/olm/inbound_group_session.h | 21 +++++++++++++++++++++ javascript/olm_inbound_group_session.js | 11 +++++++++++ python/olm/inbound_group_session.py | 9 +++++++++ src/inbound_group_session.c | 21 +++++++++++++++++++++ 4 files changed, 62 insertions(+) diff --git a/include/olm/inbound_group_session.h b/include/olm/inbound_group_session.h index 49992b2..0c5eb26 100644 --- a/include/olm/inbound_group_session.h +++ b/include/olm/inbound_group_session.h @@ -146,6 +146,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 } // extern "C" #endif diff --git a/javascript/olm_inbound_group_session.js b/javascript/olm_inbound_group_session.js index 9722c31..01bfd26 100644 --- a/javascript/olm_inbound_group_session.js +++ b/javascript/olm_inbound_group_session.js @@ -89,4 +89,15 @@ InboundGroupSession.prototype['decrypt'] = restore_stack(function( 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; diff --git a/python/olm/inbound_group_session.py b/python/olm/inbound_group_session.py index 6c01095..310620f 100644 --- a/python/olm/inbound_group_session.py +++ b/python/olm/inbound_group_session.py @@ -45,6 +45,9 @@ inbound_group_session_function( 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): def __init__(self): self.buf = create_string_buffer(lib.olm_inbound_group_session_size()) @@ -84,3 +87,9 @@ class InboundGroupSession(object): plaintext_buffer, max_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 diff --git a/src/inbound_group_session.c b/src/inbound_group_session.c index 82ff66f..65c6018 100644 --- a/src/inbound_group_session.c +++ b/src/inbound_group_session.c @@ -29,6 +29,7 @@ #define OLM_PROTOCOL_VERSION 3 +#define GROUP_SESSION_ID_LENGTH ED25519_PUBLIC_KEY_LENGTH #define PICKLE_VERSION 1 #define SESSION_KEY_VERSION 2 @@ -364,3 +365,23 @@ size_t olm_group_decrypt( 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 + ); +} From 71bcaa5d45f63fc9b217937010199030d410f305 Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Tue, 13 Sep 2016 17:15:28 +0100 Subject: [PATCH 31/36] Add a test to check the equivalence of session ids for inbound and outbound sessions --- tests/test_group_session.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/test_group_session.cpp b/tests/test_group_session.cpp index ce889ad..094c744 100644 --- a/tests/test_group_session.cpp +++ b/tests/test_group_session.cpp @@ -133,6 +133,26 @@ int main() { inbound_session, 0U, session_key, session_key_len); 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 */ /* olm_group_decrypt_max_plaintext_length destroys the input so we have to From a628ef41bd5b860b89a3e2e29df56a461e85fa00 Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Tue, 13 Sep 2016 17:41:55 +0100 Subject: [PATCH 32/36] Remove the messsage index from olm_init_inbound_group_session since it is read from the session_key --- include/olm/inbound_group_session.h | 2 -- javascript/olm_inbound_group_session.js | 4 ++-- python/olm/inbound_group_session.py | 6 +++--- src/inbound_group_session.c | 1 - tests/test_group_session.cpp | 4 ++-- 5 files changed, 7 insertions(+), 10 deletions(-) diff --git a/include/olm/inbound_group_session.h b/include/olm/inbound_group_session.h index 49992b2..4798f27 100644 --- a/include/olm/inbound_group_session.h +++ b/include/olm/inbound_group_session.h @@ -95,8 +95,6 @@ size_t olm_unpickle_inbound_group_session( */ size_t olm_init_inbound_group_session( OlmInboundGroupSession *session, - uint32_t message_index, - /* base64-encoded keys */ uint8_t const * session_key, size_t session_key_length ); diff --git a/javascript/olm_inbound_group_session.js b/javascript/olm_inbound_group_session.js index 9722c31..7e07b01 100644 --- a/javascript/olm_inbound_group_session.js +++ b/javascript/olm_inbound_group_session.js @@ -52,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_buffer = stack(key_array); 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 ); }); diff --git a/python/olm/inbound_group_session.py b/python/olm/inbound_group_session.py index 6c01095..eb0422f 100644 --- a/python/olm/inbound_group_session.py +++ b/python/olm/inbound_group_session.py @@ -33,7 +33,7 @@ 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( @@ -66,10 +66,10 @@ class InboundGroupSession(object): 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) 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): diff --git a/src/inbound_group_session.c b/src/inbound_group_session.c index 82ff66f..b42ca86 100644 --- a/src/inbound_group_session.c +++ b/src/inbound_group_session.c @@ -113,7 +113,6 @@ static size_t _init_group_session_keys( size_t olm_init_inbound_group_session( OlmInboundGroupSession *session, - uint32_t message_index, const uint8_t * session_key, size_t session_key_length ) { uint8_t key_buf[SESSION_KEY_RAW_LENGTH]; diff --git a/tests/test_group_session.cpp b/tests/test_group_session.cpp index ce889ad..3e11169 100644 --- a/tests/test_group_session.cpp +++ b/tests/test_group_session.cpp @@ -130,7 +130,7 @@ int main() { olm_inbound_group_session(inbound_session_memory); 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); /* decode the message */ @@ -172,7 +172,7 @@ int main() { olm_inbound_group_session(inbound_session_memory); size_t res = olm_init_inbound_group_session( - inbound_session, 0U, session_key, sizeof(session_key)-1 + inbound_session, session_key, sizeof(session_key)-1 ); assert_equals((size_t)0, res); From 6dfa64342ac01b66f1e84bf4ffafd0a12d3c1b6d Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Wed, 14 Sep 2016 11:10:05 +0100 Subject: [PATCH 33/36] Fix the group javascript demo --- javascript/demo/group_demo.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/javascript/demo/group_demo.js b/javascript/demo/group_demo.js index 35b22a3..1b8f7ab 100644 --- a/javascript/demo/group_demo.js +++ b/javascript/demo/group_demo.js @@ -261,7 +261,7 @@ DemoUser.prototype.receiveOneToOne = function(jsonpacket) { if (!self.peerGroupSessions[sender] || !self.peerGroupSessions[sender][body.session_id]) { 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 */ DemoUser.prototype.createInboundSession = function( - peer_id, session_id, message_index, session_key, callback + peer_id, session_id, session_key, callback ) { var self = this; this.addTask("init inbound session", function(done) { session = new Olm.InboundGroupSession(); - session.create(message_index, session_key); + session.create(session_key); if (!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; done(session); }, callback); From 7647555fc719a4dac81391e596ad5858ad7c0e1d Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Wed, 14 Sep 2016 11:48:15 +0100 Subject: [PATCH 34/36] Make release instructions more C+Pable --- README.rst | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index 1a3663a..3d4db70 100644 --- a/README.rst +++ b/README.rst @@ -32,15 +32,19 @@ To build the javascript bindings, install emscripten from http://kripken.github. Release process --------------- -# Bump version numbers in ``Makefile`` and ``javascript/package.json`` -# Prepare changelog -# ``git commit`` -# ``make test`` -# ``make js`` -# ``npm pack javascript`` -# ``scp olm-x.y.z.tgz packages@ldc-prd-matrix-001:/sites/matrix/packages/npm/olm/`` -# ``git tag x.y.z`` -# ``git push --tags`` +.. code:: bash + + # 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 + 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. From ec7d9686236cb8bc924d5fdbe1b92030afce8b4c Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Wed, 14 Sep 2016 13:55:54 +0100 Subject: [PATCH 35/36] Changelog and version bump for 1.3.0 --- CHANGELOG.rst | 16 ++++++++++++++++ Makefile | 2 +- javascript/package.json | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5b473f8..0ab2eeb 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,19 @@ +Changes in `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 `_ =============================================================== diff --git a/Makefile b/Makefile index 2299eeb..9d08c50 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ #!/usr/bin/make -f MAJOR := 1 -MINOR := 2 +MINOR := 3 PATCH := 0 VERSION := $(MAJOR).$(MINOR).$(PATCH) PREFIX ?= /usr/local diff --git a/javascript/package.json b/javascript/package.json index 0624409..df43ce1 100644 --- a/javascript/package.json +++ b/javascript/package.json @@ -1,6 +1,6 @@ { "name": "olm", - "version": "1.2.0", + "version": "1.3.0", "description": "An implementation of the Double Ratchet cryptographic ratchet", "main": "olm.js", "files": [ From 047927d822734cffe43adc3f9eae355c12c6db01 Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Wed, 14 Sep 2016 14:16:51 +0100 Subject: [PATCH 36/36] Fix the release instructions --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 3d4db70..21ae529 100644 --- a/README.rst +++ b/README.rst @@ -42,8 +42,8 @@ Release process 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 + scp olm-$VERSION.tgz packages@ldc-prd-matrix-001:/sites/matrix/packages/npm/olm/ + git tag $VERSION -s git push --tags It's probably sensible to do the above on a release branch (``release-vx.y.z``