Compare commits

...

801 Commits

Author SHA1 Message Date
Hubert Chathi 7e0c827703 release 3.2.16 2023-11-23 12:38:51 -05:00
Hubert Chathi 972faaadd5 use pypa/build instead of setup.py when building sdist 2023-11-21 21:25:55 -05:00
Hubert Chathi 807b252331 update Python versions in CI 2023-11-21 21:25:02 -05:00
parona' via Olm bbdac4045d Fix breakage in setuptools-69.0.0 by cleaning up setup.py
Hello,

Setuptools 69.0.0 deprecated a bunch stuff leading to a nasty errors during install.

> File "/tmp/pip-build-env-w815o5v3/overlay/lib/python3.11/site-packages/setuptools/config/_apply_pyprojecttoml.py", line 183, in _license
>          _set_config(dist, "license", val["text"])
>                                       ~~~^^^^^^^^
> KeyError: 'text'
> [end of output]

__version__.py wasn't used anywhere except setup.py so removing and setting it all pyproject.toml is safe.

During this decided to move as much as I could out of setup.py, zip-safe has been obsolete for modern setuptools installation methods so dropped it.

From c0be008217350f03de7f856866a402d95b5db2a3 Mon Sep 17 00:00:00 2001
From: Alfred Wingate <parona@protonmail.com>
Date: Tue, 21 Nov 2023 15:13:35 +0200
Subject: [PATCH] Move metadata to project.toml

* Setuptools 69.0.0 deprecated a slew of old style configurations.

Signed-off-by: Alfred Wingate <parona@protonmail.com>
2023-11-21 16:07:46 -05:00
Hubert Chathi 4beb2487ce JS packages are now uploaded to npmjs.com rather than gitlab.matrix.org 2023-11-21 16:06:00 -05:00
Hubert Chathi b54fa37fae add missing line from changelog 2023-11-21 16:05:27 -05:00
Hubert Chathi 66294cf7f6 release 3.2.15 2023-05-01 11:35:20 -04:00
Hubert Chathi 366520ebfd aha, it's lowercase 2023-04-27 19:53:44 -04:00
Hubert Chathi d27f162316 attempt to fix js build 2023-04-27 19:48:16 -04:00
Hubert Chathi 4b69958c95 improve compatibility with Windows (though it still doesn't work) 2023-04-27 18:52:39 -04:00
Hubert Chathi 5cfe6c3dbd more packaging improvements 2023-04-27 17:19:53 -04:00
Hubert Chathi bbdc12c569 Merge branch 'master' of https://gitlab.matrix.org/matrix-org/olm 2023-04-26 10:02:02 -04:00
Hubert Chathi afb3d403e1 actually remove dependency on future 2023-04-26 10:00:34 -04:00
Hubert Chathi 418656ee9f make sure the headers are up to date when creating the sdist 2023-04-26 09:58:10 -04:00
Hubert Chathi 0d367baa5b add script for creating Python sdist 2023-04-25 18:47:37 -04:00
Hubert Chathi 8cbb60e476 improve Python packaging 2023-04-25 18:47:14 -04:00
Michael Telatynski 0880461134 Correct `message_index` type in return signature of `InboundGroupSession::decrypt` 2023-03-29 08:33:55 +00:00
Hubert Chathi 8f4b81b512 remove workaround for closure compiler that is now causing problems
was added in 5a60e543a5
2023-03-17 17:50:10 -04:00
Jon Ringer ab4cbcd01a Enable darwin builds in Nix for olm
From 04f1249a66e75e91ef009ed04304cbc88dea798d Mon Sep 17 00:00:00 2001
From: Jonathan Ringer <jonringer117@gmail.com>
Date: Sun, 26 Feb 2023 15:14:23 -0800
Subject: [PATCH] Enable darwin builds in Nix

Move most packaging concerns into nix/overlay.nix. Alter packaging
to mostly align with nixpkgs best practices.

Signed-off-by: Jonathan Ringer <jonringer117@gmail.com>
2023-03-17 16:53:50 -04:00
Hubert Chathi 704b198f5a we are already living in the future, part 2 2023-01-19 13:46:34 -05:00
Hubert Chathi 0eb4550a8f we are already living in the future 2023-01-09 09:51:37 -05:00
Hubert Chathi 249acc9e0b fix tox config to work with newer version 2022-12-23 17:50:09 -05:00
Hubert Chathi 5efd38c990 release 3.2.14 2022-12-05 17:58:00 -05:00
Hubert Chathi ad76fc1570 allow multiple arguments to be passed when linking Python library 2022-12-02 19:38:45 -05:00
Michael Telatynski b5d68376b5 Improve Typescript typing 2022-12-01 18:36:00 +00:00
Hubert Chathi dbd8a44fa2 add documentation for installation, and other doc improvements 2022-11-28 18:39:49 -05:00
Michael Telatynski 722f4df4aa Update javascript/index.d.ts 2022-11-18 16:09:55 +00:00
Hubert Chathi 6d767aaf29 release 3.2.13 2022-10-07 11:00:05 -04:00
Hubert Chathi 21e84095e6 pkgconfig improvements 2022-10-06 22:22:56 -04:00
Hubert Chathi 464e193dad update nix info 2022-10-06 15:26:39 -04:00
Brendan Abolivier df2cfcb6d0 Python bindings: add `py.typed` to wheels 2022-10-06 17:59:11 +00:00
Hubert Chathi ed94b56d16 fix compatibility with newer versions of emscripten 2022-10-06 13:14:38 -04:00
Hubert Chathi f52d179c18 and update URL for Trixnity 2022-09-12 09:14:01 -04:00
Hubert Chathi 85c0be5fbc update license for Trixnity 2022-09-12 09:11:50 -04:00
Denis Kasak 203083cdd4 fix(megolm spec): Correct the version for the session export format.
It was mistakenly claimed to be 2 when it's supposed to be 1.
2022-09-01 14:58:16 +02:00
David Robertson 983e78dc53 Fix dead link to e2ee guide in the README 2022-06-22 16:47:15 +00:00
Hubert Chathi 92769cec71 release 3.2.12 2022-05-30 13:55:34 -04:00
Hubert Chathi d18d12d379 minor documentation fixes 2022-05-30 13:55:10 -04:00
Denis Kasak 14c5ea70d4 Describe the session export format.
This is the Megolm session format used for `m.forwarded_room_key`, the
server-side room key backups and Megolm key file exports in the Matrix
specification and implementations.
2022-05-26 14:14:44 +02:00
Denis Kasak ee1b0c8a9a Update megolm.md: Fix broken section link. 2022-05-26 10:43:28 +00:00
Hubert Chathi 84807125c0 allow memory to grow in wasm 2022-05-13 16:28:04 -04:00
Hubert Chathi eb21951124 allow passing linker flag to link to standard C++ library 2022-05-13 16:23:58 -04:00
Hubert Chathi 39252b012b re-add olm-python3 rule that was accidentally removed 2022-05-13 16:21:54 -04:00
Brendan Abolivier 86a3d95855 Fix type hints on the PkDecryption class 2022-05-13 11:39:44 +01:00
Faye Duxovni 81f5c4a3cd Make sure checks actually run 2022-05-12 20:56:17 -04:00
Faye Duxovni d0b2b8702f fix deprecated output attribute 2022-05-12 20:55:00 -04:00
Faye Duxovni e000c33a58 don't try to harden unoptimized debug builds, it just causes errors 2022-05-12 20:55:00 -04:00
Faye Duxovni 43672251e4 ensure use of gcc/clang at stdenv level for checks 2022-05-12 19:42:47 -04:00
Hubert Chathi e116efa752 Add check to nix flake: compile C library with gcc and clang 2022-05-12 17:05:22 -04:00
Faye Duxovni a4a700739e ignore nix build result symlinks 2022-05-11 14:39:17 -04:00
Faye Duxovni b8990d90f0 remove now-unused yarn, replace with nodejs 2022-05-11 14:35:37 -04:00
Faye Duxovni 99d635779c include version in derivation name 2022-05-11 14:29:53 -04:00
Faye Duxovni b65ab350f0 let pinning of nixos-unstable commit happen in flake.lock rather than flake.nix 2022-05-11 14:15:37 -04:00
Faye Duxovni c9e6bf9263 use npmlock2nix to provide node_modules 2022-05-11 13:59:33 -04:00
Faye Duxovni 727722d7a8 patch shebangs in build scripts 2022-05-11 13:07:57 -04:00
Hubert Chathi 8510b2f601 initial attempt at nix flake 2022-05-11 11:26:20 -04:00
Hubert Chathi 7bf6fb553e improve documentation for Python function 2022-05-02 12:12:31 -04:00
Hubert Chathi 1c7df35c5f exposed olm_sas_calculate_mac_fixed_base64 in the bindings 2022-04-21 21:45:19 -04:00
Hubert Chathi 2f23d99424 Release 3.2.11 2022-04-08 16:00:24 -04:00
ganfra 0a6a5a5caf Add public pickle/unpickle methods to java bindings 2022-04-08 14:28:24 +00:00
Valere b5dfa28f3b code review 2022-04-08 13:16:37 +00:00
Valere 3c91c66ee2 Unpublished fallback key bindings + forget 2022-04-08 13:16:37 +00:00
Alex Baker dcf5582f8a Add Java wrapper for olm_session_describe
Signed-off-by: Alex Baker <alex@beeper.com>
2022-02-25 10:19:11 -05:00
Hubert Chathi 9d66965962 add Trixnity to list of bindings 2022-02-17 10:08:01 -05:00
Hubert Chathi dd1905454b fix doc building. Thanks to Jonas Smedegaard. 2022-01-14 11:08:58 -05:00
Benoit Marty 9908862979 release 3.2.10 2022-01-10 11:00:49 +01:00
Benoit Marty 7d0a69a099 Ensure the Android library includes the native olm libraries 2022-01-07 22:03:28 +01:00
Benoît Marty 2c6b9d5e3a Fix typo in the url 2022-01-07 18:55:53 +00:00
Hubert Chathi 0dde38bd4f release 3.2.9 2022-01-07 10:56:06 -05:00
Benoit Marty b3478a526b Update POM_SCM_CONNECTION and POM_SCM_DEV_CONNECTION values 2022-01-07 00:13:43 +01:00
Benoit Marty b11f555b01 Do not upload source and Javadoc
Only empty jars will be uploaded
2022-01-06 10:05:37 +01:00
Benoit Marty 23380ca331 Release the library on MavenCentral
Delete stuff added for Jitpack
2022-01-06 10:04:40 +01:00
Benoit Marty 1c3af112c8 Compile and target API 31 2022-01-05 16:28:04 +01:00
Benoit Marty 55c976d4f6 Use Java 11 source compat 2022-01-05 16:12:24 +01:00
Benoit Marty db90ce6b62 Upgrade dependencies of test libraries 2022-01-05 16:12:13 +01:00
Benoit Marty 96407493d1 New notation for the different Int 2022-01-05 16:11:59 +01:00
Benoit Marty 9b2c116fbd Upgrade AGP from 4.2.3 to 7.0.4 2022-01-05 16:11:40 +01:00
Benoit Marty cbc6886a37 Upgrade from gradle-7.0 to gradle-7.3.3 2022-01-05 16:10:31 +01:00
Benoit Marty c172ab6236 Remove unnecessary file 2022-01-05 16:08:52 +01:00
Tulir Asokan 9946acac23 Add Python wrapper for olm_session_describe
Signed-off-by: Tulir Asokan <tulir@beeper.com>
2022-01-03 09:43:21 +00:00
Hubert Chathi 60122a2c2d switch to jasmine (instead of jasmine-node) for JavaScript tests 2021-12-22 13:51:47 -05:00
Hubert Chathi 8475061136 switch to doctest for unit testing
thanks to Nico Werner, who did most of the porting work
2021-12-22 13:45:33 -05:00
Hubert Chathi e197cd76d6 some cleanup 2021-12-21 13:24:34 -05:00
Hubert Chathi 797183f27f release 3.2.8 2021-12-13 08:42:39 -05:00
Hubert Chathi 21dc11ecbf update location of Nim binding 2021-12-13 08:42:14 -05:00
Hubert Chathi 8519ce0269 clear out random arrays 2021-12-10 16:15:22 -05:00
Hubert Chathi c23ce70fc6 improve handling of olm_session_describe when buffer is too short 2021-12-10 16:14:46 -05:00
Hubert Chathi 2dbeea2f1d release 3.2.7 2021-12-06 11:01:21 -05:00
Valere e854c0f907 Quick fixes 2021-12-02 21:46:32 +00:00
valere f647747d27 fallback key java bindings 2021-12-02 21:46:32 +00:00
Benjamin Kampmann f6309f0281 Disable forced exports (introduced in 72b8bf53) for wasm. 2021-12-02 12:27:37 -05:00
Hubert Chathi 4b2f68d11e add missing word 2021-12-01 15:36:46 -05:00
Hubert Chathi fb162258ab add function to TypeScript declaration 2021-12-01 14:34:41 -05:00
Hubert Chathi ee76674f03 remove duplicate definition 2021-12-01 14:34:29 -05:00
Damir Jelić 701f9c765d python: Expose the method to forget the old fallback key 2021-11-24 20:06:24 +01:00
Damir Jelić 85a2f47088 python: Use the unpublished fallback key lenght when outputing fallback keys 2021-11-24 20:06:24 +01:00
Damir Jelić 8c62046392 python: Add support to generate fallback keys 2021-11-24 20:06:24 +01:00
Damir Jelić 845e7cb43b python: Remove Python 2 from the makefile 2021-11-24 20:06:24 +01:00
Hubert Chathi 69ca6cd5ca publish to gitlab.matrix.org Maven repository 2021-11-24 16:06:51 +00:00
Benjamin Kampmann 336e1d56a8 disable DEBUG symbols for emscripten build 2021-11-24 10:43:23 -05:00
Hubert Chathi 6f59e16b58 update function documentation 2021-11-23 22:35:10 +00:00
Denis Kasak 5e5e32fe83 fix typo 2021-11-23 22:35:10 +00:00
Hubert Chathi 631f050554 add a test for fallback keys, and clear memory when we forget the old fallback 2021-11-23 22:35:10 +00:00
Hubert Chathi 29e0287ef3 add function to forget the old fallback key 2021-11-23 22:35:10 +00:00
Hubert Chathi c5eff859cb add JavaScript function for getting unpublished fallback key 2021-11-23 22:35:10 +00:00
Hubert Chathi 4127a84b3d add function for getting length of unpublished fallback keys
and fix a typo
2021-11-23 22:35:10 +00:00
Hubert Chathi 3b6ff327c0 keep testing logs 2021-11-23 22:35:10 +00:00
Hubert Chathi b989db0117 track if fallback keys were published 2021-11-23 22:35:10 +00:00
Hubert Chathi 5039c0cc3a fix python build 2021-11-19 15:49:12 -05:00
Hubert Chathi 98b8e35a7c fix symbol exporting again 2021-11-19 15:28:43 -05:00
Denis Kasak 2430e9bb9a Add link to the Security Disclosure Policy to the README. 2021-11-19 10:17:37 +00:00
Hubert Chathi 609e7e8d40 make sure we have enough space for the encrypted and encoded version of the junk 2021-11-17 14:18:03 -05:00
Hubert Chathi 06b723db6e add note about telling olm how to find wasm file 2021-11-11 14:05:07 -05:00
Johannes Marbach bce4f007b1 Use classic instead of semantic import
This replaces the semantic import for the Security framework with a
classic one. Semantic imports are currently not compatible with Kotlin
Multiplatform Mobile projects which makes OLMKit (and consequently
the iOS Matrix SDK) unusable in KMM.

Fixes: https://github.com/matrix-org/olm/issues/67
Signed-off-by: Johannes Marbach <n0-0ne@mailbox.org>
2021-11-08 15:15:46 -05:00
Hubert Chathi 0e7c0a5613 recommend using cmake more strongly 2021-11-02 16:16:07 -04:00
Hubert Chathi 201f139523 also install olm_export.h when using make 2021-11-02 12:49:52 -04:00
Hubert Chathi 03c5523aac fix typo 2021-09-29 18:33:30 -04:00
Hubert Chathi 8656f1463c release 3.2.6 2021-09-16 17:16:56 -04:00
Hubert Chathi c81dfd0718 fix Python build 2021-09-16 17:06:45 -04:00
Hubert Chathi 4fb723cad3 install the export header too 2021-09-16 15:09:42 -04:00
Hubert Chathi 72b8bf5334 use visibility annotation rather than version file with CMake 2021-09-16 13:45:10 -04:00
Hubert Chathi 904e80b75f release 3.2.5 2021-09-15 19:15:58 -04:00
Onuray Sahin 06407aa08d Generate and retrieve fallback key functions added. 2021-09-14 22:19:13 +00:00
Hubert Chathi 6a63a5bfa9 use full path to externs.js because it's failing to find it 2021-09-14 18:18:26 -04:00
Hubert Chathi e1aa1b3277 add jOlm binding 2021-08-25 21:50:33 -04:00
Stefan Ceriu 91a619b745 Added ObjC fallbackKey support and updated tests. 2021-08-18 16:44:34 +03:00
Stefan Ceriu 8ddb72cfed Updated podfile and added Xcode schemes for both iOS and macOS. 2021-08-18 16:40:32 +03:00
Hubert Chathi 6c552dd7eb use the right size in the tests 2021-08-09 16:21:13 -04:00
Hubert Chathi d84c1af882 East const for consistency 2021-08-06 17:36:01 -04:00
Hubert Chathi 4d6c3ba8d1 make account const in create_outbound_session 2021-08-06 17:29:56 -04:00
Denis Kasak b70e0b06df Differentiate between malformed pickle objects and trailing junk data.
Adds the OLM_PICKLE_EXTRA_DATA error code. We fail with this code when
the pickle object looks right except for some unexpected trailing bytes
which we didn't process.
2021-07-31 01:27:43 +00:00
Denis Kasak d704f4bd3c Fail when an unpickle succeeds but has extra junk data at the end.
Also adds tests to ensure this is working.
2021-07-31 01:27:43 +00:00
Denis Kasak 131f7cfd71 Fix off-by-one comparison error when unpickling uint32_t. 2021-07-31 01:27:43 +00:00
Denis Kasak bdd73c5c32 Fix unpickling error handling. 2021-07-31 01:27:43 +00:00
Denis Kasak 34974551ab unpickle_account: Add error checking to the harness. 2021-07-31 01:27:43 +00:00
Denis Kasak 0a8bbde361 Support building a "disarmed" target via the OLM_FUZZING macro.
Like other crypto libs, libolm contains many obstacles which a fuzzer is
unlikely to be able to surmount but which are not important for the end
goal of fuzzing. The easiest and most robust way around this is to remove
these obstacles conditionally when building the fuzzer binaries.

This commit adds a preprocessor macro OLM_FUZZING which can be used to
conditionally disables problematic bits of code during compile-time for
easier fuzzing.

Currently the only thing it disables is the encryption/decryption and
base64 encoding/decoding when processing pickled Megolm keys. This
allows the fuzzers to fuzz the unpickling functionality directly without
inadvertently fuzzing the base64 encoder and encryption (which should be
fuzzed separately).

The macro is set in the Makefile *only* when building fuzzer binaries.
2021-07-13 13:51:16 +02:00
Denis Kasak b38e282f3a fuzzing: Add script for starting fuzzers on a given harness. 2021-07-13 13:49:18 +02:00
Denis Kasak ceed90922a fuzzing: Add readme. 2021-07-13 13:49:18 +02:00
Denis Kasak 4d14750c38 Move fuzzers under fuzzing/ dir. 2021-07-13 13:49:18 +02:00
Denis Kasak e06ac20558 Add unpickle_megolm_outbound fuzzer. Enable C harness support. 2021-07-13 11:13:15 +00:00
Denis Kasak 811e56a0f0 Add lib_exports.sh for printing list of exported functions.
Prints the list of exported functions from a built library object.
Useful for sanity checking.
2021-07-13 10:50:27 +02:00
Denis Kasak 583f8b761b Add some more files to .gitignore
- `compile_commands.json`: clang compilation database
- `.ccls-cache`: Cache directory for the ccls language server
- `.clang-format`: clang formatting description
2021-07-12 16:58:11 +02:00
Denis Kasak 84dbba8e1c Makefile: Remove debugging flag from the release target. 2021-07-12 16:50:34 +02:00
Denis Kasak a44fc368f2 Makefile: Fix passing optimization flag to fuzzing builds. 2021-07-12 16:50:06 +02:00
Denis Kasak 93352b55e7 fuzz_group_decrypt: Enable AFL++ persistent mode. 2021-07-12 15:48:27 +02:00
Denis Kasak 7dd4c77c19 Add .editorconfig.
See https://editorconfig.org/ for more information.
2021-07-08 14:28:40 +00:00
Denis Kasak 4901435a0e Improve cleanup in fuzzing harnesses 2021-07-08 14:23:55 +00:00
Johannes Hayeß 254a4a5619 Fix building of tests with MSVC
Hi,

currently tests don't build with MSVC, because the Base64 test tries to initialize multiple arrays with a length value that was derived from a non-const context. I have fixed this by using vectors instead.

Sincerely

Johannes Hayeß

From 2d76972a862f0aa04b5011537bef71a49aa82a03 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Johannes=20Haye=C3=9F?= <jhaye@mailbox.org>
Date: Sun, 27 Jun 2021 17:46:24 +0200
Subject: [PATCH] Fix compiling with MSVC
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Previously attempts to initialize arrays with non-const value. This
seemingly works on GCC/clang due to their static code analysis, but
fails with MSVC. This switches to dynamic memory allocation with
std::vector, to solve the problem.

Signed-off-by: Johannes Hayeß <jhaye@mailbox.org>
2021-06-29 13:14:05 -04:00
Hubert Chathi abf8f97491 fix JavaScript build 2021-06-18 13:12:11 -04:00
Hubert Chathi 0f7c13334f install error.h when using make too 2021-06-17 15:26:46 -04:00
Hubert Chathi 2aad86ea84 fix Python build 2021-06-17 11:56:08 -04:00
Denis Kasak 9a8b421903 Update dart-olm URL. 2021-06-17 16:20:48 +02:00
Hubert Chathi 37c8e14e53 make functions const where possible 2021-06-16 23:22:25 -04:00
Hubert Chathi 7263c4221b add functions to get the error codes rather than error strings 2021-06-16 22:40:14 -04:00
Hubert Chathi 60be1ca55f add support file for cross-compiling Windows library 2021-06-16 15:28:30 -04:00
Hubert Chathi 1b7973626e only export olm functions to avoid colliding with other libraries 2021-06-16 15:05:19 -04:00
Hubert Chathi d47c2a92b8 make new pickle/unpickle function 2021-06-09 14:59:31 +00:00
Hubert Chathi 4803f4192d make (de)serialize methods public in OlmAccount 2021-06-09 14:59:31 +00:00
Hubert Chathi 3612ac7ae7 add missing dependency in Makefile for javascript/olm_prefix.js 2021-06-08 14:57:05 -04:00
Denis Kasak b90f9ee7d3 Fix typo in docstring (repeated word). 2021-06-04 22:24:15 +00:00
Denis Kasak 6ed8d687e8 Document olm_create_inbound_session_from properly.
The old docstring was the same as olm_create_inbound_session and didn't
explain the difference between them.
2021-06-04 22:24:15 +00:00
Denis Kasak 3e6592e445 Compile ASAN and MSAN versions of fuzzer harnesses too. 2021-06-02 14:02:19 +02:00
Denis Kasak 56df2613f3 Switch to afl-clang-fast(++).
This type of instrumentation is much faster (several times over) and
supports much more features than afl-gcc/afl-g++, though it requires
a LLVM/clang installation.
2021-06-02 14:02:19 +02:00
Hubert Chathi 64afab9364 prepare for release 2021-06-01 13:44:45 -04:00
Benoit Marty 995def932e Fix issue with Jitpack build 2021-05-28 18:12:50 +02:00
Hubert Chathi d856c441b6 use Python 3 2021-05-24 10:32:57 -04:00
Hubert Chathi 22bc1155ed prepare for release 2021-05-24 10:29:24 -04:00
Hubert Chathi 891a5f22c8 fix path 2021-05-24 10:27:28 -04:00
Denis Kasak ccc0d122ee olm_pk_decrypt: Ensure inputs are of correct length. 2021-05-24 15:50:14 +02:00
Denis Kasak 2f35e0bc61 olm_sas_set_their_key: Fail early on invalid base64. 2021-05-24 15:50:14 +02:00
Denis Kasak e82f2601b0 Fail decoding base64 of invalid length.
olm::decode_base64 now returns the length of the raw decoded data on
success. When given input with an invalid base64 length, it fails early
(before decoding any input) and returns -1.

This also makes the C function _olm_decode_base64 an actual binding of
olm::decode_base64 instead of a wrapper with slightly different
behaviour.
2021-05-24 15:50:14 +02:00
Denis Kasak a5efc08ef3 olm: Also initialize all fields when decoding Olm messages.
As a precaution.
2021-05-11 13:32:23 +02:00
Denis Kasak c325db02fc megolm: Fix use of uninitialized value in group message decoding.
_olm_decode_group_message should initialize all fields of the results
struct before returning. This is because its caller
_decrypt_max_plaintext_length relies on it having initialized these
fields.

Luckily, this only allows one to subvert the version check in
_decrypt_max_plaintext_length, but not the following check that the
ciphertext field is non-null because that field *is* initialized.
2021-05-11 13:23:19 +02:00
Denis Kasak 0a7b6da9a0 Slightly refactor/comment the harness for clarity. 2021-05-10 21:04:44 +00:00
Denis Kasak 8d1cfd207a Fix a fuzzing harness double free when input is of size 0.
Consider the case when the input is size 0. In this case, `count` and
`buffer_pos` will be 0 as well. The `realloc` call in the `count == 0`
branch will then effectively become a free.

However, `realloc` can sometimes return `NULL` when a 0 is passed for
the size. The current code assumes that this only happens on a memory
allocation error and breaks out of the loop. This then becomes a double
free because the buffer is freed a second time, causing an abort.

The intent of the `realloc` is probably to downsize the buffer to fit
the data exactly in order to make incorrect memory access more obvious.
This commit skips this downsizing if the size of the input data is 0.
2021-05-10 21:04:44 +00:00
Arun Babu Neelicattu 15f65283c7 make: Replace deprecated emcc configuration
EXTRA_EXPORTED_RUNTIME_METHODS is deprecated. Replace with
EXPORTED_RUNTIME_METHODS.
2021-05-10 20:40:42 +00:00
Arun Babu Neelicattu 0684eb4564 ci: add initial build pipeline 2021-05-10 20:40:42 +00:00
Arun Babu Neelicattu b0a05976ea python: remove tox basepython configuration 2021-05-10 20:40:42 +00:00
Hubert Chathi 18ad6cb067 Merge branch 'fix-ncc-audit-url' into 'master'
Fix URL to the NCC Group audit.

See merge request matrix-org/olm!22
2021-05-04 21:58:35 +00:00
Hubert Chathi d7c3971f9a update release instructions 2021-05-04 17:54:05 -04:00
Denis Kasak c95677611c Fix URL to the NCC Group audit.
The original URL is now redirecting to a generic listing page and there
are no links to the actual Olm audit paper there.
2021-05-04 15:09:29 +02:00
Lukas Lihotzki 7f53dedca6 Declare olm_sas_calculate_mac_fixed_base64 in header
Signed-off-by: Lukas Lihotzki <lukas@lihotzki.de>
2021-04-27 16:18:33 -04:00
Hubert Chathi 3b3f2c71dc Merge branch 'bma/upgrade_gradle' into 'master'
Update gradle wrapper and build tools

See merge request matrix-org/olm!20
2021-04-16 20:17:51 +00:00
Benoit Marty f1d8efd821 Simplify assertions using suggestion from IDE 2021-04-16 21:57:57 +02:00
Benoit Marty 1694f15ffb Fix test compilation and run test with success
Using command `./gradlew connectedAndroidTest` on an API 21 emulator
2021-04-16 21:50:13 +02:00
Benoit Marty 4d2522a65c Update gradle wrapper and build tools
BuildConfig.VERSION_NAME is not available anymore when building library
Also replace JCenter by MavenCentral
2021-04-16 21:08:55 +02:00
Hubert Chathi dbbf467075 Merge branch 'manu/swift_package_manager' into 'master'
Xcode: Add support of Swift Package Manager

See merge request matrix-org/olm!19
2021-04-06 18:22:51 +00:00
manuroe 26bd2fc35d Swift package: Update instructions 2021-04-06 17:18:50 +02:00
manuroe 09fbb9e966 Xcode: Add support of Swift Package Manager
Made by Johennes at https://github.com/matrix-org/olm/issues/51#issuecomment-809128833
2021-04-02 19:16:11 +02:00
Hubert Chathi f16377822f Add LibreJS license tag 2021-03-31 16:11:41 -04:00
Matthew Hodgson 09384b4d45 spell ephemeral correctly... 2021-03-18 01:29:23 +00:00
Hubert Chathi bcb89bcc24 add Common Lisp bindings 2021-03-02 16:13:39 -05:00
Hubert Chathi 3745ea57bb bump version number and add changelog 2021-02-22 17:06:13 -05:00
Hubert Chathi 0bb0f85e18 don't use variables that haven't been set yet 2021-02-22 16:54:35 -05:00
Hubert Chathi 21ba95ade5 create and install a pkg-config file on Unix-like systems 2021-02-22 16:54:26 -05:00
Hubert Chathi cabefb17dc rename npm package to @matrix-org/olm
to be published via our gitlab repository
2021-02-22 15:41:29 -05:00
Hubert Chathi a07e27cfa5 Merge branch 'sas-base64-fix' into 'master'
sas: Fix the base64 encoding of the MAC.

See merge request matrix-org/olm!16
2021-02-19 22:18:28 +00:00
Hubert Chathi 23e0486007 Merge branch 'manu/olmkit_pickling_v2' into 'master'
OLMKit:  New pickle version using a pickle key provided externally

See merge request matrix-org/olm!17
2021-02-19 19:23:21 +00:00
manuroe 4be7cc367b OLMKit: Forgot to implement pickle v2 for OLMSession 2021-02-19 12:00:03 +01:00
manuroe b69b56d0bb OLMKit: New pickle version using a pickle key provided externally
Improve ObjC wrappers so that they can use a pickle key provided by the olm lib user.

This new behavior is optional to not break existing usage.

It is retro compatible and use pickle versioning already in place. 
Existing key will be unpickled with pickle v1 and pickled with pickle v2 if an external pickle key is provided.
2021-02-19 09:23:36 +01:00
Damir Jelić f46577a06a sas: Introduce a new calculate mac function to fix the base64 issue
Since it's important to keep backwards compatibility introduce a new
function to calculate the MAC using a SAS object.

Modifying the existing functions would break compatibility with older
releases of libolm.
2021-02-02 16:58:28 +01:00
Damir Jelić 4e927eb1cf sas: Fix the base64 encoding of the MAC.
When calculating the MAC for a message using olm_sas_calculate_mac() and
olm_sas_calculate_mac_long_kdf() the resulting MAC will be base64
encoded using _olm_encode_base64().

The _olm_encode_base64() method requires an input buffer and output
buffer to be passed alongside the input length. The method is called
with the same buffer, containing the MAC, for the input buffer as well
as for the output buffer. This results in an incorrectly base64 encoded
MAC.

For example the byte array:
    [121, 105, 187, 19, 37, 94, 119, 248, 224, 34, 94, 29, 157, 5,
     15, 230, 246, 115, 236, 217, 80, 78, 56, 200, 80, 200, 82, 158,
     168, 179, 10, 230]

will be encoded as  eWm7NyVeVmXgbVhnYlZobllsWm9ibGxzV205aWJHeHo
instead of as       eWm7EyVed/jgIl4dnQUP5vZz7NlQTjjIUMhSnqizCuY

Notice the different value at the 10th character.

The correct result can be independently checked using Python for
example:

>>> from base64 import b64encode
>>> mac = [121, 105, 187, 19, 37, 94, 119, 248, 224, 34, 94, 29, 157, \
           5, 15, 230, 246, 115, 236, 217, 80, 78, 56, 200, 80, 200, \
           82, 158, 168, 179, 10, 230]
>>> b64encode(bytearray(mac)).rstrip(b"=")
>>> b'eWm7EyVed/jgIl4dnQUP5vZz7NlQTjjIUMhSnqizCuY'

This happens because the encode_base64() method that is used does not support
in-place encoding in the general case. This is because the remainder for a 32
bit input will always be 2 (32 % 6 == 2).

The remainder will be used over here:
c01164f001/src/base64.cpp (L74)

The logic that gets executed if a remainder exists depends on the original input
values, since those already got in-place encoded the whole block will behave
differently if the input buffer is the same as the output buffer.
2021-01-31 12:56:32 +01:00
Hubert Chathi c01164f001 add link to nim binding 2021-01-14 12:40:52 -05:00
Tobias Furuholm 541a2bf6fd Fix length calculation of fallback key json 2020-11-24 13:47:27 -05:00
Richard van der Hoff b9771dae61 DH ratchet sequence diagram 2020-11-23 18:33:26 +00:00
Richard van der Hoff 11d34f79af update double-ratchet diagrams 2020-11-23 18:33:26 +00:00
Hubert Chathi 1fd8d2978f fix typo 2020-11-23 13:17:08 -05:00
Hubert Chathi 64b8bc11cb use the right version in the changelog 2020-10-29 16:35:17 -04:00
Hubert Chathi 8efa0ec17d Use current source directory in CMake. Thanks to Gorgurov Alexey. 2020-10-29 16:26:06 -04:00
Hubert Chathi c5ab3ecbf2 Merge branch 'patch-1' into 'master'
Update index.d.ts; specify PRIVATE_KEY_LENGTH const export

See merge request matrix-org/olm!15
2020-10-14 01:43:31 +00:00
Michael Telatynski 7768c3219f Update index.d.ts; specify PRIVATE_KEY_LENGTH const export 2020-10-13 21:44:20 +00:00
Hubert Chathi c4d737c86c bump version numbers and update changelog 2020-10-06 17:39:48 -04:00
Hubert Chathi 60d451bbbe all the (un)pickles take a Uint8Array 2020-10-06 17:28:52 -04:00
Hubert Chathi 4d17aa4f05 bump version numbers and update changelog 2020-10-06 15:08:10 -04:00
Hubert Chathi d4afebc883 fix typo 2020-10-06 14:47:43 -04:00
Lukas Lihotzki 030e506c00 use stackAlloc instead of allocate 2020-10-06 12:02:17 +02:00
Lukas Lihotzki 22f85d3f3d simplify Makefile (olm_legacy.js) 2020-10-06 09:48:15 +02:00
Lukas Lihotzki 6611165bff do not pollute the global object 2020-10-03 03:46:15 +02:00
Lukas Lihotzki add885c874 add test_mem target for valgrind memcheck 2020-10-01 15:40:22 +02:00
Lukas Lihotzki be0c31894a fix memory leaks in tests 2020-10-01 15:39:48 +02:00
Lukas Lihotzki 73a9ced64e simplify Makefile 2020-09-26 18:24:58 +02:00
Lukas Lihotzki ac61190bb3 fix build with emscripten 2.0.4 2020-09-26 18:23:50 +02:00
Hubert Chathi 0fd315d54c Merge branch 'python_other_key_set' into 'master'
remove other_key checks from Python binding since it's done in C now

See merge request matrix-org/olm!14
2020-09-24 18:56:19 +00:00
Hubert Chathi 0e6ec3062d remove other_key checks from Python binding since it's done in C now 2020-09-24 18:56:19 +00:00
Hubert Chathi ec5ff1e032 also check that the pubkey is set when calculating the MAC 2020-09-23 16:47:00 -04:00
Hubert Chathi 78d9cbabb7 set their_key_set flag explicitly on init 2020-09-23 16:11:37 -04:00
Saúl Ibarra Corretgé 2ef1f6f4fc SAS: add olm_sas_is_their_key_set
Also make olm_sas_generate_bytes fail if their key wasn't set.
2020-09-23 15:27:55 -04:00
Hubert Chathi 4bae4134eb partially unindent R_{i,j} derivation
so that we're under GitLab's 1000 character limit for rendering maths
2020-09-17 17:46:37 -04:00
Hubert Chathi 84841a19e2 fix exponents in megolm spec 2020-09-17 17:43:00 -04:00
Hubert Chathi 3cd6b15853 Merge branch 'uhoreg/fallback' into 'master'
add support for fallback keys

See merge request matrix-org/olm!13
2020-09-17 21:42:25 +00:00
Hubert Chathi c47c6ca399 fix style 2020-09-16 16:14:23 -04:00
Hubert Chathi c45f19f12d Merge branch 'master' into uhoreg/fallback 2020-09-01 15:57:20 -04:00
Hubert Chathi 89050dc0b6 allow some things to be Uint8Array, and fix some TypeScript declarations 2020-08-31 10:50:33 -04:00
Hubert Chathi 171044f3fc add support for fallback keys 2020-08-14 17:29:41 -04:00
Saúl Ibarra Corretgé a0284c2ba3 Fix group demo to work with > 2 users
Add 4 by default, and make sure OT keys are not reused.
2020-07-04 16:59:26 -04:00
Hubert Chathi 8a958beb48 bump version info for release 2020-06-11 11:47:50 -04:00
Hubert Chathi 9349c1b82b changelog for release 2020-06-11 11:47:39 -04:00
Hubert Chathi 6fea6898d4 update release instructions 2020-06-11 11:47:24 -04:00
Hubert Chathi c9a183a7c5 add list of bindings 2020-06-11 11:25:52 -04:00
Hubert Chathi efd17631b1 move -o option before source files, for better compatibility with LLVM 2020-05-19 15:10:24 -04:00
Hubert Chathi ad173bc798 Merge branch 'matthew/js-fixes' into 'master'
Fix JS demos, which had bitrotted

See merge request matrix-org/olm!12
2020-05-19 15:08:27 +00:00
Matthew Hodgson ddd140b23d fix group demo to work with Olm.init() 2020-05-16 17:11:54 +01:00
Matthew Hodgson 14c1db02fe fix 1:1 demo to work with Olm.init() 2020-05-16 17:10:32 +01:00
Matthew Hodgson fdf25eb3ba spell out error msg if you don't Olm.init 2020-05-16 17:10:18 +01:00
Matthew Hodgson 83bf351a34 fix build on latest emscripten 2020-05-16 17:10:02 +01:00
Matthew Hodgson 5a9fdd85cb remove overzealous -D param to install which breaks build on macOS 2020-05-15 03:06:08 +01:00
Hubert Chathi 05a7af8db1 add a note about calling Olm.init 2020-05-13 19:05:19 -04:00
Hubert Chathi 281c5aac21 fix type signature of unpickle in Inbound/OutboundGroupSession
Thanks to Dominic Fischer for spotting.
2020-05-07 18:01:52 -04:00
stoically 611d3949cb Add wasm target to Makefile
Allows building an WASM-ready archive with emscripten.

This allows e.g. to compile to the `wasm32-unknown-unknown`
target with Rust.

Related matrix-rust-sdk PR:
https://github.com/matrix-org/matrix-rust-sdk/pull/31

Signed-off-by: stoically <stoically@protonmail.com>
2020-05-07 15:58:24 -04:00
Hubert Chathi 9cc2394672 Merge branch 'uhoreg/typescript' into 'master'
Add TypeScript definition file

See merge request matrix-org/olm!11
2020-04-29 16:42:25 +00:00
Hubert Chathi e6f8a99b34 add missing declaration for PkDecryption#decrypt and SAS class 2020-04-29 12:39:41 -04:00
Hubert Chathi f409b69e88 add declarations for Inbound/OutboundGroupSession 2020-04-24 17:44:28 -04:00
Hubert Chathi 954d6f98eb initial TypeScript definition file 2020-04-24 17:27:55 -04:00
Richard van der Hoff 930c467754 Update signing.md to use operatorname 2019-11-08 14:11:05 +00:00
Richard van der Hoff 0469065855 Merge branch 'rav/fix_math' into 'master'
Fix some math blocks

See merge request matrix-org/olm!10
2019-11-08 14:09:12 +00:00
Richard van der Hoff 5bcfeaffe3 Update olm.md 2019-11-08 14:00:59 +00:00
Richard van der Hoff a9c7bde457 Update signing.md 2019-11-08 13:48:34 +00:00
Richard van der Hoff 52098b3af7 Update megolm.md 2019-11-08 13:34:16 +00:00
Alexey Rusakov baaf002663 CMakeLists.txt: export include directories in install(TARGETS)
Without that, if olm is installed to a non-standard location, the user code might run CMake configuration fine but further building will fail.

Signed-off-by: Alexey Rusakov <Alexey.Rusakov@pm.me>
2019-10-23 16:48:14 -04:00
Hubert Chathi 6753595300 release 3.1.4 2019-10-09 12:33:47 -04:00
Hubert Chathi 387deeea8f Merge branch 'dbkr/olm_session_describe' into 'master'
Add olm_session_describe

See merge request matrix-org/olm!9
2019-10-09 15:37:56 +00:00
Hubert Chathi fc423fad15 check return value of snprintf, fix typo, add clarification 2019-10-08 17:44:09 -04:00
David Baker b482321213 Pass in a buffer to olm_session_describe
instead of having a static one, as that could end up taking up a
lot of memory if your app keeps olm sessions hanging about.
2019-10-04 11:43:40 +01:00
David Baker e73a208fb2 doc string 2019-10-01 11:18:05 +01:00
David Baker 39a1ee0b18 Add olm_session_describe
As a way to dump the state of an olm session, ie. the chain indicies,
so we can debug why olm sessions break and get out of sync.
2019-10-01 11:14:16 +01:00
Hubert Chathi 3568060570 Merge branch 'dbkr/emscripten_is_picky_about_pic' into 'master'
Build the js objects without PIC

See merge request matrix-org/olm!8
2019-09-30 13:12:38 +00:00
Hubert Chathi 44c2e47a3e Merge branch 'dbkr/wrong_comment_breaks_everything' into 'master'
Fix comment and also js build

See merge request matrix-org/olm!7
2019-09-30 13:08:23 +00:00
David Baker 72df5301e0 Build the js objects without PIC
This confuses emscripten now
2019-09-30 13:50:35 +01:00
David Baker b83a0c0992 Fix comment and also js build
The python that searches the header files for exports isn't smart
enough to know what is a comment and what isn't, so it picks this
up too and emscripten then complains about it being undefined.
2019-09-30 13:19:23 +01:00
Hubert Chathi 57b6839c25 Merge branch 'rav/megolm_doc_format' into 'master'
Update megolm.md

See merge request matrix-org/olm!6
2019-08-22 04:30:45 +00:00
Richard van der Hoff 32f3a82bf9 Update megolm.md 2019-08-22 04:30:45 +00:00
Trygve Aaberge e267825bb7 Makefile: Overwrite symlinks in install and install-debug
Without this, it's not possible to run the install or install-debug
rules multiple times.

Signed-off-by: Trygve Aaberge <trygveaa@gmail.com>
2019-08-08 09:29:14 -07:00
Hubert Chathi c463d8b55b Merge branch 'python/drop-hypothesis' into 'master'
tests: Drop hypothesis from the python tests.

See merge request matrix-org/olm!5
2019-07-03 19:27:30 +00:00
Alexey Andreyev aa0c9ab6b5 CMakeLists.txt: add env vars to target_include_directories 2019-07-03 15:24:13 -04:00
Hubert Chathi ebd3ba6cc1 release 3.1.3 2019-06-24 17:09:41 -04:00
Matthew Hodgson ae38f2c5a0 Merge branch 'python/unicode_decode_errors' into 'master'
Python unicode decode errors when decrypting.

See merge request matrix-org/olm!4
2019-06-22 17:06:02 +00:00
Damir Jelić 61175c969b tests: Simplify the input parameter for the Unicode decoding tests. 2019-06-20 14:08:21 +02:00
Damir Jelić 28350d612e tests: Use Unicode literals in the Unicode decoding tests.
This is needed because the function returns Unicode strings and the
comparison will fail under python2 unless Unicode literals are used.
2019-06-20 14:07:14 +02:00
Damir Jelić 5d7070d2f3 tests: Simplify the sha256 tests for python.
Hypothesis isn't used anymore and the strings are now constants, meaning
that the hashes should never match.
2019-06-20 13:55:03 +02:00
Damir Jelić 125c62098c tests: Drop hypothesis from the tests.
Hypothesis recently had some problems with the typing module breaking
the tox tests.

Since Hypothesis isn't really used much in the test this patch removes
it from them as well as from the test-requirements.
2019-06-20 13:45:33 +02:00
Damir Jelić c4d703ac3d _compat: Make the encoding argument explicit in to_unicode_str(). 2019-06-20 12:24:08 +02:00
Damir Jelić 7538a1eccf olm: Rename the errors function argument in the decryption functions. 2019-06-20 12:16:37 +02:00
Matthew Hodgson 25662564d4 Merge branch 'matthew/define-secrecy' into 'master'
explicitly define backward & forward secrecy

See merge request matrix-org/olm!3
2019-06-19 23:25:56 +00:00
Matthew Hodgson cfd1450b0e fix typo & more wording from luca 2019-06-20 00:21:47 +01:00
Damir Jelić fec41f9540 _compat: Remove the now unused native_str. 2019-06-19 15:07:14 +02:00
Damir Jelić 5e24c605d2 _compat: Change the to_native_str into a to_unicode_str function.
The to_native_str function was supposed to produce Unicode decoded
native strings for python2 and python3.

Upon further consideration this doesn't make much sense since under
python2 it would need to decode the bytes into a Unicode string and turn
it back into a python2 str.

The ability to use the replacement character requires us to use a
Unicode string under python2 as well.
2019-06-19 15:03:57 +02:00
Damir Jelić ba65551d5f _compat: Remove unused import. 2019-06-19 14:42:58 +02:00
Matthew Hodgson 27f5c25fe8 incorporate review from vdh & luca 2019-06-18 23:47:42 +01:00
Damir Jelić 9faa100c6a Makefile: Add an isort target. 2019-06-18 13:52:02 +02:00
Damir Jelić 2f5590bf38 olm: Allow decryption functions to define how to handle unicode decode errors.
This patch changes the decryption functions not to fail if there was an
unicode decode error while converting the decrypted bytes plaintext into
a native python string.

Characters that cannot be decoded as unicode are now replaced with the
unicode replacement character (U+FFFD).

The old behaviour of raising an UnicodeDecodeError can be achieved by
passing the "strict" error handling scheme to the decrypt function.
2019-06-18 13:50:46 +02:00
Matthew Hodgson f8abaf9e2f explicitly define backward & forward secrecy
as it repeatedly trips people up, including me
2019-06-18 12:45:31 +01:00
Damir Jelić e1a4e6ebf1 compat: Add a method to convert bytes to a string that handles unicode errors. 2019-06-18 13:44:22 +02:00
Hubert Chathi 4bb039a98e fix another incorrect comment 2019-06-12 16:22:15 -04:00
Hubert Chathi 3ed150edf7 use the right error in the comment 2019-06-12 11:22:28 -04:00
Matthew Hodgson a18a4e8eb4 remove megolm.rst 2019-05-20 21:39:54 +01:00
Matthew Hodgson cab1edb6da Merge branch 'markdown' into 'master'
Convert docs from RST to Markdown

See merge request matrix-org/olm!2
2019-05-20 20:38:57 +00:00
Matthew Hodgson b6cd1690f2 merge 2019-05-20 21:38:16 +01:00
Matthew Hodgson c368898cef Merge branch 'master' into markdown 2019-05-20 21:38:04 +01:00
Matthew Hodgson 214908ace5 fix thinko in megolm spec
as per https://github.com/matrix-org/olm/issues/15.
thanks to @dest
2019-05-20 21:16:51 +01:00
Hubert Chathi 969c8b45e5 add more consts and comments 2019-05-14 22:02:50 -04:00
Aaron Raimist 5b69a1a5cd Convert CONTRIBUTING.rst to markdown
Signed-off-by: Aaron Raimist <aaron@raim.ist>
2019-05-14 12:57:54 -04:00
Aaron Raimist b46ac91928 Convert README.rst to markdown
Signed-off-by: Aaron Raimist <aaron@raim.ist>
2019-05-14 12:57:48 -04:00
Aaron Raimist 73288e6f2a Convert signing.rst to markdown
Signed-off-by: Aaron Raimist <aaron@raim.ist>
2019-05-14 12:55:48 -04:00
Aaron Raimist 6a72cfd287 Convert olm.rst to markdown
Signed-off-by: Aaron Raimist <aaron@raim.ist>
2019-05-14 12:55:44 -04:00
Aaron Raimist e273189af3 Convert megolm.rst to markdown
Signed-off-by: Aaron Raimist <aaron@raim.ist>
2019-05-14 12:55:40 -04:00
Hubert Chathi 0757e6df40 add comment about input buffers being overwritten
also make some params const where possible
2019-05-14 12:53:19 -04:00
Hubert Chathi 769d013ef7 release 3.1.2 2019-04-30 18:25:21 -04:00
Benoit Marty 69feb86c01 update to newer Android SDK version 2019-04-30 18:22:30 -04:00
Benoit Marty 27fcc337a3 improve some comments 2019-04-30 18:19:26 -04:00
Hubert Chathi 6aafd69f8f bump version number for 3.1.1 2019-04-29 15:01:09 -04:00
Hubert Chathi 38649855f7 avoid variable length arrays in SAS test 2019-04-29 14:49:47 -04:00
Hubert Chathi af47497ace update changelog for 3.1.1 2019-04-29 14:35:15 -04:00
Hubert Chathi ba1c20d6b4 disable test_ratchet on dynamically-linked Windows builds 2019-04-29 13:32:46 -04:00
Hubert Chathi b79c6d6f69 export all symbols for Windows DLLs 2019-04-29 13:11:48 -04:00
Hubert Chathi 099b3ce82a also add SAS to the tests cmake 2019-04-29 11:47:55 -04:00
Hubert Chathi 327d6ac0eb Merge branch 'poljar/cmake_sas' into 'master'
cmake: Add the SAS functions to the CMake build.

See merge request matrix-org/olm!1
2019-04-28 11:35:58 +00:00
Damir Jelić 6eca9f1278 cmake: Add the SAS functions to the CMake build.
Signed-off-by: Damir Jelić <poljar@termina.org.uk>
2019-04-28 08:55:40 +02:00
Hubert Chathi 992323c37c update changelog 2019-04-22 13:17:30 -04:00
Hubert Chathi 8ec6387615 include the C++ string library in unit tests
to fix compilation errors in some compilers
2019-04-22 13:14:39 -04:00
Hubert Chathi ea13edcae0 don't use variable length or zero-length arrays in test files
as some compilers don't handle that
2019-04-22 10:12:42 -04:00
Hubert Chathi 157c0fa67e remove some debugging output from tests 2019-04-22 10:11:43 -04:00
Hubert Chathi 5832e85691
Merge pull request #89 from matrix-org/manuroe/cocoapods_update
OLMKit: Make podspec point to new https://gitlab.matrix.org/matrix-org/olm
2019-04-19 08:02:23 -04:00
manuroe 51ce62fce0 OLMKit: Make podspec point to new https://gitlab.matrix.org/matrix-org/olm 2019-04-19 11:59:22 +02:00
Hubert Chathi 009173c1ab update changelog links to point to new repo 2019-04-17 21:09:03 -04:00
Hubert Chathi bac8ca7802 prepare for 3.1.0 release 2019-04-17 17:31:01 -04:00
Hubert Chathi 0d0169c839
Merge pull request #86 from matrix-org/add_python_pk_signing
add python bindings for PK signing
2019-04-12 19:21:17 -04:00
Hubert Chathi ab6e8d5086 more isort 2019-04-12 19:17:06 -04:00
Hubert Chathi b12fe0aeb3
Merge pull request #88 from matrix-org/manuroe/sas_pksigning
OLMKit: add Short Authentication String verification
2019-04-12 19:04:09 -04:00
Hubert Chathi 107adba241 isort python/olm/pk.py 2019-04-12 13:02:57 -04:00
manuroe 5147349fea OLMKit: OMLPkSigning: Zero mutableSeed 2019-04-12 11:17:53 +02:00
manuroe 89357b6a49 OLMKit: OlmPkEncryption: Fix typos in sanity checks
(cherry picked from commit add1bd6e4250012dcfa30a40d763dba82f53c254)
2019-04-10 23:27:27 +02:00
manuroe 809793c9ba OLMKit: SAS: Add PK signing
(cherry picked from commit ff31d489c68d5d9e597bd55a6f6a64f3fe4c7ecc)
2019-04-10 23:27:14 +02:00
manuroe 4057f59453 OLMKit: SAS: Added macLongKdf support
(cherry picked from commit 934d516eb35c488ee197e1bab78a4c81e3c8241d)
2019-04-10 23:27:00 +02:00
manuroe 5de295da3e OLMKit: add Short Authentication String verification
(cherry picked from commit 3e954ca2729d3333ea853c878602d1696f616573)
2019-04-10 23:26:02 +02:00
manuroe 3609227c6e Fix arg name in comments
(cherry picked from commit c0a53ebaeda420f636698b99c393868d2bf8f905)
2019-04-10 23:26:02 +02:00
Damir Jelić 086133f39a Merge branch 'python-sas' 2019-04-10 15:18:07 +02:00
Valere c79d9282dc
Merge pull request #83 from matrix-org/BillCarsonFr/java_sas
Java binding for SAS
2019-04-10 14:03:49 +02:00
Valere eb7347bb52 Return string instead of byte array for b64 encoded data 2019-04-10 12:24:00 +02:00
Valere 16a28f297c Added macLongKdf support 2019-04-10 12:24:00 +02:00
Valere c9369a4383 E2E: SAS Verification (olm)
Fix / missing free() on some errors


Added doc regarding string encoding for keys


cleaning
2019-04-10 12:24:00 +02:00
Damir Jelić 54cb52e05e python: Add the SAS header to the manifest.
The SAS header is required to build the package therefore it needs to be
shipped with the source distribution of the package.

Adding it to the manifest achieves this.
2019-04-09 11:41:44 +02:00
Damir Jelić 32b99b7935 python: Add support for the long KDF MAC calculation. 2019-04-09 10:57:36 +02:00
Damir Jelić 659eb34fa4 python: Remove an unneeded and old copyright header. 2019-04-09 10:47:26 +02:00
Hubert Chathi 74e9300daf add python bindings for PK signing 2019-04-08 17:19:47 -04:00
Hubert Chathi ebc156e7c2 re-add null termination in javascript
because older versions of emscripten don't support the length argument to
UTF8ToString.
2019-04-08 15:54:02 -04:00
Damir Jelić 071029c201 javascript: Switch from deprecated Pointer_stringify() to UTF8toString().
The Pointer_stringify() function is deprecated and has a couple of
issues because it tries to guess the encoding of the buffer. In some
cases it can ignore the length parameter which could end up in
inconsistencies.

Switch to UTF8toString() that takes a length parameter and respects,
that way we don't need to allocate an additional byte for a NULL byte.
2019-04-08 15:18:28 -04:00
Hubert Chathi 2a6400716c
Merge branch 'master' into poljar/python-sas 2019-04-08 15:08:17 -04:00
Damir Jelić 709687a7b5 python: Build the build headers with setup.py.
This allows downstream users to install this python module with pip as
well as allowing people to declare it as a dependency in their setup.py.

Signed-off-by: Damir Jelić <poljar@termina.org.uk>
2019-04-08 15:04:32 -04:00
Damir Jelić f1d45c2cd3 python: Fix the MANIFEST file.
This patch adds the Olm header files to the manifest, this results in
the header files being added to source distributions of the python-olm
module.

The headers are required to build the module. Including them in the
source distribution is the easiest way to make sure that builds from the
source distribution will succeed provided that the Olm C library is
already installed.

Signed-off-by: Damir Jelić <poljar@termina.org.uk>
2019-04-08 15:04:32 -04:00
Damir Jelić f160d693b6 python: Add PK bindings.
This patch adds bindings to the PK part of the Olm library contained in
the pk.h header file.

Encryption, decryption as well as pickling/unpickling of the decryption
object is supported.

Signed-off-by: Damir Jelić <poljar@termina.org.uk>
2019-04-08 15:04:32 -04:00
Damir Jelić 0883a922ff python: Make the typing module a requirement only for old python versions.
Typing is part of standard library from python 3.5 and from python 3.7
onwards trying to install it results in an error.

This patch disables typing installation on python versions that are
newer than 3.5.

Signed-off-by: Damir Jelić <poljar@termina.org.uk>
2019-04-08 15:04:32 -04:00
Hubert Chathi ad024b3347 add missing OlmPkSigning class 2019-04-04 23:11:56 -04:00
Hubert Chathi 3148157ea4 add support for an incorrect KDF that snuck into Riot 1.0 2019-04-02 23:39:05 -04:00
Damir Jelić fcfa5f12a4 python: Expose the sha256() function in the utilities.
Signed-off-by: Damir Jelić <poljar@termina.org.uk>
2019-04-02 12:56:59 +02:00
Damir Jelić 446628753b python: Add Short Authentication String bindings.
This patch adds bindings to the SAS part of the Olm library contained in
the sas.h header file.

Signed-off-by: Damir Jelić <poljar@termina.org.uk>
2019-04-02 12:56:53 +02:00
Hubert Chathi d5c0eb9d20 update unit test to match function name change 2019-03-28 13:38:08 -04:00
Hubert Chathi 2f2a19f2e7 add Android bindings for PK signing 2019-03-20 10:32:53 -04:00
Hubert Chathi 0348f06a56 rename functions to be more consistent 2019-03-13 22:39:21 -04:00
Hubert Chathi 2589d1b17b I don't know what an "oml" is 2019-02-15 17:13:07 -05:00
Hubert Chathi fcbedf191f
Merge pull request #81 from matrix-org/dbkr/pk_sign
Add signing class to the pk module
2019-02-01 11:43:09 -05:00
Hubert Chathi 48dda7922d call the right function and remove unnecessary include 2019-02-01 11:39:06 -05:00
David Baker 621097f62b Yay for incorrect comments breaking the build... 2019-01-30 18:18:55 +00:00
David Baker c31ab73704 Drop support for old emscripten
because emscripted has dropped support for the old flag and us
setting it is now breaking the build.
2019-01-30 18:16:48 +00:00
David Baker 8df2ab7c07 Add signing class to the pk module 2019-01-29 20:47:41 +00:00
Damir Jelić 45091c158d python: Turn the signature buffer into a bytearray.
This is a workaround for a bug where signature verification would
overwrite the variable holding the signature.

This only happens on python2.

Signed-off-by: Damir Jelić <poljar@termina.org.uk>
2019-01-23 17:10:44 -05:00
Hubert Chathi 94f664e725
initial implementation of short authentication string generation 2019-01-21 23:21:41 -05:00
Hubert Chathi ec2695b9c9 bump OLMKit version too 2018-10-24 09:32:21 -04:00
Hubert Chathi ff24af601a prepare release 3.0.0 2018-10-23 12:58:10 -04:00
Hubert Chathi 9acfd1791e expose the private key length in the Android sdk 2018-10-23 12:24:49 -04:00
Hubert Chathi b79590e490 document how to build with cmake, and how to build the Python bindings 2018-10-23 12:24:49 -04:00
Hubert Chathi 3ebb3b69de fix capitalization and update Python binding version on release 2018-10-23 12:24:49 -04:00
Hubert Chathi 340a4965c9 include the non-wasm version in the package 2018-10-23 12:24:49 -04:00
Hubert Chathi efbe061153 default to building as a shared library 2018-10-23 12:24:49 -04:00
Hubert Chathi 238c512d20
Merge pull request #75 from matrix-org/manuroe/objc_pk_private_key_length
OLMKit: Expose PK private key length
2018-10-23 10:56:52 -04:00
manuroe c0d118f407 OLMKit: Fix typo in license header 2018-10-23 16:07:34 +02:00
manuroe 4e120a0eeb OLMKit: Fix file name case 2018-10-23 15:55:52 +02:00
manuroe 739f3c0391 OLMKit: Maintenance: Update Podfile.lock 2018-10-23 15:48:53 +02:00
manuroe 59076a6bda OLMKit: Expose PK private key length 2018-10-23 15:47:46 +02:00
Hubert Chathi 1aafac2874
Merge pull request #67 from matrix-org/cmake
CMake support
2018-10-22 11:32:33 -04:00
manuroe 2784e49595
Merge pull request #70 from matrix-org/manuroe/objc_pk
OLMKit: Add objc wrappers for pk encryption/decryption
2018-10-22 08:25:41 +02:00
Hubert Chathi 2cace25fba add functions for dealing with private keys directly 2018-10-19 12:10:36 -04:00
Hubert Chathi 1c7ff7f48d more and improved buffer sanitising for Android bindings 2018-10-19 12:10:11 -04:00
Hubert Chathi c4c3055f83
Merge pull request #71 from matrix-org/js_sanitising
zero buffers in the JavaScript bindings
2018-10-19 11:34:55 -04:00
Hubert Chathi 1d880f9711
Merge pull request #68 from matrix-org/poljar-python
Poljar's improved python bindings
2018-10-19 11:34:11 -04:00
Hubert Chathi 6e6facba3b fix c+p error: don't clobber the decryption object on error 2018-10-17 22:25:10 -04:00
manuroe cc9a97f0cb OLMKit: Zero buffers out in all pickle & unpickle methods 2018-10-17 11:29:34 +02:00
manuroe 90bbdec8ad OLMKit: Zero only critical buffers out 2018-10-17 11:25:20 +02:00
Hubert Chathi 93f764200e zero buffers in the JavaScript bindings 2018-10-16 17:50:34 -04:00
Hubert Chathi 09dc3b6025 document a couple more places where olm clobbers things 2018-10-16 16:13:09 -04:00
Hubert Chathi 22f8649c10 also ignore the non-wasm JS file 2018-10-16 16:11:22 -04:00
Hubert Chathi 0ec6a65858 add more buffer clearing 2018-10-16 15:59:32 -04:00
manuroe 2ea2bc8ebc Fix compilation warning for olm_pk_private_key_length(void); 2018-10-16 15:52:41 +02:00
manuroe 03e0d1909b Remove .DS_Store for git 2018-10-16 15:52:14 +02:00
manuroe 9fd50c8eb5 OLMKit: Add objc wrappers for pk encryption/decryption 2018-10-16 15:51:31 +02:00
Hubert Chathi 5ef6a844d6 overwrite buffers that may contain sensitive data
also reduce the amount of memory copying that we do
2018-10-16 00:31:56 -04:00
Hubert Chathi 357d4ff479 remove unnecessary comments about not enough random 2018-10-16 00:04:45 -04:00
Hubert Chathi 019ff702a0 add license headers to python bindings 2018-10-15 13:54:14 -04:00
Hubert Chathi 1eac1daa47 use new function names in Android bindings 2018-10-15 10:11:47 -04:00
Hubert Chathi 8c1169f0f5 use the correct error code 2018-10-15 10:11:47 -04:00
Hubert Chathi 46f80e71be remove fuzzers from cmake, since it doesn't work properly 2018-10-12 21:03:23 -04:00
Hubert Chathi af6c3ca5ce add dummy header files for generating cffi function list 2018-10-12 20:27:40 -04:00
Hubert Chathi 6c2f136bc9 remove code coverage and TravisCI badges
since they're not for the right repository any more
2018-10-12 19:19:19 -04:00
Hubert Chathi 718763f8fc build and test improvements
- build both Python2 and Python3 libs by default, and add separate rules
  building Python2 and Python.
- use the libraries as built by setuptools, rather than building again
  separately
2018-10-12 19:11:19 -04:00
Hubert Chathi 29b021b183 remove redundant license file 2018-10-12 18:55:37 -04:00
Hubert Chathi 04bbd728ba always use files from ../include and ../build 2018-10-12 18:55:05 -04:00
Hubert Chathi d4b2cce603 generate python/include/olm/olm.h automatically 2018-10-12 18:53:18 -04:00
Hubert Chathi 5cf074d337 Merge branch 'master' into poljar 2018-10-12 17:02:51 -04:00
Hubert Chathi 3da5b60823 add pk files to cmake, avoid some duplication, and update documentation 2018-10-12 16:22:12 -04:00
Konstantinos Sideris 4e94dfc7e0 Add CMake support
The library can now be installed using CMake v3.0+.

Below is an example configuration.

1. Generate configuation

cmake -H. -Bbuild
    -GNinja
    -DCMAKE_BUILD_TYPE=Release // The default profile.
    -DCMAKE_INSTALL_PREFIX=/usr/local/
    -DBUILD_SHARED_LIBS=ON
    -DOLM_TESTS=1
    -DOLM_FUZZERS=1

2. Build & install the targets

cmake --build build --config Release --target install

3. Run the tests

cd build/test && ctest .

The library can also be used as a dependency with CMake using

find_package(Olm::Olm REQUIRED)
target_link_libraries(my_exe Olm::Olm)

Signed-off-by: Konstantinos Sideris <sideris.konstantin@gmail.com>
2018-10-12 16:22:03 -04:00
Hubert Chathi af86a9a8b8 clear out plaintext buffers in Android SDK where possible 2018-10-12 15:55:36 -04:00
David Baker b1130fb77f
Merge pull request #61 from matrix-org/dbkr/pk_private_export_import
Work with PkDecryption keys by their private keys
2018-10-12 08:24:45 +01:00
David Baker 50ed20f61e Merge remote-tracking branch 'origin/master' into dbkr/pk_private_export_import 2018-10-12 08:22:57 +01:00
David Baker b2d91f55ec
Merge pull request #62 from matrix-org/dbkr/ci2
CircleCI Build Support
2018-10-12 08:13:45 +01:00
David Baker fac1d52dfe Add aliases for deprecated functions 2018-10-11 18:16:39 +01:00
David Baker e73ebcea67 Merge remote-tracking branch 'origin/master' into dbkr/pk_private_export_import 2018-10-10 20:15:40 +01:00
David Baker 82534708a3 Merge remote-tracking branch 'origin/master' into dbkr/ci2 2018-10-10 20:08:55 +01:00
David Baker 3cfcf1615d
Merge pull request #57 from matrix-org/dbkr/wasm
WebAssembly support
2018-10-10 20:07:19 +01:00
David Baker 1dbb060c44 Add note about passing through env var with docker 2018-10-10 19:41:12 +01:00
Hubert Chathi 44f78cf4fe
Merge branch 'master' into dbkr/wasm 2018-10-09 17:38:32 -04:00
David Baker 713e9aeb4d Build on mac 2018-10-05 15:35:21 -04:00
Hubert Chathi 2cbba061c3
Merge branch 'master' into dbkr/ci2 2018-10-05 14:54:29 -04:00
Hubert Chathi f6e3f7f44a
Merge branch 'master' into dbkr/pk_private_export_import 2018-10-05 14:52:01 -04:00
Hubert Chathi 0a25ec137f
Merge branch 'master' into dbkr/wasm 2018-10-05 10:50:01 -04:00
Hubert Chathi 173339ae9a add more comments describing the pk encrypt/decrypt functions 2018-10-05 10:35:09 -04:00
Hubert Chathi bad14db8da remove unneeded polyfill 2018-10-05 10:35:09 -04:00
Hubert Chathi 8520168e0b fix some code style issues and typos 2018-10-05 10:35:09 -04:00
David Baker 602c00a8d6 Dual-build wasm and asm.js olm 2018-10-04 20:09:54 +01:00
David Baker 031eb2dc75 ...in the right dir 2018-10-03 16:26:17 +01:00
David Baker 8161fa51a8 run npm install 2018-10-03 16:24:21 +01:00
David Baker 877166dedc Merge branch 'dbkr/wasm' into dbkr/ci2 2018-10-03 16:22:13 +01:00
David Baker 3e775938e5 Replace the impenetrable line of perl with python
Mostly because the standard emscripten docker image does not have
libjson-perl, but python always comes with json. But also because
it was impenetrable.
2018-10-03 16:06:15 +01:00
David Baker b1beadacee CircleCI config file 2018-10-03 15:59:45 +01:00
David Baker e521ee84c5 Add an export for the length of a private key 2018-10-02 19:21:05 +01:00
David Baker 9b652bb127
Merge pull request #58 from matrix-org/dbkr/method_consistency
Call appropriate wrapper function
2018-10-02 12:10:29 +01:00
David Baker 8635d68ba8 Add other breaking change 2018-10-02 12:09:33 +01:00
David Baker 0346145a81 Work with PkDecryption keys by their private keys
Change interface to allow the app to get the private part of the
key and instantiate a decryption object from just the private part
of the key.

Changes the function generating a key from random bytes to be
initialising a key with a private key (because it's exactly the
same thing). Exports & imports private key parts as ArrayBuffer at
JS level rather than base64 assuming we are moving that way in
general.
2018-10-02 12:02:56 +01:00
David Baker 00384ba87a Merge remote-tracking branch 'origin/master' into dbkr/wasm 2018-10-02 10:15:14 +01:00
David Baker 04f58bb0c9
Merge pull request #59 from matrix-org/dbkr/fix_trailing_newlines
Remove trailing letter 'K's from the test pubkeys
2018-10-02 10:04:14 +01:00
David Baker 2835110cee Remove trailing letter 'K's from the test pubkeys
base64 encoded newlines somehow?
2018-10-01 20:01:47 +01:00
David Baker b51d75392b Merge remote-tracking branch 'origin/master' into dbkr/wasm 2018-10-01 16:14:39 +01:00
David Baker 0ad32c9896 Call appropriate wrapper function
Don't think this matters since there's no PkEncryption /
PkDecryption object being passed, but for the sake of consistency
2018-10-01 13:22:04 +01:00
Matthew Hodgson 8f6e0557ee oops, fix typo - thanks to @dest4 2018-09-27 18:45:00 +01:00
David Baker c4a3918686 Support passing olm options into init() 2018-09-26 16:38:39 +01:00
David Baker 498562fa65 Breking change 2018-09-25 18:03:31 +01:00
David Baker dfbe8a4796 Return same promise if init() called many times
So we only init the library once.
2018-09-25 17:48:17 +01:00
David Baker 263b94428a Another day, another interface
Change the interface again, hopefully this time a bit more normal.
Now we wrap the emscripten module completely and just expose the
high level objects.

The olm library export is now imported as normal (ie. returns
a module rather than a function returning a module) but has an
`init` method which *must* be called. This returns a promise
which resolves when the module is ready. It also rejects if the
module failed to set up, unlike before (and unlike the
promise-not-a-promise that emscripten returns).

Generally catch failures to init the module.
2018-09-25 17:13:29 +01:00
David Baker f29d8cdd7b Also ship the wasm file 2018-09-21 16:39:04 +01:00
David Baker 5e87db615a Make OLM_OPTIONS work again
The closure compiler was just renaming the variable so it never
would have picked them up. Make it an extern so it knows what to do.
2018-09-21 16:35:17 +01:00
David Baker 122867c45c WebAssembly support!
Quite a lot going on in this PR:
 * Updates to support recent emscripten, switching to WASM which is now the default
 * Use emscripten's MODULARIZE option rather than wrapping it ourself, since doing
   so in pre-post js doesn't work anymore.
 * Most changes are moving the emscripten runtime functions to top-level
   calls rather than in the Module object.
 * Get rid of duplicated NULL_BYTE_PADDING_LENGTH
 * Fix ciphertext_length used without being declared
 * Fix things that caused the closure compiler to error, eg. using
   OLM_OPTIONS without a declaration.
 * Wait until module is inited to do OLM_ERROR = olm_error()

The main BREAKING CHANGE here is that the module now needs to initialise
asyncronously (because it has to load the wasm file). require()ing olm
now gives a function which needs to be called to create an instance.
The resulting object has a promise-like then() method that can be used
to detect when the module is ready. (We could use MODULARIZE_INSTANCE
to return the module directly as before, rather than the function,
but then we don't get the .then() method).
2018-09-21 16:01:51 +01:00
David Baker d6cd18df40
Merge pull request #55 from matrix-org/dbkr/pk_key_length
Fix output buffer length check
2018-09-20 11:30:27 +01:00
David Baker 65d4ac19c8 Fix output buffer length check
...when generating a key in PkDecryption.

The pubkey is base64ed on the output, so will be longer.
2018-09-19 14:10:12 +01:00
Hubert Chathi ed02c217e6 update address for sending feedback for olm 2018-08-15 22:03:48 -04:00
Damir Jelić ac071d9c0d python: Enable build with the local build of the Olm C library.
This patch adds the ability to build the bindings without having a
globally installed Olm C library.

Provided that the C library is already built, the tests can be run now
with make test.

Signed-off-by: Damir Jelić <poljar@termina.org.uk>
2018-07-18 17:44:32 -04:00
Damir Jelić e3d6673371 python: Import improved python bindings.
This commit imports the python bindings from:
    https://github.com/poljar/python-olm

The bindings are imported at commit c44b145818520d69eaaa350fb95afcb846125e0f

Minor modifications were made while importing:
    - Removed travis config
    - Removed Arch Linux PKGBUILD
    - Removed the html docs, they can be rebuild by running make html in
      the docs folder
    - Slightly modified the README

The new bindings feature some improvements over the old ones:
    - Python 2 and 3 support
    - Automatic memory management
    - Automatic memory clearing before it is freed
    - Type signatures via the python typing module
    - Full test coverage
    - Properties are utilized where it makes sense (e.g. account.id)

Signed-off-by: Damir Jelić <poljar@termina.org.uk>
2018-07-18 17:44:32 -04:00
Damir Jelić 2fccf44015 python: Remove the python bindings.
Signed-off-by: Damir Jelić <poljar@termina.org.uk>
2018-07-18 17:44:32 -04:00
Richard van der Hoff 0dc27f73c4 Request patches to olm@matrix.org 2018-07-09 11:53:13 +01:00
Hubert Chathi 98e8ee1b0d prepare 2.3.0 2018-07-04 15:24:44 -04:00
Hubert Chathi dac2c1064e use void in type signatures for functions with no arguments 2018-06-28 17:13:52 -04:00
Hubert Chathi f709b062bb add functions for pickling/unpickling a decryption object 2018-06-28 17:10:36 -04:00
Hubert Chathi 3ed0ec226c add termination 2018-06-28 17:03:46 -04:00
Hubert Chathi b91c77721d improve public key encryption unit test 2018-06-28 15:11:53 -04:00
Hubert Chathi 307309c69b add initial version of Android wrapper for public key API 2018-06-27 18:49:48 -04:00
Hubert Chathi 552da6eafe use the correct method to get the random length 2018-06-27 17:36:55 -04:00
Hubert Chathi 128d45cc83 add initial implementation of basic private key encryption functionality 2018-06-27 16:38:45 -04:00
Hubert Chathi 6a2a2741e8 fix some comments 2018-06-27 16:35:15 -04:00
Benoit Marty 28e0c4baca Improve .gitignore: exclude generated files for test 2018-06-27 14:00:20 -04:00
Benoit Marty 6f2145337b Comment out proxy settings 2018-06-27 14:00:20 -04:00
Benoit Marty 44c5923522 Upgrade tools
gradle 2.14.1 to 4.7
build tools 21.1.2 to 27.0.3
build gradle 2.1.3 to 3.1.3
2018-06-27 14:00:20 -04:00
Benoit Marty ed7649d71e Compiling with last NDK + some hardening
Patch received in https://github.com/matrix-org/olm/issues/13
2018-06-27 14:00:20 -04:00
Benoit Marty 08e50693b7 ignore generated files 2018-06-27 14:00:20 -04:00
Benoit Marty 9550d6cebc Fix null pointer dereference
Patch has been received by e-mail, Signed-off-by: Arnaud Fontaine <arnaud.fontaine@ssi.gouv.fr>
2018-06-27 14:00:20 -04:00
Benoit Marty 570e3a706f Add a .gitignore file specific for Android project 2018-06-27 14:00:20 -04:00
manuroe 36fd68c818 OLMAccount: Fix use of object after its memory was released 2018-06-27 12:26:44 -04:00
manuroe 20ee3858b9 Update Xcode test project 2018-06-27 12:26:26 -04:00
manuroe 2e84f7552a Fix obj-c warnings 2018-06-27 12:26:01 -04:00
manuroe 9d81046f90 Fix warnings reported by LLVM 2018-06-27 12:25:27 -04:00
Hubert Chathi ddc981c475 fix a length check and add some missing length checks 2018-06-27 12:14:19 -04:00
Richard van der Hoff 6d86835421 Update releasing instructions 2018-06-22 13:58:48 +01:00
Wilfried Klaebe 62b576b903 Python: add binding for olm_remove_one_time_keys
Signed-off-by: Wilfried Klaebe <w+gitstuff@chaos.in-kiel.de>
2018-05-29 11:41:25 +01:00
MTRNord 4065c8e11a Update Makefile to support building on Windows
The code for this change is taken from
https://stackoverflow.com/a/30225575/4929236

This patch is Signed-Off-By: Marcel Radzio <marcel@radzio-sh.de>
2018-04-12 10:51:16 +01:00
Hubert Chathi 3f5b9dd6d7 patch for libolm: fix some typos in JavaScript library
From 4a82d31e8cb808a04956fc847ed0ec0ff322b956 Mon Sep 17 00:00:00 2001
From: Hubert Chathi <hubert@uhoreg.ca>
Date: Wed, 3 Jan 2018 21:37:43 -0500
Subject: [PATCH] fix some typos in JavaScript library

Signed-off-by: Hubert Chathi <hubert@uhoreg.ca>
2018-01-09 15:57:47 +00:00
Richard van der Hoff 18b067a46f jenkins.sh: Run python bits in a virtualenv
because we may not have pep8 installed at the system level
2017-11-22 16:57:50 +00:00
Alexey Rusakov 0fd406cca8 Drop unused #include
Signed-off-by: Alexey Rusakov <ktirf@users.sf.net>
2017-09-29 09:35:04 +01:00
Alexey Rusakov 3c33180fe3 Avoid C99 inside C++ code
This disrupts building at least with Visual Studio.

Signed-off-by: Alexey Rusakov <ktirf@users.sf.net>
2017-09-29 09:35:04 +01:00
Hubert Chathi 0fd768e1b5 update link in README to match changes in 780d83a
Signed-off-by: Hubert Chathi <hubert@uhoreg.ca>
2017-06-08 11:31:39 +01:00
Eli Flanagan 780d83a1d8 update link 2017-06-07 10:10:39 +01:00
Matthew Hodgson b7dcbfdf64 add audit notes 2017-05-08 11:35:50 +01:00
Richard van der Hoff 7de2931509 Enforce PEP8 in jenins build 2017-04-24 13:14:46 +01:00
Richard van der Hoff f8c61b8f84 Python: Make ed25519_verify take some arguments
It's not much use if everything is hardcoded.
2017-04-24 13:14:36 +01:00
Richard van der Hoff 853ea8fbc7 Merge branch 'master'
Merge master into patched branch to fix conflicts prior to merge back to master
2017-04-24 12:35:17 +01:00
Richard van der Hoff 1225de14d7 PEP8
Fix line lengths in newly-added code
2017-04-24 12:34:28 +01:00
pik a3e5beab89 Add ed25519_verify to __init__.py and add test for ed25519_verify
Signed-off-by: Alexander Maznev <alexander.maznev@gmail.com>
2017-04-24 12:29:45 +01:00
pik e632bc9e52 Add utility module to olm/python - for ed25519_verify
Signed-off-by: Alexander Maznev <alexander.maznev@gmail.com>
2017-04-24 12:29:45 +01:00
pik 51840d82dc Return python int instead of c_uint32 for InboundGroupSession.decrypt message_index
Signed-off-by: Alexander Maznev <alexander.maznev@gmail.com>
2017-04-18 19:15:51 +01:00
Richard van der Hoff ed6ebb9a4d PEP8 for python bindings
make the python code adhere to PEP8
2017-04-18 19:07:56 +01:00
J08nY 001dc1edaa Python: Switch to a more general os.urandom for randomness source
Signed-off-by: Jan Jancar <johny@neuromancer.sk>
2017-04-04 10:45:11 +01:00
Richard van der Hoff bb05b5687f Makefile: fix tab/space substitution. 2017-03-29 10:44:54 +01:00
Andreas Zwinkau b0a010b153 Support building as a static library
Signed-off-by: Andreas Zwinkau <qznc@web.de>
2017-03-24 10:20:49 +00:00
Matthew Hodgson 793cec0268 bug reports 2017-03-13 11:55:33 +00:00
Sophie Taylor 7b937236ae Fixing Android.mk
Signed-off-by: Sophie Taylor <sophie@spacekitteh.moe>
2017-03-06 14:12:57 +00:00
Yannick LE COLLEN a9aeb6b5d7 Android : improve the version number management (#46)
Add functions to make the difference between the native and the java code version.

Factor out the version management in the makefiles.
2017-03-02 12:01:32 +00:00
Richard van der Hoff 77eaaa3d5f prepare v2.2.2 2017-03-01 16:21:37 +00:00
Greg Hughes 86c6af943a OLMKit: Fixed type of messageIndex argument in exportSessionAtMessageIndex
Signed-off-by: Greg Hughes <greg@ghughes.com>
2017-02-27 17:24:17 +00:00
Richard van der Hoff b185229c2b Prep v2.2.1 2017-01-18 18:16:32 +00:00
Richard van der Hoff 1014712fd0 Fix OLMKit.podspec 2017-01-18 18:02:11 +00:00
Richard van der Hoff 150c360e82 Bump version numbers to 2.2.0 2017-01-18 16:03:02 +00:00
Richard van der Hoff 972798d1b7 Prep changelog for 2.2.0 2017-01-18 15:48:48 +00:00
Yannick LE COLLEN 3c02c1547c Android: Add wrappers for export/import of inbound group sessions 2017-01-18 14:33:14 +00:00
Richard van der Hoff 1761730db8 Swift project support
Merge patch required to support Swift projects, from Avery Pierce.

Patch taken from https://github.com/matrix-org/matrix-ios-sdk/files/712987/olm_patch.txt.

Sign-off at https://github.com/matrix-org/matrix-ios-sdk/pull/220#issue-201470911.
2017-01-18 10:46:59 +00:00
Avery Pierce 757be9aeba OLMKit: Change OLMKitVersionString from a C function to an Obj-C static method 2017-01-18 10:46:37 +00:00
Avery Pierce 4373ac3ef6 OLMKit: Exclude private headers from pod library 2017-01-18 10:46:37 +00:00
Richard van der Hoff a45c3cc809 Merge pull request #44 from matrix-org/manuroe/olmkit-backup
OLMKit: Add wrappers for export/import of inbound group sessions
2017-01-17 15:16:16 +00:00
manuroe ded15597c6 OLMKit: Reset intermediate buffer to zeroes 2017-01-17 16:14:22 +01:00
manuroe 885b85f516 OLMKit: Add wrappers for export/import of inbound group sessions 2017-01-17 14:47:41 +01:00
Richard van der Hoff c14f4b28b9 Update podspec to support macOS
Merged from https://github.com/aapierce0/OLMKit/commits/macOS_port

Sign-off taken from https://github.com/matrix-org/matrix-ios-sdk/pull/218#issue-201062079.
2017-01-17 10:08:50 +00:00
Richard van der Hoff c0c307c05e Add a CONTRIBUTING file 2017-01-17 10:07:45 +00:00
Richard van der Hoff 8fc7f47011 More README fixes 2017-01-17 10:03:13 +00:00
Richard van der Hoff d1226e1865 link to local docs/olm.rst 2017-01-17 10:00:33 +00:00
Richard van der Hoff 199a4e7061 Fix Android build instrucitons 2017-01-17 09:59:30 +00:00
Avery Pierce b2b93d7a1f Strip down to Foundation (no need to include UIKit or Cocoa) 2017-01-14 20:13:35 -06:00
Avery Pierce 30851905b5 Add macOS support to the Podspec 2017-01-14 14:57:46 -06:00
Richard van der Hoff 860740a91e Merge pull request #42 from matrix-org/rav/megolm_export
Export and import of megolm session data
2017-01-10 15:39:42 +00:00
Yannick LE COLLEN 14c30da0e2 Merge pull request #43 from matrix-org/pedroc/android_e2e_dev
Android wrappers for olm library
2017-01-10 16:09:18 +01:00
ylecollen ccbb9606b7 Move the android project from /Java/Android/OlmLibSdk 2017-01-10 16:06:21 +01:00
Richard van der Hoff c04b770cd3 Add some tests for inbound session import/export 2017-01-10 14:11:42 +00:00
ylecollen 8ea5bc7960 Merge remote-tracking branch 'origin/pedroc/android_e2e_dev' into pedroc/android_e2e_dev 2017-01-10 14:45:30 +01:00
ylecollen 03ae28e087 Update after a new review 2017-01-10 14:45:20 +01:00
Richard van der Hoff 4323bec5d6 README.rst: fix formatting 2017-01-10 11:32:52 +00:00
Richard van der Hoff fc72c732fd Store a flag indicating if the sender key is verified 2017-01-10 10:57:14 +00:00
ylecollen eeb210f733 -> Check the function descriptions
-> Update the deserialization methods (the jni methods create an object instance before deserializing them.
2017-01-10 11:40:57 +01:00
Richard van der Hoff a2f0c93a93 Implement importing group session data
olm_import_inbound_group_session, which reads the format written by
olm_export_inbound_group_session to initialise a group session.
2017-01-09 17:45:46 +00:00
ylecollen 29339bc00a Update getSdkOlmVersion to provide the git commit 2017-01-09 16:39:54 +01:00
ylecollen a14bf30c43 add sanity checks when releasing the objects 2017-01-09 15:29:23 +01:00
ylecollen 26a7ef8ddc Clear some data before releasing them 2017-01-09 15:12:15 +01:00
ylecollen 99ff3d15c8 remove the useless malloc(len+1) + zero terminated 2017-01-09 15:03:15 +01:00
ylecollen 30c8d069bc The crypto objects are now saved as String to keep the backward compliancy. 2017-01-09 13:56:41 +01:00
ylecollen 7bf7a7e415 use the same way to name the creation method i.e. createNewXX. Avoid the initWithXX. 2017-01-09 13:55:58 +01:00
ylecollen 13d3f4a1c7 The serialization / deserialization unit test did not fail when there was an error 2017-01-09 13:55:04 +01:00
ylecollen f257580cb1 verifyEd25519Signature triggers an exception instead of filling an error buffer 2017-01-09 10:25:18 +01:00
ylecollen 0339610269 Simplify the function declarations in olm_jni to avoid having to cast the getXXX methods 2017-01-09 10:01:01 +01:00
Richard van der Hoff 5fbeb3e29b Enable exporting inbound group session keys
A pair of functions which allow you to export the megolm keys for an inbound
group session, so that an application can save/restore them.
2017-01-06 16:41:56 +00:00
ylecollen eec8a50caf Move the exception test 2017-01-05 10:16:14 +01:00
ylecollen 90c55806c0 fix an invalid test 2017-01-05 09:42:25 +01:00
ylecollen 9df5dd9c42 The olm objects are serialized as byte[] instead of strings. 2017-01-04 18:30:35 +01:00
ylecollen 7f6a63068b Fix a C compilation warning. 2017-01-04 14:49:51 +01:00
ylecollen 4ca8086a39 OlmSession triggers exception when there is a failure. 2017-01-04 14:35:08 +01:00
ylecollen 846ea49a98 the OlmOutboundGroupSession methods trigger an exception when they fail 2017-01-04 13:13:13 +01:00
ylecollen 261c08086c the OlmInboundGroupSession methods trigger an exception when they fails 2017-01-04 12:35:15 +01:00
ylecollen 88548f687e OlmAccount methods trigger an exception when they fail. 2017-01-04 11:46:37 +01:00
ylecollen 570e8bbe93 use secureRandom in getRandomKey 2017-01-04 09:07:12 +01:00
ylecollen ce9f67d5be Simplify the serialization / deserializtion methods (CommonSerializeUtils) 2017-01-04 08:57:41 +01:00
ylecollen 2070de4fc5 initInboundSessionWithAccount triggers an exception when it fails. 2017-01-03 17:20:18 +01:00
ylecollen 45a98c20a8 EncryptMessage triggers an exception when it fails 2017-01-03 16:41:04 +01:00
ylecollen 9552e14fda -> the byte[] to String conversions are done on Java level (when it is possible)
-> remove javaCStringToUtf8
2017-01-03 16:12:20 +01:00
ylecollen 765647cda5 There is more GetStringUTFChars call. 2017-01-03 14:14:56 +01:00
ylecollen de962ef8d7 encryptMessage : the UTF8 conversion is done on JAVA side. 2017-01-03 11:38:43 +01:00
ylecollen e7c7d77a8a Comments update 2017-01-03 11:04:22 +01:00
ylecollen 5e948b1ac4 Add exception checks 2017-01-03 11:04:06 +01:00
ylecollen c8c7a8ad19 -> simplify OlmSession.DecryptMessage
-> Always use javaCStringToUtf8 to convert byte array to String
2017-01-03 10:46:56 +01:00
ylecollen 65352d05aa ->Replace 2 spaces tabs by 4 spaces.
->fix the NULL and 0 pointer comparisons mixes
2017-01-03 09:32:59 +01:00
ylecollen b03cdebfb5 Update the tab size to 4 spaces 2017-01-02 17:10:59 +01:00
ylecollen 5e0bbe7702 Rename getUnreleasedCount to isReleased. 2017-01-02 16:29:44 +01:00
ylecollen 47a52dcf41 Use a 4 spaces tabulation 2017-01-02 16:02:17 +01:00
ylecollen 8f3d5bed72 Fix tabulations mix and split block of variables when it is possible. 2017-01-02 15:32:14 +01:00
ylecollen f4ae0d86c9 getOlmAccountId is aonly public in the OLM SDK package 2017-01-02 15:28:42 +01:00
ylecollen b893b81c82 Simplify signMessageJni 2017-01-02 14:41:54 +01:00
ylecollen 60bcf865d0 remove useless "new string" 2017-01-02 14:19:22 +01:00
ylecollen 2a7c191d84 identityKeys / oneTimeKeys : remove useless try/catch blocks 2017-01-02 14:18:12 +01:00
ylecollen b2b182161e Update the README files. 2017-01-02 14:04:53 +01:00
ylecollen da2e1c5902 setRandomInBuffer : clear tempByteArray content 2017-01-02 14:01:45 +01:00
Richard van der Hoff bd6ab72ca4 Fix jenkins failure
do npm install before running the js tests
2016-12-23 10:40:17 +00:00
Richard van der Hoff 86b64b653a Bump version numbers throughout 2016-12-22 15:03:00 +00:00
Richard van der Hoff ac323b738d Update release process instructions 2016-12-22 15:02:43 +00:00
Richard van der Hoff e71dc46a1e Update CHANGELOG 2016-12-22 14:51:56 +00:00
Richard van der Hoff 90b3613053 Merge pull request #36 from matrix-org/manuroe/olmkit
OLMKit
2016-12-22 14:43:01 +00:00
ylecollen 2593c69a8a Update the copyrights 2016-12-22 11:12:41 +01:00
ylecollen c3eb050be2 signMessage : the utf8 conversion is done on Java side. 2016-12-21 18:37:34 +01:00
ylecollen e17eb69048 create a dedicated file for olm_manager. 2016-12-21 17:47:21 +01:00
ylecollen e6d634f9db move getOlmLibVersionJni to the bottom 2016-12-21 16:31:56 +01:00
ylecollen 2e77e39579 move getOlmLibVersionJni 2016-12-21 16:26:07 +01:00
ylecollen ffb40326ff Fix a potential memory leak. 2016-12-21 15:17:53 +01:00
ylecollen 643165067f setRandomInBuffer uses java.lang.SecureRandom. 2016-12-21 15:10:54 +01:00
ylecollen bacdc3c539 Add an encoding type while converting byte[] to string 2016-12-21 13:06:51 +01:00
ylecollen 1c067b1cb6 remove the RTL support by default. 2016-12-21 13:01:12 +01:00
ylecollen d741c012f3 identityKeys and oneTimeKeys return Map instead of JSON. 2016-12-21 12:58:00 +01:00
Richard van der Hoff fb91b1f182 Merge pull request #41 from matrix-org/rav/js_tests
Add some tests for the Javascript wrappers
2016-12-20 12:03:07 +00:00
manuroe 46ad79517e OLMKit: More zeroing 2016-12-20 11:46:57 +01:00
ylecollen c553d18a9a Update the readme files. 2016-12-20 10:42:06 +01:00
ylecollen 2fab6f946e Update the execution mode 2016-12-20 10:10:52 +01:00
manuroe 3540926b98 OLMKit: Reset intermediate buffers to zeroes 2016-12-19 18:10:37 +01:00
Richard van der Hoff e2e398bd94 Add some tests for the Javascript wrappers
These would have helped avoid the recent FRV.
2016-12-19 09:43:58 +00:00
Richard van der Hoff 819f0d24db Merge pull request #40 from matrix-org/rav/fix_encryption
Avoid buffer overrun on encryption
2016-12-16 15:01:45 +00:00
Richard van der Hoff 8e554ab5ef Avoid buffer overrun on encryption
Make sure we null-terminate encrypted strings before passing them to
UTF8ToString.

This used to work when we allocated the buffer on the stack, because it turns
out that allocate() zeroinits the returned memory. malloc(), of course, does
not.
2016-12-16 14:42:41 +00:00
Richard van der Hoff 7fd63bcac7 Merge pull request #39 from matrix-org/rav/messages_on_heap
Allocate memory for message blobs on the heap
2016-12-15 16:54:39 +00:00
Richard van der Hoff 09b3e1eecd typo 2016-12-15 16:28:30 +00:00
Richard van der Hoff 8356fa37ad zero out plaintext buffers
Avoid leaving copies of the plaintext sitting around in the emscripten heap.
2016-12-15 13:37:34 +00:00
Richard van der Hoff 2e04868c46 Merge pull request #38 from matrix-org/rav/handle_load_exceptions
Better handling of exceptions during loading, and import OLM_OPTIONS
2016-12-14 14:34:39 +00:00
Richard van der Hoff 76610c0a3a Allocate memory for message blobs on the heap
Messages can be very large, so we don't really want to allocate them on the
stack. Switch to using the heap for them, and try to clean up some of the
string handling while we're at it.
2016-12-14 12:05:56 +00:00
Richard van der Hoff 51b141ecb6 Let apps override emscripten settings
Read settings from OLM_OPTIONS to allow apps to configure some options. In
particular, this is useful for setting the heap size.
2016-12-14 11:46:12 +00:00
Richard van der Hoff 1bf807bf33 Better handling of exceptions during loading
If we get an exception during load, don't define half of window.Olm (which
confuses apps).

This is a partial fix to https://github.com/vector-im/riot-web/issues/2726.
2016-12-12 16:52:03 +00:00
pedroGitt ac0ccb224d Update decryptMessage() API with the error message as an output parameter 2016-11-28 11:56:20 +01:00
pedroGitt 0263cd3039 Remove un used code 2016-11-28 11:53:54 +01:00
manuroe aa12cbcac2 OLMKit: Make returned NSError provide the raw olm error string (ex:"UNKNOWN_MESSAGE_INDEX") in their NSLocalizedDescriptionKey.
NSLocalizedFailureReasonErrorKey can contain more contextual information.
2016-11-24 11:45:59 +01:00
pedroGitt d3d3024ea2 Add missing gradle folder 2016-11-24 10:21:12 +01:00
pedroGitt b3c2015616 Rename JNI API: getOlmLibVersion() => getOlmLibVersionJni() 2016-11-23 09:36:46 +01:00
pedroGitt e63be97774 Update with master branch => OLM V2.0.0 2016-11-23 01:20:47 +01:00
pedroGitt 7a6897642b Merge remote-tracking branch 'origin/master' into pedroc/android_e2e_dev 2016-11-23 00:04:58 +01:00
pedroGitt c725a561a2 Add lib version as sufix in the aar file name 2016-11-23 00:01:34 +01:00
manuroe 93926e9047 OLMKit: Fixed warnings in objc wrapper 2016-11-18 11:39:39 +01:00
manuroe 29de7825c9 OLMKit: Update Copyrights 2016-11-17 15:50:23 +01:00
manuroe e19b7f54dc OLMKit: Improve wording 2016-11-17 14:43:04 +01:00
manuroe 32a5424971 OLMKit: Podfile.lock++ 2016-11-17 14:33:41 +01:00
manuroe 0b1ecbff2d OLMKit: Add it to olm from version 2.0.1 2016-11-17 14:03:15 +01:00
manuroe 7ee17a2957 OLMKit: Add missing implementations for matchesInboundSession matchesInboundSessionFrom 2016-11-14 17:35:24 +01:00
manuroe aa70c8afbc OLMKit: Fix warning in OLMUtility 2016-11-14 17:06:34 +01:00
manuroe bc697bf5e2 OLMKit: Fixed type-cast of messageIndex of [OLMInboundGroupSession decryptMessage] for 32 and 64bits platforms 2016-11-14 17:02:56 +01:00
manuroe cf66af6f2e OLMKit: Replaced NSAsserts by NSErrors 2016-11-14 16:54:51 +01:00
pedroGitt 04fd4c5a13 Fix the decrypt issue (crash on V5.1.1) when the message to decrypt contains emojis:
- add an internal specific JNI function (javaCStringToUtf8()) to perform the UTF-8 conversion
- the SDK is configured to enable/disable the use of javaCStringToUtf8()
2016-11-14 11:56:37 +01:00
pedroGitt d944d5fad7 Update initInboundSessionWithAccountFrom() return code:
- return code is a basic error code (0 OK, -1  KO)
Remove TODO comments
2016-11-08 11:25:10 +01:00
pedroGitt 1fd908c72a Light refactoring by using test helper methods getFingerprintKey() and getIdentityKey() 2016-11-08 11:21:45 +01:00
manuroe 27a8c28da4 OLMKit: Update obj-c wrapper to 2.0.0 2016-11-07 17:27:09 +01:00
manuroe 5d1b66c350 Merge remote-tracking branch 'origin/master' into olmkit 2016-11-07 17:21:39 +01:00
pedroGitt c144d3fec5 Update test02AliceToBobBackAndForth() with a last sequence where Alice encrypts and Bob decrypts 2016-11-07 11:40:23 +01:00
pedroGitt 59bb145ceb Add new API to verify object release 2016-11-07 11:06:26 +01:00
pedroGitt c71235d508 Fix javadoc header 2016-11-07 11:01:03 +01:00
pedroGitt e71013b005 Set javadoc level to private 2016-11-07 11:00:01 +01:00
manuroe 62f5280670 OLMKit: podspec: Attempt to fix duplicate symbols because ed25519.c includes .c files 2016-11-04 11:07:36 +01:00
manuroe 9ac937a200 OLMKit: podspec: Attempt to fix duplicate symbols because ed25519.c includes .c files 2016-11-04 11:01:09 +01:00
manuroe 09b14c2b3d OLMKit: podspec: Some files are missing 2016-11-04 10:10:56 +01:00
manuroe 671218dc07 OLMKit: podspec: Some files are missing 2016-11-04 09:53:55 +01:00
manuroe 34998b52c7 OLMKit: Attempt to make podspec work when the pod is downloaded from git 2016-11-03 17:33:31 +01:00
pedroGitt 8041a7e0ef Refactoring serialization flow:
- New class CommonSerializeUtils: helper class dedicated to serialization, to contain common code to all the OlmXXX classes that require to be serialized
- make OlmXXX classes extending CommonSerializeUtils
2016-10-31 12:09:06 +01:00
pedroGitt 7a0d7cc36d Fix OlmException cast issue
- OlmException class extends now from IOException
- update corresponding serializing unit tests
 - update
2016-10-28 10:49:04 +02:00
pedroGitt 0d3c1a2a46 Fix javadoc header comments 2016-10-28 10:02:06 +02:00
pedroGitt 724d9ea20b Add new API to provide the SDK version 2016-10-27 18:47:14 +02:00
pedroGitt 1f1cbf2b3e Add new tests for multiple creations of account and outbound group sessions: check random generation function in JNI works properly 2016-10-27 18:14:04 +02:00
pedroGitt 6348a45515 Introduce a new log file (ENABLE_JNI_LOG) to disable/enable logs in JNI side 2016-10-27 18:02:38 +02:00
pedroGitt 8dbc2b50d5 Remove unsecure logs (keys value were printed)
Rename local parameter properly
2016-10-27 17:54:30 +02:00
pedroGitt 6f6d33c7eb Update test01VerifyEd25519Signing() with a real message 2016-10-27 17:24:16 +02:00
Richard van der Hoff f6c05be8c5 Add a document on signing keys 2016-10-27 11:55:48 +01:00
pedroGitt 31f8fe23c5 Fix random issue: increase random seed precision to micro sec
- previously the random seed was seconds based, and it could originate identical identity keys for different OlmAccount
2016-10-27 11:44:33 +02:00
pedroGitt 6204fcd128 Add javadoc auto generation:
- add a buildJavaDoc task in build.gradle
- update classes javadoc headers
2016-10-26 18:15:37 +02:00
pedroGitt d6824a4f49 Revert loadLibrary as static. 2016-10-26 18:12:38 +02:00
pedroGitt b0fc867bfe Update verifyEd25519Signature() unit test: add a test to detect a key length too small
- add logs in verifyEd25519SignatureJni() to print the key used
2016-10-26 15:08:39 +02:00
pedroGitt b140e48183 Add missing copyright header
Add sanity tests for OlmAccount and OlmSession
Add a first version of MatchInboundSession
2016-10-25 18:21:50 +02:00
pedroGitt 232de794f2 Update return code for initOutboundSessionWithAccount() and initInboundSessionWithAccount():
An error code is now returned, no utility to return the object itself (initial implementation matching iOS)
2016-10-25 18:18:40 +02:00
pedroGitt eb2052ba05 Update due to renaming in olm_account.cpp (maxOneTimeKeysJni() & generateOneTimeKeysJni())
Fix removeOneTimeKeysForSession() default return code
2016-10-25 18:16:19 +02:00
pedroGitt a6401c72e6 Update olm_matches_inbound_session_(from)() return code processing 2016-10-25 18:14:39 +02:00
pedroGitt f3d4789b58 Renaming:
- generateOneTimeKeys => generateOneTimeKeysJni
- maxOneTimeKeys => maxOneTimeKeysJni
2016-10-25 18:13:44 +02:00
Richard van der Hoff 700596b46a Update python wrapper to run against libolm.so.2 2016-10-25 14:50:15 +01:00
Richard van der Hoff 2d7b10a160 Merge branch 'release-v2.0.0' 2016-10-25 14:50:03 +01:00
Mark Haines 7e9f3bebb8 Document the return values for olm_matches_inbound_session 2016-10-25 14:42:10 +01:00
Richard van der Hoff 27c7b4a767 Version bump for 2.0.0 2016-10-25 11:35:20 +01:00
pedroGitt 1af282fb37 Add new file: test helper methods 2016-10-25 10:21:15 +02:00
Richard van der Hoff d02c457da5 Changelog: Mention install-headers 2016-10-24 17:22:43 +01:00
Richard van der Hoff 4367afc65e Prepare changelog for v2.0.0 2016-10-24 16:51:20 +01:00
Richard van der Hoff 64130c1f8b Fix broken fuzzer compilation
fuzz_group_decrypt.cpp got broken by 653790e; fix it up
2016-10-24 16:32:21 +01:00
pedroGitt 6b3cb69ded Fix compiler warnings
- for 32bits platform target
- when debug flag is not defined
2016-10-24 17:21:28 +02:00
pedroGitt acafa69c67 Add possibility to define NDK_DEBUG flag 2016-10-24 17:14:35 +02:00
Richard van der Hoff 05b48086a4 remove redundant svg 2016-10-24 15:52:05 +01:00
pedroGitt 3ec91b849b Add possibility to define NDK_DEBUG flag 2016-10-24 16:51:03 +02:00
Richard van der Hoff 807fec2ebf double_ratchet.svg 2016-10-24 15:17:27 +01:00
pedroGitt c485bf1d53 Update comments 2016-10-24 15:31:59 +02:00
pedroGitt 3f60e62827 Add missing account releaseAccount for test01VerifyEd25519Signing() 2016-10-24 15:30:55 +02:00
Richard van der Hoff d8136096c0 Merge pull request #33 from matrix-org/rav/pickle_length
Return the base64-encoded length of pickles
2016-10-24 10:43:43 +01:00
Mark Haines 8de0f1fbb3 Merge pull request #32 from matrix-org/markjh/replay
Document the potential for message replays and possible mitigations
2016-10-24 10:28:54 +01:00
Richard van der Hoff a7310c5821 Return the base64-encoded length of pickles
make olm_pickle_* return the lengths of the base64-encoded pickles, rather than
the raw pickle. (From the application's POV, the format of the pickle is
opaque: it doesn't even know that it is base64-encoded. So returning the length
of the raw pickle is particularly unhelpful.)
2016-10-24 10:06:06 +01:00
pedroGitt 332d9d0c09 Add serialization for inbound group session
- remove compiler warnings when logs are not enabled
- new getInstanceId() function to refactor code
2016-10-23 23:55:45 +02:00
pedroGitt fae857582c Add serialization for outbound group session 2016-10-21 19:05:20 +02:00
pedroGitt 71f57b79e5 Update android platform to 16 2016-10-21 19:03:18 +02:00
pedroGitt 700b02589c Add getRandomKey() 2016-10-21 19:00:46 +02:00
Richard van der Hoff d1a535861d Merge branch 'rav/clear_random_bufs' 2016-10-21 17:36:06 +01:00
Richard van der Hoff 21ce3491dd Clear random buf in olm_init_outbound_group_session
All the other methods clear their random inputs. This one needs to do the same,
to reduce the risk of the randomness being used elsewhere and leaking key info.
2016-10-21 17:19:59 +01:00
pedroGitt fb87d8feee Serialization for OlmAccount and OlmSession OK 2016-10-21 18:09:20 +02:00
Mark Haines 884ad02413 Spelling 2016-10-21 17:07:26 +01:00
Mark Haines 092bf880f5 s/they've/they have/ 2016-10-21 15:45:33 +01:00
Mark Haines 0a7d4e35cc Reword and s/message index/ratchet index/ 2016-10-21 15:44:53 +01:00
Mark Haines 8c4a11a92d Document the potential for message replays and possible mitigations 2016-10-21 15:13:20 +01:00
Mark Haines 5a98012c0d Merge pull request #31 from matrix-org/markjh/groupmessageindex
Return the message index when decrypting group messages.
2016-10-21 09:57:42 +01:00
pedroGitt 1511962df1 Add serialization feature to OlmAccount
- new JNI API: serializeDataWithKeyJni() and initWithSerializedDataJni()
- update account unit test
- modify OlmAccount constructor API: an exception may be thrown
2016-10-20 17:42:57 +02:00
pedroGitt 8b050e5e1e Local variable renaming 2016-10-20 17:40:20 +02:00
pedroGitt 50bf60499a Update verifyEd25519Signature() to fix error message processing 2016-10-20 17:39:09 +02:00
pedroGitt 09fd7246ea Format update 2016-10-20 17:36:53 +02:00
pedroGitt 0cb45abdf3 Update logs 2016-10-20 17:36:20 +02:00
pedroGitt 867ef94ced First update with serialization mechanism 2016-10-20 14:40:59 +02:00
pedroGitt 250af95330 First update with serialization mechanism 2016-10-20 14:40:10 +02:00
Mark Haines 9a8d2d15d9 Check the message index in the tests 2016-10-20 11:51:56 +01:00
Richard van der Hoff 65b3345317 Merge branch 'master' 2016-10-20 11:43:41 +01:00
Richard van der Hoff 68d98234e1 Merge pull request #30 from matrix-org/rav/misc_docs
Add notes on limitations to megolm spec
2016-10-20 11:42:09 +01:00
Mark Haines 3091dc2b1d Add NULL check for message_index pointer 2016-10-20 11:35:45 +01:00
Mark Haines 653790eacb Return the message index when decrypting group messages.
Applications can use the index to detect replays of the same message.
2016-10-20 09:58:55 +01:00
Richard van der Hoff 5f1b93bd0f s/ephemeral/one-time/ in olm spec
We're standardising on 'one-time keys' as a term for the thing that Bob uploads
for prekey messages.
2016-10-19 19:18:58 +01:00
Richard van der Hoff df04cd509a Add notes on limitations to megolm spec 2016-10-19 19:16:23 +01:00
Richard van der Hoff 23fdc0b0f9 Link to the megolm spec 2016-10-19 19:14:18 +01:00
Richard van der Hoff d48dc81976 Document the unknown key-share attacks and mitigation (#29) 2016-10-19 17:27:24 +01:00
pedroGitt e45c03c9e0 Add new exception message for deserialization 2016-10-19 18:18:12 +02:00
Richard van der Hoff 351b26fa6e Merge pull request #28 from matrix-org/rav/fix_megolm_segfault
Fix a buffer bounds check when decoding group messages
2016-10-19 15:21:07 +01:00
Richard van der Hoff 780203b054 Merge pull request #27 from matrix-org/rav/fuzzers_readme
Add a README for the fuzzers
2016-10-19 15:20:54 +01:00
Richard van der Hoff 1ff64391ed Fix a buffer bounds check when decoding group messages
Fixes a segfault when a group message had exactly the length of the mac +
signature.

Also tweak skipping of unknown tags to avoid an extra trip around the loop.
2016-10-19 15:03:40 +01:00
Richard van der Hoff cada801de5 Add a README for the fuzzers 2016-10-19 14:59:50 +01:00
pedroGitt 5b524efc44 Update due to the de/serialization API 2016-10-19 15:23:19 +02:00
pedroGitt 26d9934fc8 Fix verifyEd25519Signature() API to properly return the error message in case signature verification failed
- update unit tests
2016-10-18 19:02:18 +02:00
pedroGitt a6913c49c2 Fix javadoc header comment 2016-10-18 19:00:23 +02:00
pedroGitt 139402611a Add OlmUtility class
- add unit tests for OlmUtility
- rename OlmGroupTest to OlmGroupSessionTest
- update OlmException
2016-10-18 16:05:28 +02:00
pedroGitt 034fa6be40 - Add new API for OlmUtility
- Introducing namespace AndroidOlmSdk
- Fix logs (function names mixed up)
- Add new check based on the calling java object instance type (IsInstanceOf())
- Fix return value for getXXXInstanceId() in case of failure. Now 0 is returned.
2016-10-18 15:59:36 +02:00
pedroGitt 4ccc45ab0a - Update Javadoc for keys constants definitions
- change return code from removeOneTimeKeysForSession() (direct value from JNI is now returned)
2016-10-17 16:58:19 +02:00
pedroGitt b6cf3f1eec Update Javadoc method headers with direct link to PRE_KEY definition 2016-10-17 16:55:14 +02:00
pedroGitt 4545b7bc19 Fix "invalid address or address of corrupt passed to dlfree" in 32bits platform devices 2016-10-17 16:53:36 +02:00
pedroGitt 7e69d96afc Add extra tests to check if the calling JAVA instance is the expected one 2016-10-17 16:48:29 +02:00
manuroe a9be04fa4b OLMKit: Add [OLMUtility sha256:] 2016-10-17 15:47:52 +02:00
pedroGitt ebfcd03ce5 - Add in/outbound group unit test OK 2016-10-14 18:43:57 +02:00
manuroe 4a2aac5800 OLMKit: Add signature tests 2016-10-14 15:57:12 +02:00
pedroGitt 1028099550 - Add inbound and outbound group sessions
- Modify constructors for inbound and outbound group sessions
- Add new Ecxception class
2016-10-14 15:27:20 +02:00
pedroGitt 57ec6fff88 Temp commit.. adding group session API in progress 2016-10-13 19:21:01 +02:00
manuroe d1060af8f0 OLMKit: Renamed [OLMAccount markKeysAsPublished] into [OLMAccount markOneTimeKeysAsPublished] and implemented it 2016-10-13 16:10:00 +02:00
pedroGitt 147df845d0 Enbale all ABI platforms 2016-10-13 14:39:44 +02:00
pedroGitt 572c7cd464 Renaming JNI API decryptMessage in decryptMessageJni 2016-10-13 10:44:59 +02:00
pedroGitt e59ee33b67 Add missing copyright headers 2016-10-13 10:33:43 +02:00
pedroGitt 618eab0086 Update SDK JAVA API making initNewAccount() private and called in the respective constructors 2016-10-13 10:30:08 +02:00
pedroGitt 42c85adbc4 Update function API signatures with macros 2016-10-13 10:27:24 +02:00
pedroGitt 293a12a138 Fix warning compiler 2016-10-13 10:26:27 +02:00
pedroGitt 502de788f4 Merge remote-tracking branch 'origin/pedroc/android_e2e' into pedroc/android_e2e_dev 2016-10-13 09:36:43 +02:00
PedroGitt f88ee7677c - Fix encrypt API (update lencrypted ength)
- Fix warning compiler
2016-10-13 00:19:47 +02:00
pedroGitt 1679c4513f Temp commit: debug in progress 2016-10-12 19:04:50 +02:00
manuroe 3cb01fd279 OLMKit: Fixed typo in test 2016-10-11 16:54:56 +02:00
pedroGitt f2ca1ce304 - Add OlmSession unit test
- Simplify JNI function signatures definition (Account & Session)
2016-10-11 15:53:49 +02:00
manuroe 7ae6410f37 OLMKit: Add tests for OLMInboundGroupSession and OLMOutboundGroupSession 2016-10-10 18:01:02 +02:00
manuroe 2bd912990f OLMKit: Add megolm api: OLMInboundGroupSession and OLMOutboundGroupSession 2016-10-10 17:58:22 +02:00
pedroGitt 67f7939470 - Add decryptMessageJni() to olm_session.cpp API
- review comments header
- refactor utility functions
2016-10-07 17:35:27 +02:00
pedroGitt 655c841cc3 - Update Unit tests for OlmAccount
- new file olm_utility.cpp to have a stand alone function to initialize/alloc a random buffer
 - new class OlmMessage
 - complete OlmSession API with encryptMessage()
 - comments review
 - OlmAccount unit tests are green
 - new gradle to compile the shared lib according to debug mode
2016-10-06 19:55:03 +02:00
pedroGitt 0393ad6843 Update Account unit tests 2016-10-06 10:30:24 +02:00
pedroGitt 573713dd00 - Add missing file 2016-10-06 08:40:21 +02:00
pedroGitt 5573d3ab23 First commit adding Olm Lib for Android
- Add Android Studio project
2016-10-05 18:25:09 +02:00
pedroGitt 3136826e02 First commit on depo
- Add file
2016-10-05 15:31:52 +02:00
Matthew Hodgson 38acc352a3 fix missing ctypes function signatures
These missing signatures were causing OSX to truncate 64-bit pointers
to 32-bit pointers when calling the missing methods, causing segfaults
2016-10-02 02:50:52 +01:00
Matthew Hodgson 68ec41f8ca s/PCKS/PKCS/ 2016-10-02 00:48:06 +01:00
Matthew Hodgson 63800ad8e6 s/PCKS/PKCS/ 2016-10-02 00:47:29 +01:00
manuroe 2ca67ace60 OLMKit: OLMAccount: Replace a olm_session_last_error by olm_account_last_error 2016-09-30 08:16:58 +02:00
Matthew Hodgson 6d80d934cd typo 2016-09-28 18:49:56 +01:00
manuroe 45ecaaedd1 OLMKit: Add [OLMUtility ed25519Verify] 2016-09-28 16:07:39 +02:00
manuroe f29eabde8b OLMKit: Use the same version as libolm 2016-09-28 16:06:45 +02:00
manuroe 103de50518 OLMKit: Implement missing [OLMAccount signMessage:] 2016-09-28 16:06:11 +02:00
manuroe 6f113dd7b3 OLMKit: Make the project build
Make OLMKit CocoaPods expose the obj-c wrapper of libolm
2016-09-27 14:07:30 +02:00
manuroe 1d06f2a4d9 Merge remote-tracking branch 'OLMKit/olmkit' into olmkit
# Conflicts:
#	.gitignore
#	include/olm/olm.hh
#	javascript/README.md
2016-09-27 08:30:33 +02:00
Richard van der Hoff 8a8d100ee5 Makefile: install-headers is phony 2016-09-23 12:22:34 +01:00
Emmanuel Gil Peyrot 7c9b2f6395 Add a Makefile rule to install the headers
Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
2016-09-23 12:17:44 +01:00
Richard van der Hoff 6ea9fb450e Merge branch 'rav/megolm_spec' 2016-09-23 11:55:28 +01:00
Richard van der Hoff 4049261867 megolm.rst: link to protobuf spec 2016-09-23 11:54:35 +01:00
Richard van der Hoff 6449c90d81 More review feedback 2016-09-22 18:23:43 +01:00
Richard van der Hoff 97c991ffee Merge branch 'rav/python_fixes' 2016-09-22 16:54:29 +01:00
Richard van der Hoff b411f07df9 InboundGroupSession.init no longer requires a separate message_index 2016-09-22 16:24:03 +01:00
Richard van der Hoff cfcee54a81 Handle non-base64 chars in pickle files 2016-09-22 15:09:40 +01:00
Richard van der Hoff fc6688c4c8 megolm.rst: review feedback
Split ratchet algorithm out to a separate section.

Also clean up some phrasing and correct a typo or two.
2016-09-22 13:32:03 +01:00
Richard van der Hoff 182eccc624 megolm.rst: linkify Ed25519 2016-09-22 11:38:33 +01:00
Richard van der Hoff ec91dd4570 Megolm.rst: add Background and footer 2016-09-22 11:35:23 +01:00
Richard van der Hoff c07444d3e6 First stab at a megolm spec 2016-09-22 11:04:19 +01:00
Mark Haines 4106767fd0 Merge pull request #25 from matrix-org/markjh/fix_jenkins
Fix jenkins.sh
2016-09-16 17:38:18 +01:00
Mark Haines 8acf8fd367 Fix jenkins.sh 2016-09-16 17:34:21 +01:00
Richard van der Hoff 4ff663a0ae Merge pull request #24 from matrix-org/rav/one_time_keys_comment
Update comment on olm_account_one_time_keys
2016-09-16 14:21:15 +01:00
Richard van der Hoff 76aad9d9c3 Update comment on olm_account_one_time_keys
Document what it actually returns.
2016-09-16 14:05:48 +01:00
Mark Haines 047927d822 Fix the release instructions 2016-09-14 14:16:51 +01:00
Mark Haines ec7d968623 Changelog and version bump for 1.3.0 2016-09-14 13:55:54 +01:00
Mark Haines 7647555fc7 Make release instructions more C+Pable 2016-09-14 11:48:15 +01:00
Mark Haines 6dfa64342a Fix the group javascript demo 2016-09-14 11:10:05 +01:00
Mark Haines f274adfe24 Merge remote-tracking branch 'matrix/master' 2016-09-13 17:55:21 +01:00
Mark Haines d7bc00c81d Merge pull request #23 from matrix-org/markjh/remove_message_index
Remove the messsage index from olm_init_inbound_group_session
2016-09-13 17:54:14 +01:00
Mark Haines 1a50a4b3a0 Merge pull request #22 from matrix-org/markjh/inbound_group_session_id
Add a olm_inbound_group_session_id method
2016-09-13 17:54:07 +01:00
Mark Haines a628ef41bd Remove the messsage index from olm_init_inbound_group_session since it is read from the session_key 2016-09-13 17:51:02 +01:00
Mark Haines 71bcaa5d45 Add a test to check the equivalence of session ids for inbound and outbound sessions 2016-09-13 17:15:28 +01:00
Mark Haines 6971f54fea Add a olm_inbound_group_session_id method 2016-09-13 17:02:36 +01:00
Mark Haines e0b51971b7 Merge pull request #21 from matrix-org/markjh/fix_session_ids
Use the ed25519 public key as the group session id.
2016-09-13 17:02:12 +01:00
Mark Haines a89a169c89 ``if`` is not the same as ``for`` 2016-09-13 17:00:54 +01:00
Mark Haines 5926a8fd29 Comment on the encoding of the message counter. 2016-09-13 16:45:54 +01:00
Mark Haines d62e344db7 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.
2016-09-13 15:42:47 +01:00
Matthew Hodgson 576c6ad292 call double ratchet what it is 2016-09-06 22:46:58 +01:00
Richard van der Hoff 49ca6aca98 Bump version numbers to 1.2.0 2016-09-06 22:26:24 +01:00
Richard van der Hoff 976495e0ac Merge pull request #20 from matrix-org/rav/prep_v1.2.0
Prepare changelog for v1.2.0
2016-09-06 22:22:46 +01:00
Richard van der Hoff b5c65bed0a Prepare changelog for v1.2.0 2016-09-06 17:04:37 +01:00
Richard van der Hoff 79485b2230 Merge pull request #19 from matrix-org/rav/megolm_signing
Sign megolm messages
2016-09-06 15:53:06 +01:00
Richard van der Hoff 2fc83aa9ac 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.
2016-09-06 15:26:26 +01:00
Richard van der Hoff 50cd2b2a43 Clean up some typos
Remove redundant args from some js funcs, and fix a comment typo
2016-09-06 14:06:43 +01:00
Richard van der Hoff 7c84ce8098 Merge pull request #18 from matrix-org/rav/pickle_ed25519_in_c
Convert ed25519 pickling functions to C
2016-09-06 11:08:04 +01:00
Richard van der Hoff 8912d13b0b Merge pull request #17 from matrix-org/rav/group_session_error_handling
Fix error handling for group sessions
2016-09-06 11:07:53 +01:00
Richard van der Hoff c2b51207ee Fix error handling for group sessions
Fix a couple of places where we were using the wrong method to get the last
error.
2016-09-05 19:42:04 +01:00
Richard van der Hoff 617f9b1696 remove js package before building 2016-09-05 18:42:18 +01:00
Richard van der Hoff e7996e5984 pack the js after building 2016-09-05 18:40:37 +01:00
Richard van der Hoff 099757dc8f Build the JS wrappers on jenkins 2016-09-05 16:35:22 +01:00
Richard van der Hoff 833ecd3c73 Convert ed25519 pickling functions to C
... so that I can use them from the group session bits.
2016-09-05 12:59:12 +01:00
Richard van der Hoff c09aa77c4a Avoid ldconfig in the Makefile
... because OSX doesn't support it.
2016-09-05 11:56:53 +01:00
Richard van der Hoff 057ab15c1a Merge pull request #14 from matrix-org/rav/convert_crypto_to_c
Convert crypto.hh into C-compatible interface
2016-09-05 10:42:09 +01:00
Richard van der Hoff 69f269ffaf Convert AES functions to plain C 2016-09-05 10:40:39 +01:00
Richard van der Hoff f0acf6582f Convert Ed25519 and Curve25519 functions to plain C 2016-09-05 10:40:39 +01:00
Richard van der Hoff 2aad4cfa86 Merge pull request #15 from matrix-org/rav/has_received_message
OlmSession.has_received_message
2016-09-05 10:38:41 +01:00
Richard van der Hoff fee1748c60 Merge pull request #16 from matrix-org/rav/fix_megolm_utf8
Fix megolm decryption of UTF-8
2016-09-05 10:37:55 +01:00
Richard van der Hoff 9d16d82089 Merge pull request #13 from matrix-org/rav/split_out_key_lengths
Create new constants for key lengths, etc
2016-09-05 10:36:43 +01:00
Richard van der Hoff 1d4c13c798 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.
2016-09-05 00:49:36 +01:00
Richard van der Hoff 2e9021c2e7 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.
2016-09-04 23:41:10 +01:00
Richard van der Hoff 39212987bd 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).
2016-09-02 15:11:14 +01:00
Chris Ballinger daab2a58af OLMAccount and OLMSession serialization 2016-04-13 16:53:47 -07:00
Chris Ballinger f505113fb7 Initial test passing 2016-04-09 14:00:30 -07:00
Chris Ballinger 719eb543a8 Xcode, podspec, wrapper 2016-04-08 17:26:12 -07:00
548 changed files with 70570 additions and 4211 deletions

27
.circleci/config.yml Normal file
View File

@ -0,0 +1,27 @@
version: 2
jobs:
build:
docker:
- image: trzeci/emscripten
working_directory: ~/repo
steps:
- checkout
- run:
name: Native Compile
command: make
- run:
name: Native Tests
command: make test
- run:
name: JS Compile
command: make js
- run:
name: Install JS Deps
working_directory: ~/repo/javascript
command: npm install
- run:
name: JS Tests
working_directory: ~/repo/javascript
command: npm run test

14
.editorconfig Normal file
View File

@ -0,0 +1,14 @@
root = true
[*]
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
end_of_line = lf
[*.{c,cpp,h,hh,py,ts,js,java,m}]
indent_style = space
indent_size = 4
[Makefile]
indent_style = tab

39
.gitignore vendored
View File

@ -1,6 +1,43 @@
/build
/CHANGELOG.html
/docs/megolm.html
/docs/olm.html
/docs/signing.html
/olm-*.tgz
/README.html
/tracing/README.html
/tracing/README.html
/python/dist
/javascript/checksums.txt
/javascript/checksums.txt.asc
/javascript/olm_prefix.js
/compile_commands.json
/.clang-format
.ccls-cache/
/python/.eggs
/python/install-temp
/result
# Xcode
build/
.build/
DerivedData/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
*.moved-aside
*.xcuserstate
*.hmap
*.ipa
*.dSYM.zip
*.dSYM
Pods/
*.xcworkspace
# JetBrains tools
.idea/

51
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,51 @@
default:
image: registry.fedoraproject.org/fedora-minimal:latest
stages:
- build
- test
- trigger
build:lib:
stage: build
tags:
- docker
script:
- microdnf --assumeyes --nodocs install cmake gcc gcc-c++
- cmake . -Bbuild
- cmake --build build
artifacts:
paths:
- build/
test:lib:
stage: test
tags:
- docker
needs:
- build:lib
script:
- microdnf --assumeyes --nodocs install cmake
- pushd build/tests
- ctest .
artifacts:
paths:
- build/tests/Testing/Temporary/
trigger:android:
stage: trigger
trigger:
strategy: depend
include: android/.gitlab-ci.yml
trigger:javascript:
stage: trigger
trigger:
strategy: depend
include: javascript/.gitlab-ci.yml
trigger:python:
stage: trigger
trigger:
strategy: depend
include: python/.gitlab-ci.yml

View File

@ -1,5 +1,433 @@
Changes in `1.1.0 <http://matrix.org/git/olm/commit/?h=1.1.0>`_
===============================================================
Changes in `3.2.16 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.16>`_
===========================================================================
This release includes the following changes since 3.2.15:
* Fix and modernize the Python packaging (thanks to Alfred Wingate)
Changes in `3.2.15 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.15>`_
===========================================================================
This release includes the following changes since 3.2.14:
* Improvements to Python packaging
* No longer depend on ``future`` since Python 2 is no longer supported.
* Improve compatibility with tox 4.
* Add support for making standalone sdist.
* Improvements to Nix flake (Thanks to Jon Ringer)
* Improve structure.
* Enable Darwin builds.
* Typescript type fix.
Changes in `3.2.14 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.14>`_
===========================================================================
This release includes the following changes since 3.2.13:
* TypeScript type improvements.
* Improvements to Python packaging
* Documentation improvements.
Changes in `3.2.13 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.13>`_
===========================================================================
This release includes the following changes since 3.2.12:
* Fix compilation with newer versions of emscripten.
* The npm package is compiled with emscripten 3.1.17 to fix compatibility with
node 18.
* Add py.typed to Python wheels.
* Some documentation fixes and updates.
* Improve the pkgconfig file.
Changes in `3.2.12 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.12>`_
===========================================================================
This release includes the following changes since 3.2.11:
* Expose olm_sas_calculate_mac_fixed_base64 in the bindings.
* Allow memory to grow in wasm. Thanks to benkuly for the suggestion.
* Fix Python type hints.
* Some Python build fixes.
* Initial work on a Nix flake for building and testing.
Changes in `3.2.11 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.11>`_
===========================================================================
This release includes the following changes since 3.2.10:
* Fix building documentation. Thanks to Jonas Smedegaard. The documents
written in Markdown are now converted to HTML using Pandoc.
* Add methods for getting unpublished fallback key in Objective-C binding.
* Add public pickle/unpickle methods to Java binding.
* Add wrapper for olm_session_describe to Java binding. Thanks to Alex Baker.
Changes in `3.2.10 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.10>`_
===========================================================================
This release includes no change since 3.2.9, but is created to be able to
publish again the Android library on MavenCentral.
Changes in `3.2.9 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.9>`_
=========================================================================
This release includes the following changes since 3.2.8:
* Switch C++ tests to use doctest. Thanks to Nicolas Werner.
* Switch JavaScript tests to use jasmine instead of deprecated jasmine-node.
* Add session describe function to Python binding. Thanks to Tulir Asokan.
Changes in `3.2.8 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.8>`_
=========================================================================
This release includes the following changes since 3.2.7:
* Improve handling of olm_session_describe when the buffer is too small.
* Ensure random arrays are blanked in JavaScript bindings.
Changes in `3.2.7 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.7>`_
=========================================================================
This release includes the following changes since 3.2.6:
* Fix installation with the Makefile.
* Fix exporting again, so we only export olm symbols.
* Fix WASM build. Thanks to Benjamin Kampmann.
* Add more functions for fallback keys.
Changes in `3.2.6 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.6>`_
=========================================================================
This release includes the following changes since 3.2.5:
* Fix building on various platforms when using CMake. Building from the
Makefile still assumes that it is using gcc.
Changes in `3.2.5 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.5>`_
=========================================================================
This release includes the following changes since 3.2.4:
* Add functions for getting error codes rather than error strings. Thanks to
Nicolas Werner for the suggestion.
* Only export olm symbols. Thanks to Mohammed Sadiq for the suggestion.
* Improve error handling in unpickle functions.
* Add support for fallback keys to the Objective-C and Android bindings.
Changes in `3.2.4 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.4>`_
=========================================================================
This release includes the following changes since 3.2.3:
* Android build fixes.
Changes in `3.2.3 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.3>`_
=========================================================================
This release includes the following changes since 3.2.2:
* Add some checks for invalid input and ensure all fields are initialized.
* Include LibreJS license tags. Thanks to Johannes Marbach for the suggestion.
* Support for Swift Package Manager. Thanks to Johannes Marbach.
Changes in `3.2.2 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.2>`_
=========================================================================
This release includes the following changes since 3.2.1:
* Fixes in the TypeScript definition file.
* CMake build fixes. Thanks to Gorgurov Alexey.
* Change the JavaScript package name to ``@matrix-org/olm``. Note that
this means that packages will need to change their ``require`` or
``import`` statements to use this new name.
* Include file checksums in the JavaScript package.
* Fix length calculation in fallback key json. Thanks to Tobias Furuholm.
* Add a new function to calculate the correct base64 encoding for SAS.
(Currently only available in the C API.)
* Add the ability to specify a pickle key in the Objective-C binding.
* Add pkg-config file on Unix-like systems.
Changes in `3.2.1 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.1>`_
=========================================================================
This release includes the following changes since 3.2.0:
* Fixes in the TypeScript definition file.
Changes in `3.2.0 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.0>`_
=========================================================================
This release includes the following changes since 3.1.5:
* Add support for fallback keys (MSC2732).
* Allow some arguments in the JavaScript bindings to be either Uint8Array or
strings.
* Fixes to the TypeScript definition file.
* Improvements to the JavaScript group demo. Thanks to Saúl Ibarra Corretgé.
* Ensure that the other party's public key has been set in SAS module. Thanks
to Saúl Ibarra Corretgé.
* Fix building with newer versions of emscripten, and simplify makefile. Thanks
to Lukas Lihotzki.
* Reduce pollution of the global namespace in the Javascript binding. Thanks to
Lukas Lihotzki.
Changes in `3.1.5 <https://gitlab.matrix.org/matrix-org/olm/tags/3.1.5>`_
=========================================================================
This release includes the following changes since 3.1.4:
* Build improvements:
* Fix CMake handling when installing in a non-standard location. Thanks to
Alexey Rusakov.
* Add support in the Makefile for creating a WASM-ready archive. Thanks to
stoically.
* Improve support for LLVM is Makefile. Thanks to caywin25 for reporting.
* Add a TypeScript definition file.
* Some documentation and example fixes.
* Add list of bindings to the README.
Changes in `3.1.4 <https://gitlab.matrix.org/matrix-org/olm/tags/3.1.4>`_
=========================================================================
This release includes the following changes since 3.1.3:
* Build improvements:
* Install headers in the system-configured include directory with CMake.
* Overwrite symbolic links when installing with make.
* Improve compatibility with more emscripten versions.
* Don't use hypothesis in Python unit tests.
* Some documentation improvements.
Changes in `3.1.3 <https://gitlab.matrix.org/matrix-org/olm/tags/3.1.3>`_
=========================================================================
This release fixes unicode issues in the Python bindings, and adds some
clarifications to the documentation.
Changes in `3.1.2 <https://gitlab.matrix.org/matrix-org/olm/tags/3.1.2>`_
=========================================================================
This release updates the Android bindings to use a newer Android SDK version.
Changes in `3.1.1 <https://gitlab.matrix.org/matrix-org/olm/tags/3.1.1>`_
=========================================================================
This release fixes various build issues:
* Include the SAS files and tests in the CMake files.
* Address some build issues on Windows.
Changes in `3.1.0 <https://gitlab.matrix.org/matrix-org/olm/tags/3.1.0>`_
=========================================================================
This release includes the following changes since 3.0.0:
* Add functions to support Short Authentication String key verification. The
new functions are in the ``sas.h`` header file. The Android, iOS, JavaScript
and Python bindings also include corresponding functions.
* Add functions to perform public key signing. These are meant for use with
cross-signing. The new functions are ``olm_pk_signing_size``,
``olm_pk_signing``, ``olm_pk_signing_last_error``, ``olm_clear_pk_signing``,
``olm_pk_signing_key_from_seed``, ``olm_pk_signing_seed_length``,
``olm_pk_signing_public_key_length``, ``olm_pk_signature_length``, and
``olm_pk_sign``. Signatures generated by ``olm_pk_sign`` can be verified
using ``olm_ed25519_verify``. The Android, iOS, JavaScript and Python
bindings also include corresponding functions.
* Fix compilation under some compilers.
JavaScript wrapper:
* Improved compatibility with newer versions of Emscripten, and dropped support
for some older versions of Emscripten.
Python wrapper:
* Build fixes.
* Add bindings for the public key encryption/decryption functions from olm 2.3.0.
Changes in `3.0.0 <https://gitlab.matrix.org/matrix-org/olm/tags/3.0.0>`_
=========================================================================
This release includes the following changes to 2.3.0:
* Support for building using cmake. Thanks to Konstantinos Sideris.
* Add more functions for managing private keys in the public key decryption
functionality. These are meant for use with server-side encrypted key
backups. The new functions are ``olm_pk_private_key_length``,
``olm_pk_key_from_private``, and ``olm_pk_get_private_key``.
* ``olm_pk_generate_key`` and ``olm_pk_generate_key_random_length`` are
deprecated: to generate a random key, use ``olm_pk_key_from_private``
with random bytes as the private key.
Python wrapper:
* BREAKING CHANGE: This release introduces a new API for the Python wrapper,
thanks to Damir Jelić. The new API should be much easier to use for Python
developers. However, this means that existing code will need to be rewritten
to use the new API.
JavaScript wrapper:
* BREAKING CHANGE: Olm now uses WebAssembly which means it needs
to load the wasm file asynchronously, and therefore needs to be
started up asynchronously. The imported module now has an init()
method which returns a promise. The library cannot be used until
this promise resolves. It will reject if the library fails to start.
* Using ``olm/olm.js`` will use the WebAssembly version of the library. For
environments that do not support WebAssembly, use ``olm/olm_legacy.js``.
Objective-C wrapper:
* Add support for the public key encryption/decryption functionality.
Changes in `2.3.0 <https://gitlab.matrix.org/matrix-org/olm/tags/2.3.0>`_
=========================================================================
This release includes the following changes since 2.2.2:
* Support building on Windows. Thanks to Marcel Radzio.
* Avoid C99 inside C++ code. Thanks to Alexey Rusakov.
* Support building as a static library. Thanks to Andreas Zwinkau.
New functionality:
* Add a number of methods for public key encryption and decryption. This
functionality is meant for use with allowing virus scanning of encrypted
attachments, server-side encrypted key backups, and possibly other uses. The
methods are listed in the ``olm/pk.h`` header file. Corresponding wrappers
are available in the JavaScript and Android wrappers. Objective-C and Python
wrappers will be available in a future release.
Android wrapper:
* Update build tool dependencies
* Apply some hardening flags and fix some compilation and run-time issues.
Thanks in part to Arnaud Fontaine.
Objective-C wrapper:
* Update project file
* Fix compiler warnings
Python wrapper:
* Add binding for ``olm_remove_one_time_keys``. Thanks to Wilfried Klaebe.
* Add utility module for ``ed25519_verify``. Thanks to Alexander Maznev.
* Improve portability. Thanks to Jan Jancar.
Changes in `2.2.2 <https://gitlab.matrix.org/matrix-org/olm/tags/2.2.2>`_
=========================================================================
Objective-C wrapper:
* Fixed type of ``messageIndex`` argument in
``exportSessionAtMessageIndex``. Thanks to Greg Hughes.
Changes in `2.2.1 <https://gitlab.matrix.org/matrix-org/olm/tags/2.2.1>`_
=========================================================================
The only change in this release is a fix to the build scripts for the
Objective-C wrapper which made it impossible to release the 2.2.0 CocoaPod.
Changes in `2.2.0 <https://gitlab.matrix.org/matrix-org/olm/tags/2.2.0>`_
=========================================================================
This release includes the following changes since 2.1.0:
* Add Java wrappers to allow use under Android.
New functionality:
* Add a number of methods allowing InboundGroupSessions to be exported and
imported. These are: ``olm_inbound_group_session_first_known_index``,
``olm_export_inbound_group_session_length``,
``olm_export_inbound_group_session``, ``olm_import_inbound_group_session``
and ``olm_inbound_group_session_is_verified``. Corresponding wrappers are
available in the Javascript, Python, Objective-C and Android wrappers.
Objective-C wrapper:
* Fix a number of issues with the build scripts which prevented it being used
for macOS/Swift projects. Thanks to Avery Pierce.
Changes in `2.1.0 <https://gitlab.matrix.org/matrix-org/olm/tags/2.1.0>`_
=========================================================================
This release includes the following changes since 2.0.0:
* Add OLMKit, the Objective-C wrapper. Thanks to Chris Ballinger for the
initial work on this.
Javascript wrapper:
* Handle exceptions during loading better (don't leave a half-initialised
state).
* Allow applications to tune emscripten options (such as the amount of heap).
* Allocate memory for encrypted/decrypted messages on the empscripten heap,
rather than the stack, allowing more efficient memory use.
Changes in `2.0.0 <https://gitlab.matrix.org/matrix-org/olm/tags/2.0.0>`_
=========================================================================
This release includes the following changes since 1.3.0:
* Fix a buffer bounds check when decoding group messages.
* Update ``olm_group_decrypt`` to return the ratchet index for decrypted
messages.
* Fix ``olm_pickle_account``, ``olm_pickle_session``,
``olm_pickle_inbound_group_session`` and
``olm_pickle_outbound_group_session`` to correctly return the length of the
pickled object.
* Add a `specification <./docs/megolm.rst>`_ of the Megolm ratchet, and add
some information on mitigating unknown key-share attacks to the `Olm
specification <./docs/olm.rst>`_.
* Add an ``install-headers`` target to the Makefile (and run it when installing
the library). (Credit to Emmanuel Gil Peyrot).
Changes in `1.3.0 <https://gitlab.matrix.org/matrix-org/olm/tags/1.3.0>`_
=========================================================================
This release updates the group session identifier to avoid collisions.
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 <https://gitlab.matrix.org/matrix-org/olm/tags/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 <https://gitlab.matrix.org/matrix-org/olm/tags/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
@ -10,8 +438,8 @@ existing OlmAccounts should in any case be considered compromised (as above),
the library refuses to load them, returning OLM_BAD_LEGACY_ACCOUNT_PICKLE.
Changes in `1.0.0 <http://matrix.org/git/olm/commit/?h=1.0.0>`_
===============================================================
Changes in `1.0.0 <https://gitlab.matrix.org/matrix-org/olm/tags/1.0.0>`_
=========================================================================
This release includes a fix to a bug which had the potential to leak sensitive
data to the application: see

142
CMakeLists.txt Normal file
View File

@ -0,0 +1,142 @@
cmake_minimum_required(VERSION 3.4)
project(olm VERSION 3.2.16 LANGUAGES CXX C)
option(OLM_TESTS "Build tests" ON)
option(BUILD_SHARED_LIBS "Build as a shared library" ON)
add_definitions(-DOLMLIB_VERSION_MAJOR=${PROJECT_VERSION_MAJOR})
add_definitions(-DOLMLIB_VERSION_MINOR=${PROJECT_VERSION_MINOR})
add_definitions(-DOLMLIB_VERSION_PATCH=${PROJECT_VERSION_PATCH})
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
set(CMAKE_C_VISIBILITY_PRESET hidden)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
add_library(olm
src/account.cpp
src/base64.cpp
src/cipher.cpp
src/crypto.cpp
src/memory.cpp
src/message.cpp
src/pickle.cpp
src/ratchet.cpp
src/session.cpp
src/utility.cpp
src/pk.cpp
src/sas.c
src/ed25519.c
src/error.c
src/inbound_group_session.c
src/megolm.c
src/olm.cpp
src/outbound_group_session.c
src/pickle_encoding.c
lib/crypto-algorithms/aes.c
lib/crypto-algorithms/sha256.c
lib/curve25519-donna/curve25519-donna.c)
add_library(Olm::Olm ALIAS olm)
# restrict the exported symbols
include(GenerateExportHeader)
generate_export_header(olm
EXPORT_FILE_NAME ${CMAKE_CURRENT_SOURCE_DIR}/include/olm/olm_export.h)
target_include_directories(olm
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/lib)
set_target_properties(olm PROPERTIES
SOVERSION ${PROJECT_VERSION_MAJOR}
VERSION ${PROJECT_VERSION})
set_target_properties(olm PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
include(GNUInstallDirs)
# Make a pkg-config file
configure_file(${PROJECT_NAME}.pc.in ${PROJECT_NAME}.pc @ONLY NEWLINE_STYLE UNIX)
#
# Installation
#
set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/Olm)
install(TARGETS olm
EXPORT olm-targets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
# The exported target will be named Olm.
set_target_properties(olm PROPERTIES EXPORT_NAME Olm)
install(FILES
${CMAKE_CURRENT_SOURCE_DIR}/include/olm/olm.h
${CMAKE_CURRENT_SOURCE_DIR}/include/olm/olm_export.h
${CMAKE_CURRENT_SOURCE_DIR}/include/olm/outbound_group_session.h
${CMAKE_CURRENT_SOURCE_DIR}/include/olm/inbound_group_session.h
${CMAKE_CURRENT_SOURCE_DIR}/include/olm/pk.h
${CMAKE_CURRENT_SOURCE_DIR}/include/olm/sas.h
${CMAKE_CURRENT_SOURCE_DIR}/include/olm/error.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/olm)
if (UNIX AND NOT APPLE)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
)
endif ()
# Export the targets to a script.
install(EXPORT olm-targets
FILE OlmTargets.cmake
NAMESPACE Olm::
DESTINATION ${INSTALL_CONFIGDIR})
# Create a ConfigVersion.cmake file.
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/OlmConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion)
configure_package_config_file(
${CMAKE_CURRENT_LIST_DIR}/cmake/OlmConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/OlmConfig.cmake
INSTALL_DESTINATION ${INSTALL_CONFIGDIR})
#Install the config & configversion.
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/OlmConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/OlmConfigVersion.cmake
DESTINATION ${INSTALL_CONFIGDIR})
# Register package in user's package registry
export(EXPORT olm-targets
FILE ${CMAKE_CURRENT_BINARY_DIR}/OlmTargets.cmake
NAMESPACE Olm::)
export(PACKAGE Olm)
if (OLM_TESTS)
add_subdirectory(tests)
endif()

67
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,67 @@
# Contributing code to libolm
To contribute code to this library, the preferred way is to clone the git
repository, create a git patch series (for example via ``git
format-patch --stdout origin/master``), and send this by email to
``olm@matrix.org``.
Naturally, you must be willing to license your contributions under the same
license as the project itself - in this case, Apache Software License v2 (see
[LICENSE](LICENSE)).
## Sign off
In order to have a concrete record that your contribution is intentional and
you agree to license it under the same terms as the project's license, we've
adopted the same lightweight approach that the
[Linux Kernel](https://www.kernel.org/doc/html/latest/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin),
[Docker](https://github.com/docker/docker/blob/master/CONTRIBUTING.md),
and many other projects use: the DCO ([Developer Certificate of Origin](http://developercertificate.org/)).
This is a simple declaration that you wrote the contribution or otherwise have
the right to contribute it to Matrix::
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
660 York Street, Suite 102,
San Francisco, CA 94110 USA
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
If you agree to this for your contribution, then all that's needed is to
include the line in your commits or covering email::
Signed-off-by: Your Name <your@email.example.org>
...using your real name; unfortunately pseudonyms and anonymous contributions
can't be accepted. Git makes this trivial - just use the -s flag when you do
``git commit``, having first set ``user.name`` and ``user.email`` git configs
(which you should have done anyway :)

338
Makefile
View File

@ -1,49 +1,83 @@
#!/usr/bin/make -f
MAJOR := 1
MINOR := 1
PATCH := 0
include common.mk
VERSION := $(MAJOR).$(MINOR).$(PATCH)
PREFIX ?= /usr/local
BUILD_DIR := build
RELEASE_OPTIMIZE_FLAGS ?= -g -O3
DEBUG_OPTIMIZE_FLAGS ?= -g -O0
RELEASE_OPTIMIZE_FLAGS ?= -O3
DEBUG_OPTIMIZE_FLAGS ?= -g -O0 -U_FORTIFY_SOURCE
JS_OPTIMIZE_FLAGS ?= -O3
FUZZING_OPTIMIZE_FLAGS ?= -O3
CC = gcc
FUZZER_OPTIMIZE_FLAGS ?= -O3
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
EMAR = emar
AR = ar
UNAME := $(shell uname)
ifeq ($(UNAME),Darwin)
SO := dylib
OLM_LDFLAGS :=
else
SO := so
OLM_LDFLAGS := -Wl,-soname,libolm.so.$(MAJOR) \
-Wl,--version-script,version_script.ver
endif
RELEASE_TARGET := $(BUILD_DIR)/libolm.$(SO).$(VERSION)
STATIC_RELEASE_TARGET := $(BUILD_DIR)/libolm.a
DEBUG_TARGET := $(BUILD_DIR)/libolm_debug.$(SO).$(VERSION)
JS_WASM_TARGET := javascript/olm.js
JS_ASMJS_TARGET := javascript/olm_legacy.js
WASM_TARGET := $(BUILD_DIR)/wasm/libolm.a
JS_EXPORTED_FUNCTIONS := javascript/exported_functions.json
JS_EXPORTED_RUNTIME_METHODS := [ALLOC_STACK,writeAsciiToMemory,intArrayFromString,UTF8ToString,stringToUTF8]
JS_EXTERNS := javascript/externs.js
PUBLIC_HEADERS := include/olm/olm.h include/olm/outbound_group_session.h include/olm/inbound_group_session.h
PUBLIC_HEADERS := include/olm/olm.h include/olm/outbound_group_session.h include/olm/inbound_group_session.h include/olm/pk.h include/olm/sas.h include/olm/error.h include/olm/olm_export.h
SOURCES := $(wildcard src/*.cpp) $(wildcard src/*.c) \
lib/crypto-algorithms/sha256.c \
lib/crypto-algorithms/aes.c \
lib/curve25519-donna/curve25519-donna.c
FUZZER_SOURCES := $(wildcard fuzzers/fuzz_*.cpp) $(wildcard fuzzers/fuzz_*.c)
FUZZER_SOURCES := $(wildcard fuzzing/fuzzers/fuzz_*.cpp) $(wildcard fuzzing/fuzzers/fuzz_*.c)
TEST_SOURCES := $(wildcard tests/test_*.cpp) $(wildcard tests/test_*.c)
OBJECTS := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCES)))
RELEASE_OBJECTS := $(addprefix $(BUILD_DIR)/release/,$(OBJECTS))
DEBUG_OBJECTS := $(addprefix $(BUILD_DIR)/debug/,$(OBJECTS))
FUZZER_OBJECTS := $(addprefix $(BUILD_DIR)/fuzzers/objects/,$(OBJECTS))
FUZZER_BINARIES := $(addprefix $(BUILD_DIR)/,$(basename $(FUZZER_SOURCES)))
FUZZER_ASAN_OBJECTS := $(addprefix $(BUILD_DIR)/fuzzers/objects/,$(addprefix asan_,$(OBJECTS)))
FUZZER_MSAN_OBJECTS := $(addprefix $(BUILD_DIR)/fuzzers/objects/,$(addprefix msan_,$(OBJECTS)))
FUZZER_DEBUG_OBJECTS := $(addprefix $(BUILD_DIR)/fuzzers/objects/,$(addprefix debug_,$(OBJECTS)))
FUZZER_BINARIES := $(addprefix $(BUILD_DIR)/fuzzers/,$(basename $(notdir $(FUZZER_SOURCES))))
FUZZER_ASAN_BINARIES := $(addsuffix _asan,$(FUZZER_BINARIES))
FUZZER_MSAN_BINARIES := $(addsuffix _msan,$(FUZZER_BINARIES))
FUZZER_DEBUG_BINARIES := $(patsubst $(BUILD_DIR)/fuzzers/fuzz_%,$(BUILD_DIR)/fuzzers/debug_%,$(FUZZER_BINARIES))
TEST_BINARIES := $(patsubst tests/%,$(BUILD_DIR)/tests/%,$(basename $(TEST_SOURCES)))
JS_OBJECTS := $(addprefix $(BUILD_DIR)/javascript/,$(OBJECTS))
WASM_OBJECTS := $(addprefix $(BUILD_DIR)/wasm/,$(OBJECTS))
# pre & post are the js-pre/js-post options to emcc.
# They are injected inside the modularised code and
# processed by the optimiser.
JS_PRE := $(wildcard javascript/*pre.js)
JS_POST := javascript/olm_outbound_group_session.js \
javascript/olm_inbound_group_session.js \
javascript/olm_pk.js \
javascript/olm_sas.js \
javascript/olm_post.js
# The prefix & suffix are just added onto the start & end
# of what comes out emcc, so are outside of the modularised
# code and not seen by the opimiser.
JS_PREFIX := javascript/olm_prefix.js
JS_SUFFIX := javascript/olm_suffix.js
DOCS := tracing/README.html \
docs/megolm.html \
docs/olm.html \
docs/signing.html \
README.html \
CHANGELOG.html
@ -52,50 +86,98 @@ CPPFLAGS += -Iinclude -Ilib \
-DOLMLIB_VERSION_PATCH=$(PATCH)
# we rely on <stdint.h>, which was introduced in C99
CFLAGS += -Wall -Werror -std=c99 -fPIC
CXXFLAGS += -Wall -Werror -std=c++11 -fPIC
CFLAGS += -Wall -Werror -std=c99
CXXFLAGS += -Wall -Werror -std=c++11
LDFLAGS += -Wall -Werror
EMCCFLAGS = --closure 1 --memory-init-file 0 -s NO_FILESYSTEM=1 -s INVOKE_RUN=0
# NO_BROWSER is kept for compatibility with emscripten 1.35.24, but is no
# longer needed.
EMCCFLAGS += -s NO_BROWSER=1
CFLAGS_NATIVE = -fPIC
CXXFLAGS_NATIVE = -fPIC
EMCC.c = $(EMCC) $(CFLAGS) $(CPPFLAGS) -c
EMCC.cc = $(EMCC) $(CXXFLAGS) $(CPPFLAGS) -c
EMCCFLAGS = --closure 1 --memory-init-file 0 -s NO_FILESYSTEM=1 -s INVOKE_RUN=0 -s MODULARIZE=1 -Wno-error=closure
# Olm generally doesn't need a lot of memory to encrypt / decrypt its usual
# payloads (ie. Matrix messages), but we do need about 128K of heap to encrypt
# a 64K event (enough to store the ciphertext and the plaintext, bearing in
# mind that the plaintext can only be 48K because base64). We also have about
# 36K of statics. So let's have 256K of memory.
# (This can't be changed by the app with wasm since it's baked into the wasm).
# (emscripten also mandates at least 16MB of memory for asm.js now, so
# we don't use this for the legacy build.)
EMCCFLAGS_WASM += -s TOTAL_STACK=65536 -s TOTAL_MEMORY=262144 -s ALLOW_MEMORY_GROWTH
EMCCFLAGS_ASMJS += -s WASM=0
EMCC.c = $(EMCC) $(CFLAGS) $(CPPFLAGS) -c -DNDEBUG -DOLM_STATIC_DEFINE=1
EMCC.cc = $(EMCC) $(CXXFLAGS) $(CPPFLAGS) -c -DNDEBUG -DOLM_STATIC_DEFINE=1
EMCC_LINK = $(EMCC) $(LDFLAGS) $(EMCCFLAGS)
AFL_CC = afl-clang-fast
AFL_CXX = afl-clang-fast++
AFL.c = $(AFL_CC) $(CFLAGS) $(CPPFLAGS) -c
AFL.cc = $(AFL_CXX) $(CXXFLAGS) $(CPPFLAGS) -c
AFL_LINK.c = $(AFL_CC) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS)
AFL_LINK.cc = $(AFL_CXX) $(LDFLAGS) $(CXXFLAGS) $(CPPFLAGS)
AFL_ASAN.c = AFL_USE_ASAN=1 $(AFL_CC) -m32 $(CFLAGS) $(CPPFLAGS) -c
AFL_ASAN.cc = AFL_USE_ASAN=1 $(AFL_CXX) -m32 $(CXXFLAGS) $(CPPFLAGS) -c
AFL_LINK_ASAN.c = AFL_USE_ASAN=1 $(AFL_CC) -m32 $(LDFLAGS) $(CFLAGS) $(CPPFLAGS)
AFL_LINK_ASAN.cc = AFL_USE_ASAN=1 $(AFL_CXX) -m32 $(LDFLAGS) $(CXXFLAGS) $(CPPFLAGS)
AFL_MSAN.c = AFL_USE_MSAN=1 $(AFL_CC) $(CFLAGS) $(CPPFLAGS) -c
AFL_MSAN.cc = AFL_USE_MSAN=1 $(AFL_CXX) $(CXXFLAGS) $(CPPFLAGS) -c
AFL_LINK_MSAN.c = AFL_USE_MSAN=1 $(AFL_CC) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS)
AFL_LINK_MSAN.cc = AFL_USE_MSAN=1 $(AFL_CXX) $(LDFLAGS) $(CXXFLAGS) $(CPPFLAGS)
# generate .d files when compiling
CPPFLAGS += -MMD
### per-target variables
$(RELEASE_OBJECTS): CFLAGS += $(RELEASE_OPTIMIZE_FLAGS)
$(RELEASE_OBJECTS): CXXFLAGS += $(RELEASE_OPTIMIZE_FLAGS)
$(RELEASE_OBJECTS): CFLAGS += $(RELEASE_OPTIMIZE_FLAGS) $(CFLAGS_NATIVE)
$(RELEASE_OBJECTS): CXXFLAGS += $(RELEASE_OPTIMIZE_FLAGS) $(CXXFLAGS_NATIVE)
$(RELEASE_TARGET): LDFLAGS += $(RELEASE_OPTIMIZE_FLAGS)
$(DEBUG_OBJECTS): CFLAGS += $(DEBUG_OPTIMIZE_FLAGS)
$(DEBUG_OBJECTS): CXXFLAGS += $(DEBUG_OPTIMIZE_FLAGS)
$(DEBUG_OBJECTS): CFLAGS += $(DEBUG_OPTIMIZE_FLAGS) $(CFLAGS_NATIVE)
$(DEBUG_OBJECTS): CXXFLAGS += $(DEBUG_OPTIMIZE_FLAGS) $(CXXFLAGS_NATIVE)
$(DEBUG_TARGET): LDFLAGS += $(DEBUG_OPTIMIZE_FLAGS)
$(TEST_BINARIES): CPPFLAGS += -Itests/include
$(TEST_BINARIES): LDFLAGS += $(DEBUG_OPTIMIZE_FLAGS) -L$(BUILD_DIR)
$(FUZZER_OBJECTS): CFLAGS += $(FUZZER_OPTIMIZE_FLAGS)
$(FUZZER_OBJECTS): CXXFLAGS += $(FUZZER_OPTIMIZE_FLAGS)
$(FUZZER_BINARIES): CPPFLAGS += -Ifuzzers/include
$(FUZZER_BINARIES): LDFLAGS += $(FUZZER_OPTIMIZE_FLAGS) -L$(BUILD_DIR)
$(FUZZER_DEBUG_BINARIES): CPPFLAGS += -Ifuzzers/include
$(FUZZER_DEBUG_BINARIES): LDFLAGS += $(DEBUG_OPTIMIZE_FLAGS)
$(FUZZER_OBJECTS): CFLAGS += $(FUZZER_OPTIMIZE_FLAGS) -D OLM_FUZZING=1
$(FUZZER_OBJECTS): CXXFLAGS += $(FUZZER_OPTIMIZE_FLAGS) -D OLM_FUZZING=1
$(FUZZER_DEBUG_OBJECTS): CFLAGS += $(DEBUG_OPTIMIZE_FLAGS) $(CFLAGS_NATIVE) -D OLM_FUZZING=1
$(FUZZER_DEBUG_OBJECTS): CXXFLAGS += $(DEBUG_OPTIMIZE_FLAGS) $(CXXFLAGS_NATIVE) -D OLM_FUZZING=1
$(FUZZER_ASAN_OBJECTS): CFLAGS += $(FUZZER_OPTIMIZE_FLAGS) -D OLM_FUZZING=1
$(FUZZER_ASAN_OBJECTS): CXXFLAGS += $(FUZZER_OPTIMIZE_FLAGS) -D OLM_FUZZING=1
$(FUZZER_MSAN_OBJECTS): CFLAGS += $(FUZZER_OPTIMIZE_FLAGS) -D OLM_FUZZING=1
$(FUZZER_MSAN_OBJECTS): CXXFLAGS += $(FUZZER_OPTIMIZE_FLAGS) -D OLM_FUZZING=1
$(FUZZER_BINARIES): CPPFLAGS += -Ifuzzing/fuzzers/include
$(FUZZER_BINARIES): LDFLAGS += $(FUZZER_OPTIMIZE_FLAGS) -L$(BUILD_DIR) -lstdc++
$(FUZZER_ASAN_BINARIES): CPPFLAGS += -Ifuzzing/fuzzers/include
$(FUZZER_ASAN_BINARIES): LDFLAGS += $(FUZZER_OPTIMIZE_FLAGS) -L$(BUILD_DIR) -lstdc++
$(FUZZER_MSAN_BINARIES): CPPFLAGS += -Ifuzzing/fuzzers/include
$(FUZZER_MSAN_BINARIES): LDFLAGS += $(FUZZER_OPTIMIZE_FLAGS) -L$(BUILD_DIR) -lstdc++
$(FUZZER_DEBUG_BINARIES): CPPFLAGS += -Ifuzzing/fuzzers/include
$(FUZZER_DEBUG_BINARIES): LDFLAGS += $(DEBUG_OPTIMIZE_FLAGS) -lstdc++
$(JS_OBJECTS): CFLAGS += $(JS_OPTIMIZE_FLAGS)
$(JS_OBJECTS): CXXFLAGS += $(JS_OPTIMIZE_FLAGS)
$(JS_TARGET): LDFLAGS += $(JS_OPTIMIZE_FLAGS)
$(JS_WASM_TARGET): LDFLAGS += $(JS_OPTIMIZE_FLAGS)
$(JS_ASMJS_TARGET): LDFLAGS += $(JS_OPTIMIZE_FLAGS)
### Fix to make mkdir work on windows and linux
ifeq ($(shell echo "check_quotes"),"check_quotes")
WINDOWS := yes
else
WINDOWS := no
endif
ifeq ($(WINDOWS),yes)
mkdir = mkdir $(subst /,\,$(1)) > nul 2>&1 || (exit 0)
else
mkdir = mkdir -p $(1)
endif
### top-level targets
@ -103,31 +185,69 @@ lib: $(RELEASE_TARGET)
.PHONY: lib
$(RELEASE_TARGET): $(RELEASE_OBJECTS)
@echo
@echo '****************************************************************************'
@echo '* WARNING: Building olm with make is deprecated. Please use cmake instead. *'
@echo '****************************************************************************'
@echo
$(CXX) $(LDFLAGS) --shared -fPIC \
-Wl,-soname,libolm.so.$(MAJOR) \
-Wl,--version-script,version_script.ver \
$(OLM_LDFLAGS) \
$(OUTPUT_OPTION) $(RELEASE_OBJECTS)
ldconfig -l $@
ln -sf libolm.$(SO).$(VERSION) $(BUILD_DIR)/libolm.$(SO).$(MAJOR)
ln -sf libolm.$(SO).$(VERSION) $(BUILD_DIR)/libolm.$(SO)
debug: $(DEBUG_TARGET)
.PHONY: debug
$(DEBUG_TARGET): $(DEBUG_OBJECTS)
$(CXX) $(LDFLAGS) --shared -fPIC \
-Wl,-soname,libolm_debug.so.$(MAJOR) \
-Wl,--version-script,version_script.ver \
$(OLM_LDFLAGS) \
$(OUTPUT_OPTION) $(DEBUG_OBJECTS)
ldconfig -l $@
ln -sf libolm_debug.$(SO).$(VERSION) $(BUILD_DIR)/libolm_debug.$(SO).$(MAJOR)
js: $(JS_TARGET)
static: $(STATIC_RELEASE_TARGET)
.PHONY: static
$(STATIC_RELEASE_TARGET): $(RELEASE_OBJECTS)
$(AR) rcs $@ $^
js: $(JS_WASM_TARGET) $(JS_ASMJS_TARGET)
.PHONY: js
$(JS_TARGET): $(JS_OBJECTS) $(JS_PRE) $(JS_POST) $(JS_EXPORTED_FUNCTIONS)
$(EMCC_LINK) \
wasm: $(WASM_TARGET)
.PHONY: wasm
$(WASM_TARGET): $(WASM_OBJECTS)
$(EMAR) rcs $@ $^
javascript/olm_prefix.js: javascript/olm_prefix.js.in Makefile common.mk
sed s/@VERSION@/$(VERSION)/ javascript/olm_prefix.js.in > $@
# Note that the output file we give to emcc determines the name of the
# wasm file baked into the js, hence messing around outputting to olm.js
# and then renaming it.
$(JS_WASM_TARGET): $(JS_OBJECTS) $(JS_PRE) $(JS_POST) $(JS_EXPORTED_FUNCTIONS) $(JS_PREFIX) $(JS_SUFFIX)
EMCC_CLOSURE_ARGS="--externs $(CURDIR)/$(JS_EXTERNS)" $(EMCC_LINK) \
$(EMCCFLAGS_WASM) \
$(foreach f,$(JS_PRE),--pre-js $(f)) \
$(foreach f,$(JS_POST),--post-js $(f)) \
$(foreach f,$(JS_PREFIX),--extern-pre-js $(f)) \
$(foreach f,$(JS_SUFFIX),--extern-post-js $(f)) \
-s "EXPORTED_FUNCTIONS=@$(JS_EXPORTED_FUNCTIONS)" \
$(JS_OBJECTS) -o $@
-s "EXPORTED_RUNTIME_METHODS=$(JS_EXPORTED_RUNTIME_METHODS)" \
-o $@ $(JS_OBJECTS)
$(JS_ASMJS_TARGET): $(JS_OBJECTS) $(JS_PRE) $(JS_POST) $(JS_EXPORTED_FUNCTIONS) $(JS_PREFIX) $(JS_SUFFIX)
EMCC_CLOSURE_ARGS="--externs $(CURDIR)/$(JS_EXTERNS)" $(EMCC_LINK) \
$(EMCCFLAGS_ASMJS) \
$(foreach f,$(JS_PRE),--pre-js $(f)) \
$(foreach f,$(JS_POST),--post-js $(f)) \
$(foreach f,$(JS_PREFIX),--extern-pre-js $(f)) \
$(foreach f,$(JS_SUFFIX),--extern-post-js $(f)) \
-s "EXPORTED_FUNCTIONS=@$(JS_EXPORTED_FUNCTIONS)" \
-s "EXPORTED_RUNTIME_METHODS=$(JS_EXPORTED_RUNTIME_METHODS)" \
-o $@ $(JS_OBJECTS)
build_tests: $(TEST_BINARIES)
@ -137,30 +257,39 @@ test: build_tests
$$i || exit $$?; \
done
fuzzers: $(FUZZER_BINARIES) $(FUZZER_DEBUG_BINARIES)
test_mem: build_tests
for i in $(TEST_BINARIES); do \
echo $$i; \
valgrind -q --leak-check=yes --exit-on-first-error=yes --error-exitcode=1 $$i || exit $$?; \
done
fuzzers: $(FUZZER_BINARIES) $(FUZZER_ASAN_BINARIES) $(FUZZER_MSAN_BINARIES) $(FUZZER_DEBUG_BINARIES)
.PHONY: fuzzers
$(JS_EXPORTED_FUNCTIONS): $(PUBLIC_HEADERS)
perl -MJSON -ne '$$f{"_$$1"}=1 if /(olm_[^( ]*)\(/; END { @f=sort keys %f; print encode_json \@f }' $^ > $@.tmp
./exports.py $^ > $@.tmp
mv $@.tmp $@
all: test js lib debug doc
.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
install-headers: $(PUBLIC_HEADERS)
test -d $(DESTDIR)$(PREFIX)/include/olm || $(call mkdir,$(DESTDIR)$(PREFIX)/include/olm)
install $(PUBLIC_HEADERS) $(DESTDIR)$(PREFIX)/include/olm/
.PHONY: install-headers
install-debug: debug install-headers
test -d $(DESTDIR)$(PREFIX)/lib || $(call mkdir,$(DESTDIR)$(PREFIX)/lib)
install $(DEBUG_TARGET) $(DESTDIR)$(PREFIX)/lib/libolm_debug.$(SO).$(VERSION)
ln -sf libolm_debug.$(SO).$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm_debug.$(SO).$(MAJOR)
ln -sf 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
install: lib install-headers
test -d $(DESTDIR)$(PREFIX)/lib || $(call mkdir,$(DESTDIR)$(PREFIX)/lib)
install $(RELEASE_TARGET) $(DESTDIR)$(PREFIX)/lib/libolm.$(SO).$(VERSION)
ln -sf libolm.$(SO).$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm.$(SO).$(MAJOR)
ln -sf libolm.$(SO).$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm.$(SO)
.PHONY: install
clean:;
@ -172,60 +301,108 @@ doc: $(DOCS)
### rules for building objects
$(BUILD_DIR)/release/%.o: %.c
mkdir -p $(dir $@)
$(call mkdir,$(dir $@))
$(COMPILE.c) $(OUTPUT_OPTION) $<
$(BUILD_DIR)/release/%.o: %.cpp
mkdir -p $(dir $@)
$(call mkdir,$(dir $@))
$(COMPILE.cc) $(OUTPUT_OPTION) $<
$(BUILD_DIR)/debug/%.o: %.c
mkdir -p $(dir $@)
$(call mkdir,$(dir $@))
$(COMPILE.c) $(OUTPUT_OPTION) $<
$(BUILD_DIR)/debug/%.o: %.cpp
mkdir -p $(dir $@)
$(call mkdir,$(dir $@))
$(COMPILE.cc) $(OUTPUT_OPTION) $<
$(BUILD_DIR)/javascript/%.o: %.c
mkdir -p $(dir $@)
$(call mkdir,$(dir $@))
$(EMCC.c) $(OUTPUT_OPTION) $<
$(BUILD_DIR)/javascript/%.o: %.cpp
mkdir -p $(dir $@)
$(call mkdir,$(dir $@))
$(EMCC.cc) $(OUTPUT_OPTION) $<
$(BUILD_DIR)/wasm/%.o: %.c
$(call mkdir,$(dir $@))
$(EMCC.c) $(OUTPUT_OPTION) $<
$(BUILD_DIR)/wasm/%.o: %.cpp
$(call mkdir,$(dir $@))
$(EMCC.cc) $(OUTPUT_OPTION) $<
$(BUILD_DIR)/tests/%: tests/%.c $(DEBUG_OBJECTS)
mkdir -p $(dir $@)
$(LINK.c) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
$(call mkdir,$(dir $@))
$(LINK.c) -o $@ $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS)
$(BUILD_DIR)/tests/%: tests/%.cpp $(DEBUG_OBJECTS)
mkdir -p $(dir $@)
$(LINK.cc) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
$(call mkdir,$(dir $@))
$(LINK.cc) -o $@ $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS)
$(BUILD_DIR)/fuzzers/objects/%.o: %.c
mkdir -p $(dir $@)
$(call mkdir,$(dir $@))
$(AFL.c) $(OUTPUT_OPTION) $<
$(BUILD_DIR)/fuzzers/objects/%.o: %.cpp
mkdir -p $(dir $@)
$(call mkdir,$(dir $@))
$(AFL.cc) $(OUTPUT_OPTION) $<
$(BUILD_DIR)/fuzzers/fuzz_%: fuzzers/fuzz_%.c $(FUZZER_OBJECTS)
$(AFL_LINK.c) $< $(FUZZER_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
$(BUILD_DIR)/fuzzers/objects/asan_%.o: %.c
$(call mkdir,$(dir $@))
$(AFL_ASAN.c) $(OUTPUT_OPTION) $<
$(BUILD_DIR)/fuzzers/fuzz_%: fuzzers/fuzz_%.cpp $(FUZZER_OBJECTS)
$(AFL_LINK.cc) $< $(FUZZER_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
$(BUILD_DIR)/fuzzers/objects/asan_%.o: %.cpp
$(call mkdir,$(dir $@))
$(AFL_ASAN.cc) $(OUTPUT_OPTION) $<
$(BUILD_DIR)/fuzzers/debug_%: fuzzers/fuzz_%.c $(DEBUG_OBJECTS)
$(LINK.c) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
$(BUILD_DIR)/fuzzers/objects/msan_%.o: %.c
$(call mkdir,$(dir $@))
$(AFL_MSAN.c) $(OUTPUT_OPTION) $<
$(BUILD_DIR)/fuzzers/debug_%: fuzzers/fuzz_%.cpp $(DEBUG_OBJECTS)
$(LINK.cc) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
$(BUILD_DIR)/fuzzers/objects/msan_%.o: %.cpp
$(call mkdir,$(dir $@))
$(AFL_MSAN.cc) $(OUTPUT_OPTION) $<
$(BUILD_DIR)/fuzzers/objects/debug_%.o: %.c
$(call mkdir,$(dir $@))
$(COMPILE.c) $(OUTPUT_OPTION) $<
$(BUILD_DIR)/fuzzers/objects/debug_%.o: %.cpp
$(call mkdir,$(dir $@))
$(COMPILE.cc) $(OUTPUT_OPTION) $<
$(BUILD_DIR)/fuzzers/fuzz_%: fuzzing/fuzzers/fuzz_%.c $(FUZZER_OBJECTS)
$(AFL_LINK.c) -o $@ $< $(FUZZER_OBJECTS) $(LOADLIBES) $(LDLIBS)
$(BUILD_DIR)/fuzzers/fuzz_%: fuzzing/fuzzers/fuzz_%.cpp $(FUZZER_OBJECTS)
$(AFL_LINK.cc) -o $@ $< $(FUZZER_OBJECTS) $(LOADLIBES) $(LDLIBS)
$(BUILD_DIR)/fuzzers/debug_%: fuzzing/fuzzers/fuzz_%.c $(FUZZER_DEBUG_OBJECTS)
$(LINK.c) -o $@ $< $(FUZZER_DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS)
$(BUILD_DIR)/fuzzers/debug_%: fuzzing/fuzzers/fuzz_%.cpp $(FUZZER_DEBUG_OBJECTS)
$(LINK.cc) -o $@ $< $(FUZZER_DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS)
$(BUILD_DIR)/fuzzers/fuzz_%_asan: fuzzing/fuzzers/fuzz_%.c $(FUZZER_ASAN_OBJECTS)
$(AFL_LINK_ASAN.c) -o $@ $< $(FUZZER_ASAN_OBJECTS) $(LOADLIBES) $(LDLIBS)
$(BUILD_DIR)/fuzzers/fuzz_%_asan: fuzzing/fuzzers/fuzz_%.cpp $(FUZZER_ASAN_OBJECTS)
$(AFL_LINK_ASAN.cc) -o $@ $< $(FUZZER_ASAN_OBJECTS) $(LOADLIBES) $(LDLIBS)
$(BUILD_DIR)/fuzzers/fuzz_%_msan: fuzzing/fuzzers/fuzz_%.c $(FUZZER_MSAN_OBJECTS)
$(AFL_LINK_MSAN.c) -o $@ $< $(FUZZER_MSAN_OBJECTS) $(LOADLIBES) $(LDLIBS)
$(BUILD_DIR)/fuzzers/fuzz_%_msan: fuzzing/fuzzers/fuzz_%.cpp $(FUZZER_MSAN_OBJECTS)
$(AFL_LINK_MSAN.cc) -o $@ $< $(FUZZER_MSAN_OBJECTS) $(LOADLIBES) $(LDLIBS)
%.html: %.rst
rst2html $< $@
%.html: %.md
pandoc --from markdown --to html5 --standalone --lua-filter gitlab-math.lua --katex -o $@ $<
### dependencies
-include $(RELEASE_OBJECTS:.o=.d)
@ -233,5 +410,10 @@ $(BUILD_DIR)/fuzzers/debug_%: fuzzers/fuzz_%.cpp $(DEBUG_OBJECTS)
-include $(JS_OBJECTS:.o=.d)
-include $(TEST_BINARIES:=.d)
-include $(FUZZER_OBJECTS:.o=.d)
-include $(FUZZER_DEBUG_OBJECTS:.o=.d)
-include $(FUZZER_ASAN_OBJECTS:.o=.d)
-include $(FUZZER_MSAN_OBJECTS:.o=.d)
-include $(FUZZER_BINARIES:=.d)
-include $(FUZZER_ASAN_BINARIES:=.d)
-include $(FUZZER_MSAN_BINARIES:=.d)
-include $(FUZZER_DEBUG_BINARIES:=.d)

63
OLMKit.podspec Normal file
View File

@ -0,0 +1,63 @@
Pod::Spec.new do |s|
# The libolm version
MAJOR = 3
MINOR = 2
PATCH = 16
s.name = "OLMKit"
s.version = "#{MAJOR}.#{MINOR}.#{PATCH}"
s.summary = "An Objective-C wrapper of olm (http://matrix.org/git/olm)"
s.description = <<-DESC
olm is an implementation of the Double Ratchet cryptographic ratchet in C++
DESC
s.homepage = "https://gitlab.matrix.org/matrix-org/olm"
s.license = { :type => "Apache License, Version 2.0", :file => "LICENSE" }
s.authors = { "Chris Ballinger" => "chrisballinger@gmail.com",
"matrix.org" => "support@matrix.org" }
s.ios.deployment_target = "6.0"
s.osx.deployment_target = "10.9"
# Expose the Objective-C wrapper API of libolm
s.public_header_files = "xcode/OLMKit/*.h"
s.source = {
:git => "https://gitlab.matrix.org/matrix-org/olm.git",
:tag => s.version.to_s
}
s.source_files = "xcode/OLMKit/*.{h,m}", "include/**/*.{h,hh}", "src/*.{c,cpp}", "lib/crypto-algorithms/sha256.c", "lib/crypto-algorithms/aes.c", "lib/curve25519-donna/curve25519-donna.c"
s.private_header_files = "xcode/OLMKit/*_Private.h"
# Those files (including .c) are included by ed25519.c. We do not want to compile them twice
s.preserve_paths = "lib/ed25519/**/*.{h,c}"
s.library = "c++"
# Use the same compiler options for C and C++ as olm/Makefile
s.compiler_flags = "-g -O3 -DOLMLIB_VERSION_MAJOR=#{MAJOR} -DOLMLIB_VERSION_MINOR=#{MINOR} -DOLMLIB_VERSION_PATCH=#{PATCH}"
# For headers search paths, manage first the normal installation. Then, use paths used
# when the pod is local
s.xcconfig = {
'USER_HEADER_SEARCH_PATHS' =>"${PODS_ROOT}/OLMKit/include ${PODS_ROOT}/OLMKit/lib #{File.join(File.dirname(__FILE__), 'include')} #{File.join(File.dirname(__FILE__), 'lib')}"
}
s.subspec 'olmc' do |olmc|
olmc.source_files = "src/*.{c}", "lib/curve25519-donna.h", "lib/crypto-algorithms/sha256.{h,c}", "lib/crypto-algorithms/aes.{h,c}", "lib/curve25519-donna/curve25519-donna.c"
olmc.compiler_flags = ' -std=c99 -fPIC'
end
s.subspec 'olmcpp' do |olmcpp|
olmcpp.source_files = "src/*.{cpp}"
olmcpp.compiler_flags = ' -std=c++11 -fPIC'
end
end

52
Package.swift Normal file
View File

@ -0,0 +1,52 @@
// swift-tools-version:5.3
import PackageDescription
let major = 3, minor = 2, patch = 16
let package = Package(
name: "Olm",
platforms: [.iOS(.v8), .macOS(.v10_10)],
products: [
.library(name: "libolm", targets: ["libolm"]),
.library(name: "OLMKit", targets: ["OLMKit"])
],
targets: [
.target(
name: "libolm",
path: ".",
sources: [
"src",
"lib/crypto-algorithms/aes.c",
"lib/crypto-algorithms/sha256.c",
"lib/curve25519-donna/curve25519-donna.c"
],
cSettings: [
.headerSearchPath("lib"),
.define("OLMLIB_VERSION_MAJOR", to: "\(major)"),
.define("OLMLIB_VERSION_MINOR", to: "\(minor)"),
.define("OLMLIB_VERSION_PATCH", to: "\(patch)")
]
),
.target(
name: "OLMKit",
dependencies: ["libolm"],
path: "xcode/OLMKit",
exclude: ["Info.plist"],
cSettings: [
.headerSearchPath("..")
]
),
.testTarget(
name: "OLMKitTests",
dependencies: ["OLMKit"],
path: "xcode/OLMKitTests",
exclude: ["Info.plist"],
cSettings: [
.headerSearchPath("..")
]
)
],
cLanguageStandard: .c99,
cxxLanguageStandard: .cxx11
)

328
README.md Normal file
View File

@ -0,0 +1,328 @@
# Olm
An implementation of the Double Ratchet cryptographic ratchet described by
https://whispersystems.org/docs/specifications/doubleratchet/, written in C and
C++11 and exposed as a C API.
The specification of the Olm ratchet can be found in [docs/olm.md](docs/olm.md).
This library also includes an implementation of the Megolm cryptographic
ratchet, as specified in [docs/megolm.md](docs/megolm.md).
## Installing
### Linux and other Unix-like systems
Your distribution may have pre-compiled packages available. If not, or if you
need a newer version, you will need to compile from source. See the "Building"
section below for more details.
### macOS
The easiest way to install on macOS is via Homebrew. If you do not have
Homebrew installed, follow the instructions at https://brew.sh/ to install it.
You can then install libolm by running
```bash
brew install libolm
```
If you also need the Python packages, you can run
```bash
pip3 install python-olm --global-option="build_ext" --global-option="--include-dirs="`brew --prefix libolm`"/include" --global-option="--library-dirs="`brew --prefix libolm`"/lib"
```
Note that this will install an older version of the Python bindings, which may
be missing some functions. If you need the latest version, you will need to
build from source.
### Windows
You will need to build from source. See the "Building" section below for more
details.
### Bindings
#### JavaScript
You can use pre-built npm packages, available at
<https://gitlab.matrix.org/matrix-org/olm/-/packages?type=npm>.
#### Python
A Python source package and pre-built packages for certain architectures from
<https://pypi.org/project/python-olm/>. If a pre-built package is not
available for your architecture, you will need:
- cmake (recommended) or GNU make
- a C/C++ compiler
to build the source package.
You can then run `pip install python-olm`.
Currently, we try to provide packages for all supported versions of Python on
x86-64, i686, and aarch64, but we cannot guarantee that packages for all
versions will be available on all architectures.
#### Android
Pre-built Android bindings are available at
<https://gitlab.matrix.org/matrix-org/olm/-/packages?type=Maven>.
## Building
To build olm as a shared library run:
```bash
cmake . -Bbuild
cmake --build build
```
To run the tests, run:
```bash
cd build/tests
ctest .
```
To build olm as a static library (which still needs libstdc++ dynamically) run:
```bash
cmake . -Bbuild -DBUILD_SHARED_LIBS=NO
cmake --build build
```
The library can also be used as a dependency with CMake using:
```cmake
find_package(Olm::Olm REQUIRED)
target_link_libraries(my_exe Olm::Olm)
```
### Bindings
#### JavaScript
The recommended way to build the JavaScript bindings is using
[Nix](https://nixos.org/). With Nix, you can run
```bash
nix build .\#javascript
```
to build the bindings.
If you do not have Nix you can, install emscripten from https://emscripten.org/
and then run:
```bash
make js
```
Emscripten can also be run via Docker, in which case, you need to pass through
the EMCC_CLOSURE_ARGS environment variable.
#### Android
To build the android project for Android bindings, run:
```bash
cd android
./gradlew clean build
```
#### Objective-C
To build the Xcode workspace for Objective-C bindings, run:
```bash
cd xcode
pod install
open OLMKit.xcworkspace
```
#### Python
To build the Python 3 bindings, first build olm as a library as above, and
then run:
```bash
cd python
make
```
### Using make instead of cmake
**WARNING:** Using cmake is the preferred method for building the olm library;
the Makefile may be removed in the future or have functionality removed. In
addition, the Makefile may make certain assumptions about your system and is
not as well tested.
To build olm as a dynamic library, run:
```bash
make
```
To run the tests, run:
```bash
make test
```
To build olm as a static library, run:
```bash
make static
```
## Bindings
libolm can be used in different environments using bindings. In addition to the
JavaScript, Python, Java (Android), and Objective-C bindings included in this
repository, some bindings are (in alphabetical order):
- [cl-megolm](https://github.com/K1D77A/cl-megolm) (MIT) Common Lisp bindings
- [dart-olm](https://gitlab.com/famedly/company/frontend/libraries/dart-olm) (AGPLv3) Dart bindings
- [Dhole/go-olm](https://github.com/Dhole/go-olm) (Apache-2.0) Go bindings
- [jOlm](https://github.com/brevilo/jolm) (Apache-2.0) Java bindings
- [libQtOlm](https://gitlab.com/b0/libqtolm/) (GPLv3) Qt bindings
- [matrix-kt](https://github.com/Dominaezzz/matrix-kt) (Apache-2.0) Kotlin
library for Matrix, including Olm methods
- [maunium.net/go/mautrix/crypto/olm](https://github.com/tulir/mautrix-go/tree/master/crypto/olm)
(Apache-2.0) fork of Dhole/go-olm
- [nim-olm](https://codeberg.org/BarrOff/nim-olm) (MIT) Nim bindings
- [olm-sys](https://gitlab.gnome.org/BrainBlasted/olm-sys) (Apache-2.0) Rust
bindings
- [Trixnity](https://gitlab.com/trixnity/trixnity) (Apache-2.0) Kotlin SDK for
Matrix, including Olm bindings
Note that bindings may have a different license from libolm, and are *not*
endorsed by the Matrix.org Foundation C.I.C.
## Release process
First: bump version numbers in ``common.mk``, ``CMakeLists.txt``,
``javascript/package.json``, ``python/pyproject.toml``, ``OLMKit.podspec``,
``Package.swift``, and ``android/gradle.properties``.
Also, ensure the changelog is up to date, and that everything is committed to
git.
It's probably sensible to do the above on a release branch (``release-vx.y.z``
by convention), and merge back to master once the release is complete.
```bash
make clean
# build and test C library
make test
# build and test JS wrapper
make js
(cd javascript && \
npm run test && \
sha256sum olm.js olm_legacy.js olm.wasm > checksums.txt && \
gpg -b -a -u F75FDC22C1DE8453 checksums.txt && \
npm publish)
VERSION=x.y.z
git tag $VERSION -s
git push --tags
# OLMKit CocoaPod release
# Make sure the version OLMKit.podspec is the same as the git tag
# (this must be checked before git tagging)
pod spec lint OLMKit.podspec --use-libraries --allow-warnings
pod trunk push OLMKit.podspec --use-libraries --allow-warnings
# Check the pod has been successully published with:
pod search OLMKit
```
Python and JavaScript packages are published to the registry at
<https://gitlab.matrix.org/matrix-org/olm/-/packages>. The GitLab
documentation contains instructions on how to set up twine (Python) and npm
(JavaScript) to upload to the registry.
To publish the Android library to MavenCentral (you will need some secrets), in the /android folder:
- Run the command `./gradlew clean build publish --no-daemon --no-parallel --stacktrace`. The generated AAR must be approx 500 kb.
- Connect to https://s01.oss.sonatype.org
- Click on Staging Repositories and check the the files have been uploaded
- Click on close
- Wait (check Activity tab until step "Repository closed" is displayed)
- Click on release. The staging repository will disappear
- Check that the release is available in https://repo1.maven.org/maven2/org/matrix/android/olm-sdk/ (it can take a few minutes)
## Design
Olm is designed to be easy port to different platforms and to be easy
to write bindings for.
It was originally implemented in C++, with a plain-C layer providing the public
API. As development has progressed, it has become clear that C++ gives little
advantage, and new functionality is being added in C, with C++ parts being
rewritten as the need ariases.
### Error Handling
All C functions in the API for olm return ``olm_error()`` on error.
This makes it easy to check for error conditions within the language bindings.
### Random Numbers
Olm doesn't generate random numbers itself. Instead the caller must
provide the random data. This makes it easier to port the library to different
platforms since the caller can use whatever cryptographic random number
generator their platform provides.
### Memory
Olm avoids calling malloc or allocating memory on the heap itself.
Instead the library calculates how much memory will be needed to hold the
output and the caller supplies a buffer of the appropriate size.
### Output Encoding
Binary output is encoded as base64 so that languages that prefer unicode
strings will find it easier to handle the output.
### Dependencies
Olm uses pure C implementations of the cryptographic primitives used by
the ratchet. While this decreases the performance it makes it much easier
to compile the library for different architectures.
## Contributing
Please see [CONTRIBUTING.md](CONTRIBUTING.md) when making contributions to the library.
## Security assessment
Olm 1.3.0 was independently assessed by NCC Group's Cryptography Services
Practive in September 2016 to check for security issues: you can read all
about it at
https://www.nccgroup.com/globalassets/our-research/us/public-reports/2016/november/ncc_group_olm_cryptogrpahic_review_2016_11_01.pdf
and https://matrix.org/blog/2016/11/21/matrixs-olm-end-to-end-encryption-security-assessment-released-and-implemented-cross-platform-on-riot-at-last/
## Security issues
If you think you found a security issue in libolm, any of its bindings or the Olm/Megolm protocols, please follow our [Security Disclosure Policy](https://matrix.org/security-disclosure-policy/) to report.
## Bug reports
For non-sensitive bugs, please file bug reports at https://github.com/matrix-org/olm/issues.
## What's an olm?
It's a really cool species of European troglodytic salamander.
http://www.postojnska-jama.eu/en/come-and-visit-us/vivarium-proteus/
## Legal Notice
The software may be subject to the U.S. export control laws and regulations
and by downloading the software the user certifies that he/she/it is
authorized to do so in accordance with those export control laws and
regulations.

View File

@ -1,106 +0,0 @@
Olm
===
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
Building
--------
To build olm as a shared library run:
.. code:: bash
make
To run the tests run:
.. code:: bash
make test
To build the javascript bindings, install emscripten from http://kripken.github.io/emscripten-site/ and then run:
.. code:: bash
make js
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``
It's probably sensible to do the above on a release branch (``release-vx.y.z``
by convention), and merge back to master once complete.
Design
------
Olm is designed to be easy port to different platforms and to be easy
to write bindings for.
It was originally implemented in C++, with a plain-C layer providing the public
API. As development has progressed, it has become clear that C++ gives little
advantage, and new functionality is being added in C, with C++ parts being
rewritten as the need ariases.
Error Handling
~~~~~~~~~~~~~~
All C functions in the API for olm return ``olm_error()`` on error.
This makes it easy to check for error conditions within the language bindings.
Random Numbers
~~~~~~~~~~~~~~
Olm doesn't generate random numbers itself. Instead the caller must
provide the random data. This makes it easier to port the library to different
platforms since the caller can use whatever cryptographic random number
generator their platform provides.
Memory
~~~~~~
Olm avoids calling malloc or allocating memory on the heap itself.
Instead the library calculates how much memory will be needed to hold the
output and the caller supplies a buffer of the appropriate size.
Output Encoding
~~~~~~~~~~~~~~~
Binary output is encoded as base64 so that languages that prefer unicode
strings will find it easier to handle the output.
Dependencies
~~~~~~~~~~~~
Olm uses pure C implementations of the cryptographic primitives used by
the ratchet. While this decreases the performance it makes it much easier
to compile the library for different architectures.
What's an olm?
--------------
It's a really cool species of European troglodytic salamander.
http://www.postojnska-jama.eu/en/come-and-visit-us/vivarium-proteus/
Legal Notice
------------
The software may be subject to the U.S. export control laws and regulations
and by downloading the software the user certifies that he/she/it is
authorized to do so in accordance with those export control laws and
regulations.

29
Windows64.cmake Normal file
View File

@ -0,0 +1,29 @@
# Cross-compile for Windows (64-bit) using Mingw-w64
# Build using:
# cmake . -Bbuild -DCMAKE_TOOLCHAIN_FILE=Windows64.cmake
# cmake --build build
# from @ticho:cyberdi.sk
# https://paste.debian.net/1201338/
# the name of the target operating system
SET(CMAKE_SYSTEM_NAME Windows)
# which compilers to use for C and C++
SET(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc-posix)
SET(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++-posix)
SET(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
# here is the target environment located
SET(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)
# adjust the default behaviour of the FIND_XXX() commands:
# search headers and libraries in the target environment, search
# programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# static-link against the standard libraries
set(CMAKE_CXX_STANDARD_LIBRARIES "-static-libgcc -static-libstdc++")

22
android/.gitignore vendored Normal file
View File

@ -0,0 +1,22 @@
#IDEs
/.idea
*.iml
#OS
.DS_Store
#builds
/build
*.apk
.gradle
/local.properties
# Native build
/olm-sdk/src/main/obj
/olm-sdk/doc
/olm-sdk/src/main/libs/**/*.so
# Test
/olm-sdk/src/main/libs/**/gdbserver
/olm-sdk/src/main/libs/**/gdb.setup

46
android/.gitlab-ci.yml Normal file
View File

@ -0,0 +1,46 @@
# TODO: consider replacing this with a smaller image
image: docker.io/inovex/gitlab-ci-android
stages:
- build
- test
variables:
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
before_script:
- export GRADLE_USER_HOME=$(pwd)/.gradle
- export ANDROID_HOME=${ANDROID_SDK_HOME}
- echo "sdk.dir=${ANDROID_SDK_HOME}" > ./android/local.properties
- echo "ndk.dir=${ANDROID_NDK_HOME}" >> ./android/local.properties
- cp -R $ANDROID_SDK_ROOT/licenses ./android/.
- chmod +x ./android/gradlew
cache:
key: ${CI_PROJECT_ID}
paths:
- android/.gradle/
build:android:aar:
stage: build
tags:
- docker
script:
- pushd android
- ./gradlew clean assembleRelease
artifacts:
expire_in: 1 weeks
paths:
- android/olm-sdk/build/outputs/aar/*.aar
- android/local.properties
test:android:aar:
stage: test
tags:
- docker
script:
- pushd android
- ./gradlew assembleAndroidTest
# TODO: Add emulator to run tests
needs:
- build:android:aar

25
android/README.rst Normal file
View File

@ -0,0 +1,25 @@
OlmLibSdk
=========
OlmLibSdk exposes an android wrapper to libolm.
Installation
------------
Android Olm library is released on MavenCentral.
Add this dependency to your project:
```groovy
implementation "org.matrix.android:olm:3.2.8"
```
Latest version: ![Latest version](https://img.shields.io/maven-central/v/org.matrix.android/olm)
Development
-----------
import the project from the ``android/`` path.
The project contains some JNI files and some Java wrapper files.
The project contains some tests under AndroidTests package.

38
android/build.gradle Normal file
View File

@ -0,0 +1,38 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
mavenCentral()
google()
}
dependencies {
// Release notes of Android Gradle Plugin (AGP):
// https://developer.android.com/studio/releases/gradle-plugin
classpath 'com.android.tools.build:gradle:7.0.4'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.18.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
mavenCentral()
google()
}
plugins.withId("com.vanniktech.maven.publish.base") {
group = project.getProperties().getOrDefault("GROUP", "0.0.0")
version = project.getProperties().getOrDefault("VERSION_NAME", "name")
mavenPublishing {
publishToMavenCentral("S01")
pomFromGradleProperties()
signAllPublications()
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

49
android/gradle.properties Normal file
View File

@ -0,0 +1,49 @@
## Project-wide Gradle settings.
#
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
#
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
#
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
#Wed Oct 05 11:49:34 CEST 2016
#systemProp.https.proxyPort=8080
#systemProp.http.proxyHost=batproxy
#systemProp.https.proxyHost=batproxy
#systemProp.http.proxyPort=8080
android.useAndroidX=true
org.gradle.configureondemand=false
# Maven publication
# Ref: https://github.com/vanniktech/gradle-maven-publish-plugin
GROUP=org.matrix.android
POM_ARTIFACT_ID=olm
VERSION_NAME=3.2.16
POM_PACKAGING=aar
POM_NAME=Olm Android wrapper
POM_DESCRIPTION=An Android wrapper to libolm.
POM_INCEPTION_YEAR=2021
POM_URL=https://gitlab.matrix.org/matrix-org/olm
POM_LICENSE_NAME=The Apache Software License, Version 2.0
POM_LICENCE_URL=https://www.apache.org/licenses/LICENSE-2.0.txt
POM_LICENCE_DIST=repo
POM_SCM_URL=https://gitlab.matrix.org/matrix-org/olm
POM_SCM_CONNECTION=scm:git:https://gitlab.matrix.org/matrix-org/olm.git
POM_SCM_DEV_CONNECTION=scm:git:ssh://git@gitlab.int.matrix.org:matrix-org/olm.git
POM_DEVELOPER_ID=matrixdev
POM_DEVELOPER_NAME=matrixdev
POM_DEVELOPER_URL=https://gitlab.matrix.org/matrix-org
POM_DEVELOPER_EMAIL=android@element.io

Binary file not shown.

View File

@ -0,0 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=c9490e938b221daf0094982288e4038deed954a3f12fb54cbf270ddf4e37d879
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

160
android/gradlew vendored Executable file
View File

@ -0,0 +1,160 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

90
android/gradlew.bat vendored Normal file
View File

@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -0,0 +1,167 @@
import org.apache.tools.ant.taskdefs.condition.Os
import com.vanniktech.maven.publish.AndroidLibrary
import com.vanniktech.maven.publish.JavadocJar
apply plugin: 'com.android.library'
apply plugin: "com.vanniktech.maven.publish.base"
android {
compileSdk 31
defaultConfig {
minSdk 14
targetSdk 31
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField "String", "OLM_VERSION", "\"${project.getProperties().getOrDefault("VERSION_NAME", "0.0.0")}\""
// The following argument makes the Android Test Orchestrator run its
// "pm clear" command after each test invocation. This command ensures
// that the app's state is completely cleared between tests.
testInstrumentationRunnerArguments clearPackageData: 'true'
buildConfigField "String", "OLM_VERSION", "\"${project.getProperties().getOrDefault("VERSION_NAME", "0.0.0")}\""
}
buildTypes {
debug {
resValue "string", "git_olm_revision", "\"${gitRevision()}\""
resValue "string", "git_olm_revision_unix_date", "\"${gitRevisionUnixDate()}\""
resValue "string", "git_olm_revision_date", "\"${gitRevisionDate()}\""
}
release {
resValue "string", "git_olm_revision", "\"${gitRevision()}\""
resValue "string", "git_olm_revision_unix_date", "\"${gitRevisionUnixDate()}\""
resValue "string", "git_olm_revision_date", "\"${gitRevisionDate()}\""
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets.main {
jniLibs.srcDir 'src/main/libs'
jni.srcDirs = []
}
task buildJavaDoc(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
destinationDir = file("./doc/")
options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PRIVATE
failOnError false
}
task ndkBuildNativeRelease(type: Exec, description: 'NDK building..') {
println 'ndkBuildNativeRelease starts..'
workingDir file('src/main')
commandLine getNdkBuildCmd(), 'NDK_DEBUG=0'
}
task ndkBuildNativeDebug(type: Exec, description: 'NDK building..') {
println 'ndkBuildNativeDebug starts..'
workingDir file('src/main')
commandLine getNdkBuildCmd(), 'NDK_DEBUG=1'
}
task cleanNative(type: Exec, description: 'Clean NDK build') {
workingDir file('src/main')
commandLine getNdkBuildCmd(), 'clean'
}
tasks.withType(JavaCompile) {
compileTask ->
if (compileTask.name.startsWith('compileDebugJava')) {
println 'test compile: Debug'
compileTask.dependsOn ndkBuildNativeDebug
} else if (compileTask.name.startsWith('compileReleaseJava')) {
println 'test compile: Release'
compileTask.dependsOn ndkBuildNativeRelease
}
compileTask.dependsOn buildJavaDoc
}
task androidJavadocs(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
android.libraryVariants.all { variant ->
if (variant.name == 'release') {
owner.classpath += variant.javaCompileProvider.get().classpath
}
}
exclude '**/R.html', '**/R.*.html', '**/index.html'
}
task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
archiveClassifier.set('javadoc')
from androidJavadocs.destinationDir
}
task androidSourcesJar(type: Jar) {
archiveClassifier.set('sources')
from android.sourceSets.main.java.srcDirs
}
clean.dependsOn cleanNative
libraryVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFileName
if (outputFile != null && outputFile.endsWith('.aar')) {
output.outputFileName = outputFile.replace(".aar", "-${version}.aar")
}
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
}
def getNdkFolder() {
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def ndkFolder = properties.getProperty('ndk.dir', null)
if (ndkFolder == null)
throw new GradleException("NDK location missing. Define it with ndk.dir in the local.properties file")
return ndkFolder
}
def getNdkBuildCmd() {
def ndkBuildCmd = getNdkFolder() + "/ndk-build"
if (Os.isFamily(Os.FAMILY_WINDOWS))
ndkBuildCmd += ".cmd"
return ndkBuildCmd
}
def gitRevision() {
def cmd = "git rev-parse --short HEAD"
return cmd.execute().text.trim()
}
def gitRevisionUnixDate() {
def cmd = "git show -s --format=%ct HEAD^{commit}"
return cmd.execute().text.trim()
}
def gitRevisionDate() {
def cmd = "git show -s --format=%ci HEAD^{commit}"
return cmd.execute().text.trim()
}
dependencies {
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test:core:1.4.0'
androidTestImplementation 'androidx.test:runner:1.4.0'
androidTestImplementation 'androidx.test:rules:1.4.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
}
mavenPublishing {
configure(new AndroidLibrary(new JavadocJar.Empty(), false))
}

View File

@ -0,0 +1,506 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016 Vector Creations 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.
*/
package org.matrix.olm;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@RunWith(AndroidJUnit4.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class OlmAccountTest {
private static final String LOG_TAG = "OlmAccountTest";
private static final int GENERATION_ONE_TIME_KEYS_NUMBER = 50;
private static OlmAccount mOlmAccount;
private static OlmManager mOlmManager;
private boolean mIsAccountCreated;
private final String FILE_NAME = "SerialTestFile";
@BeforeClass
public static void setUpClass() {
// load native lib
mOlmManager = new OlmManager();
String olmLibVersion = mOlmManager.getOlmLibVersion();
assertNotNull(olmLibVersion);
String olmSdkVersion = mOlmManager.getDetailedVersion(ApplicationProvider.getApplicationContext());
assertNotNull(olmLibVersion);
Log.d(LOG_TAG, "## setUpClass(): Versions - Android Olm SDK = " + olmSdkVersion + " Olm lib =" + olmLibVersion);
}
@AfterClass
public static void tearDownClass() {
// TBD
}
@Before
public void setUp() {
if (mIsAccountCreated) {
assertNotNull(mOlmAccount);
}
}
@After
public void tearDown() {
// TBD
}
/**
* Basic test: creation and release.
*/
@Test
public void test01CreateReleaseAccount() {
try {
mOlmAccount = new OlmAccount();
} catch (OlmException e) {
e.printStackTrace();
fail("OlmAccount failed " + e.getMessage());
}
assertNotNull(mOlmAccount);
mOlmAccount.releaseAccount();
assertEquals(0, mOlmAccount.getOlmAccountId());
}
@Test
public void test02CreateAccount() {
try {
mOlmAccount = new OlmAccount();
} catch (OlmException e) {
e.printStackTrace();
fail("OlmAccount failed " + e.getMessage());
}
assertNotNull(mOlmAccount);
mIsAccountCreated = true;
}
@Test
public void test04GetOlmAccountId() {
long olmNativeInstance = mOlmAccount.getOlmAccountId();
Log.d(LOG_TAG, "## testGetOlmAccountId olmNativeInstance=" + olmNativeInstance);
assertTrue(0 != olmNativeInstance);
}
/**
* Test if {@link OlmAccount#identityKeys()} returns a JSON object
* that contains the following keys: {@link OlmAccount#JSON_KEY_FINGER_PRINT_KEY}
* and {@link OlmAccount#JSON_KEY_IDENTITY_KEY}
*/
@Test
public void test05IdentityKeys() {
Map<String, String> identityKeys = null;
try {
identityKeys = mOlmAccount.identityKeys();
} catch (Exception e) {
fail("identityKeys failed " + e.getMessage());
}
assertNotNull(identityKeys);
Log.d(LOG_TAG, "## testIdentityKeys Keys=" + identityKeys);
// is JSON_KEY_FINGER_PRINT_KEY present?
String fingerPrintKey = TestHelper.getFingerprintKey(identityKeys);
assertFalse("fingerprint key missing", TextUtils.isEmpty(fingerPrintKey));
// is JSON_KEY_IDENTITY_KEY present?
String identityKey = TestHelper.getIdentityKey(identityKeys);
assertFalse("identity key missing", TextUtils.isEmpty(identityKey));
}
//****************************************************
//***************** ONE TIME KEYS TESTS **************
//****************************************************
@Test
public void test06MaxOneTimeKeys() {
long maxOneTimeKeys = mOlmAccount.maxOneTimeKeys();
Log.d(LOG_TAG, "## testMaxOneTimeKeys(): maxOneTimeKeys=" + maxOneTimeKeys);
assertTrue(maxOneTimeKeys > 0);
}
/**
* Test one time keys generation.
*/
@Test
public void test07GenerateOneTimeKeys() {
String error = null;
try {
mOlmAccount.generateOneTimeKeys(GENERATION_ONE_TIME_KEYS_NUMBER);
} catch (Exception e) {
error = e.getMessage();
}
assertNull(error);
}
/**
* Test the generated amount of one time keys = GENERATION_ONE_TIME_KEYS_NUMBER.
*/
@Test
public void test08OneTimeKeysJsonFormat() {
int oneTimeKeysCount = 0;
Map<String, Map<String, String>> oneTimeKeysJson = null;
try {
oneTimeKeysJson = mOlmAccount.oneTimeKeys();
} catch (Exception e) {
fail(e.getMessage());
}
assertNotNull(oneTimeKeysJson);
try {
Map<String, String> map = oneTimeKeysJson.get(OlmAccount.JSON_KEY_ONE_TIME_KEY);
assertNotNull(OlmAccount.JSON_KEY_ONE_TIME_KEY + " object is missing", map);
// test the count of the generated one time keys:
oneTimeKeysCount = map.size();
assertEquals("Expected count=" + GENERATION_ONE_TIME_KEYS_NUMBER + " found=" + oneTimeKeysCount, GENERATION_ONE_TIME_KEYS_NUMBER, oneTimeKeysCount);
} catch (Exception e) {
fail("Exception MSg=" + e.getMessage());
}
}
@Test
public void test10RemoveOneTimeKeysForSession() {
OlmSession olmSession = null;
try {
olmSession = new OlmSession();
} catch (OlmException e) {
fail("Exception Msg=" + e.getMessage());
}
long sessionId = olmSession.getOlmSessionId();
assertTrue(0 != sessionId);
String errorMessage = null;
try {
mOlmAccount.removeOneTimeKeys(olmSession);
} catch (Exception e) {
errorMessage = e.getMessage();
}
assertNotNull(errorMessage);
olmSession.releaseSession();
sessionId = olmSession.getOlmSessionId();
assertEquals(0, sessionId);
}
@Test
public void test11MarkOneTimeKeysAsPublished() {
try {
mOlmAccount.markOneTimeKeysAsPublished();
} catch (Exception e) {
fail(e.getMessage());
}
}
@Test
public void test12SignMessage() {
String clearMsg = "String to be signed by olm";
String signedMsg = null;
try {
signedMsg = mOlmAccount.signMessage(clearMsg);
} catch (Exception e) {
fail(e.getMessage());
}
assertNotNull(signedMsg);
// additional tests are performed in test01VerifyEd25519Signing()
}
// ********************************************************
// ************* SERIALIZATION TEST ***********************
// ********************************************************
@Test
public void test13Serialization() {
FileOutputStream fileOutput;
ObjectOutputStream objectOutput;
OlmAccount accountRef = null;
OlmAccount accountDeserial;
try {
accountRef = new OlmAccount();
} catch (OlmException e) {
fail(e.getMessage());
}
try {
accountRef.generateOneTimeKeys(GENERATION_ONE_TIME_KEYS_NUMBER);
} catch (Exception e) {
fail(e.getMessage());
}
// get keys references
Map<String, String> identityKeysRef = null;
try {
identityKeysRef = accountRef.identityKeys();
} catch (Exception e) {
fail("identityKeys failed " + e.getMessage());
}
Map<String, Map<String, String>> oneTimeKeysRef = null;
try {
oneTimeKeysRef = accountRef.oneTimeKeys();
} catch (Exception e) {
fail(e.getMessage());
}
assertNotNull(identityKeysRef);
assertNotNull(oneTimeKeysRef);
try {
Context context = ApplicationProvider.getApplicationContext();
//context.getFilesDir();
fileOutput = context.openFileOutput(FILE_NAME, Context.MODE_PRIVATE);
// serialize account
objectOutput = new ObjectOutputStream(fileOutput);
objectOutput.writeObject(accountRef);
objectOutput.flush();
objectOutput.close();
// deserialize account
FileInputStream fileInput = context.openFileInput(FILE_NAME);
ObjectInputStream objectInput = new ObjectInputStream(fileInput);
accountDeserial = (OlmAccount) objectInput.readObject();
objectInput.close();
assertNotNull(accountDeserial);
// get de-serialized keys
Map<String, String> identityKeysDeserial = accountDeserial.identityKeys();
Map<String, Map<String, String>> oneTimeKeysDeserial = accountDeserial.oneTimeKeys();
assertNotNull(identityKeysDeserial);
assertNotNull(oneTimeKeysDeserial);
// compare identity keys
assertEquals(identityKeysDeserial.toString(), identityKeysRef.toString());
// compare onetime keys
assertEquals(oneTimeKeysDeserial.toString(), oneTimeKeysRef.toString());
accountRef.releaseAccount();
accountDeserial.releaseAccount();
} catch (FileNotFoundException e) {
Log.e(LOG_TAG, "## test13Serialization(): Exception FileNotFoundException Msg==" + e.getMessage());
fail("test13Serialization failed " + e.getMessage());
} catch (ClassNotFoundException e) {
Log.e(LOG_TAG, "## test13Serialization(): Exception ClassNotFoundException Msg==" + e.getMessage());
fail("test13Serialization failed " + e.getMessage());
} catch (IOException e) {
Log.e(LOG_TAG, "## test13Serialization(): Exception IOException Msg==" + e.getMessage());
fail("test13Serialization failed " + e.getMessage());
}
/*catch (OlmException e) {
Log.e(LOG_TAG, "## test13Serialization(): Exception OlmException Msg==" + e.getMessage());
}*/ catch (Exception e) {
Log.e(LOG_TAG, "## test13Serialization(): Exception Msg==" + e.getMessage());
fail("test13Serialization failed " + e.getMessage());
}
}
// ****************************************************
// *************** SANITY CHECK TESTS *****************
// ****************************************************
@Test
public void test14GenerateOneTimeKeysError() {
// keys number = 0 => no error
String errorMessage = null;
try {
mOlmAccount.generateOneTimeKeys(0);
} catch (Exception e) {
errorMessage = e.getMessage();
}
assertNull(errorMessage);
// keys number = negative value
errorMessage = null;
try {
mOlmAccount.generateOneTimeKeys(-50);
} catch (Exception e) {
errorMessage = e.getMessage();
}
assertNotNull(errorMessage);
}
@Test
public void test15RemoveOneTimeKeysForSessionError() {
OlmAccount olmAccount = null;
try {
olmAccount = new OlmAccount();
} catch (OlmException e) {
fail(e.getMessage());
}
try {
olmAccount.removeOneTimeKeys(null);
} catch (Exception e) {
fail(e.getMessage());
}
olmAccount.releaseAccount();
}
@Test
public void test16SignMessageError() {
OlmAccount olmAccount = null;
try {
olmAccount = new OlmAccount();
} catch (OlmException e) {
fail(e.getMessage());
}
String signedMsg = null;
try {
signedMsg = olmAccount.signMessage(null);
} catch (Exception e) {
}
assertNull(signedMsg);
olmAccount.releaseAccount();
}
/**
* Create multiple accounts and check that identity keys are still different.
* This test validates random series are provide enough random values.
*/
@Test
public void test17MultipleAccountCreation() {
try {
OlmAccount account1 = new OlmAccount();
OlmAccount account2 = new OlmAccount();
OlmAccount account3 = new OlmAccount();
OlmAccount account4 = new OlmAccount();
OlmAccount account5 = new OlmAccount();
OlmAccount account6 = new OlmAccount();
OlmAccount account7 = new OlmAccount();
OlmAccount account8 = new OlmAccount();
OlmAccount account9 = new OlmAccount();
OlmAccount account10 = new OlmAccount();
Map<String, String> identityKeys1 = account1.identityKeys();
Map<String, String> identityKeys2 = account2.identityKeys();
Map<String, String> identityKeys3 = account3.identityKeys();
Map<String, String> identityKeys4 = account4.identityKeys();
Map<String, String> identityKeys5 = account5.identityKeys();
Map<String, String> identityKeys6 = account6.identityKeys();
Map<String, String> identityKeys7 = account7.identityKeys();
Map<String, String> identityKeys8 = account8.identityKeys();
Map<String, String> identityKeys9 = account9.identityKeys();
Map<String, String> identityKeys10 = account10.identityKeys();
String identityKey1 = TestHelper.getIdentityKey(identityKeys1);
String identityKey2 = TestHelper.getIdentityKey(identityKeys2);
assertNotEquals(identityKey1, identityKey2);
String identityKey3 = TestHelper.getIdentityKey(identityKeys3);
assertNotEquals(identityKey2, identityKey3);
String identityKey4 = TestHelper.getIdentityKey(identityKeys4);
assertNotEquals(identityKey3, identityKey4);
String identityKey5 = TestHelper.getIdentityKey(identityKeys5);
assertNotEquals(identityKey4, identityKey5);
String identityKey6 = TestHelper.getIdentityKey(identityKeys6);
assertNotEquals(identityKey5, identityKey6);
String identityKey7 = TestHelper.getIdentityKey(identityKeys7);
assertNotEquals(identityKey6, identityKey7);
String identityKey8 = TestHelper.getIdentityKey(identityKeys8);
assertNotEquals(identityKey7, identityKey8);
String identityKey9 = TestHelper.getIdentityKey(identityKeys9);
assertNotEquals(identityKey8, identityKey9);
String identityKey10 = TestHelper.getIdentityKey(identityKeys10);
assertNotEquals(identityKey9, identityKey10);
account1.releaseAccount();
account2.releaseAccount();
account3.releaseAccount();
account4.releaseAccount();
account5.releaseAccount();
account6.releaseAccount();
account7.releaseAccount();
account8.releaseAccount();
account9.releaseAccount();
account10.releaseAccount();
} catch (OlmException e) {
fail(e.getMessage());
}
}
@Test
public void test18GenerateFallbackKey() {
try {
OlmAccount account1 = new OlmAccount();
account1.generateFallbackKey();
Map<String, Map<String, String>> fallbackKeyMap = account1.fallbackKey();
assertNotNull(fallbackKeyMap);
assertEquals(1, fallbackKeyMap.size());
} catch (OlmException e) {
fail(e.getMessage());
}
}
}

View File

@ -0,0 +1,630 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016 Vector Creations 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.
*/
package org.matrix.olm;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@RunWith(AndroidJUnit4.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class OlmGroupSessionTest {
private static final String LOG_TAG = "OlmSessionTest";
private final String FILE_NAME_SERIAL_OUT_SESSION = "SerialOutGroupSession";
private final String FILE_NAME_SERIAL_IN_SESSION = "SerialInGroupSession";
private static OlmManager mOlmManager;
private static OlmOutboundGroupSession mAliceOutboundGroupSession;
private static String mAliceSessionIdentifier;
private static long mAliceMessageIndex;
private static final String CLEAR_MESSAGE1 = "Hello!";
private static String mAliceToBobMessage;
private static OlmInboundGroupSession mBobInboundGroupSession;
private static String mAliceOutboundSessionKey;
private static String mBobSessionIdentifier;
private static String mBobDecryptedMessage;
@BeforeClass
public static void setUpClass(){
// load native lib
mOlmManager = new OlmManager();
String version = mOlmManager.getOlmLibVersion();
assertNotNull(version);
Log.d(LOG_TAG, "## setUpClass(): lib version="+version);
}
/**
* Basic test:
* - alice creates an outbound group session
* - bob creates an inbound group session with alice's outbound session key
* - alice encrypts a message with its session
* - bob decrypts the encrypted message with its session
* - decrypted message is identical to original alice message
*/
@Test
public void test01CreateOutboundSession() {
// alice creates OUTBOUND GROUP SESSION
try {
mAliceOutboundGroupSession = new OlmOutboundGroupSession();
} catch (OlmException e) {
fail("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode());
}
}
@Test
public void test02GetOutboundGroupSessionIdentifier() {
// test session ID
mAliceSessionIdentifier = null;
try {
mAliceSessionIdentifier = mAliceOutboundGroupSession.sessionIdentifier();
} catch (Exception e) {
fail(e.getMessage());
}
assertNotNull(mAliceSessionIdentifier);
assertTrue(mAliceSessionIdentifier.length() > 0);
}
@Test
public void test03GetOutboundGroupSessionKey() {
// test session Key
mAliceOutboundSessionKey = null;
try {
mAliceOutboundSessionKey = mAliceOutboundGroupSession.sessionKey();
} catch (Exception e) {
fail(e.getMessage());
}
assertNotNull(mAliceOutboundSessionKey);
assertTrue(mAliceOutboundSessionKey.length() > 0);
}
@Test
public void test04GetOutboundGroupMessageIndex() {
// test message index before any encryption
mAliceMessageIndex = mAliceOutboundGroupSession.messageIndex();
assertEquals(0, mAliceMessageIndex);
}
@Test
public void test05OutboundGroupEncryptMessage() {
// alice encrypts a message to bob
try {
mAliceToBobMessage = mAliceOutboundGroupSession.encryptMessage(CLEAR_MESSAGE1);
} catch (Exception e) {
fail("Exception in bob encryptMessage, Exception code=" + e.getMessage());
}
assertFalse(TextUtils.isEmpty(mAliceToBobMessage));
// test message index after encryption is incremented
mAliceMessageIndex = mAliceOutboundGroupSession.messageIndex();
assertEquals(1, mAliceMessageIndex);
}
@Test
public void test06CreateInboundGroupSession() {
// bob creates INBOUND GROUP SESSION with alice outbound key
try {
mBobInboundGroupSession = new OlmInboundGroupSession(mAliceOutboundSessionKey);
} catch (OlmException e) {
fail("Exception in bob OlmInboundGroupSession, Exception code=" + e.getExceptionCode());
}
}
@Test
public void test08GetInboundGroupSessionIdentifier() {
// check both session identifiers are equals
mBobSessionIdentifier = null;
try {
mBobSessionIdentifier = mBobInboundGroupSession.sessionIdentifier();
} catch (Exception e) {
fail(e.getMessage());
}
assertFalse(TextUtils.isEmpty(mBobSessionIdentifier));
}
@Test
public void test09SessionIdentifiersAreIdentical() {
// check both session identifiers are equals: alice vs bob
assertEquals(mAliceSessionIdentifier, mBobSessionIdentifier);
}
@Test
public void test10InboundDecryptMessage() {
mBobDecryptedMessage = null;
OlmInboundGroupSession.DecryptMessageResult result = null;
try {
result = mBobInboundGroupSession.decryptMessage(mAliceToBobMessage);
} catch (Exception e) {
fail(e.getMessage());
}
// test decrypted message
mBobDecryptedMessage = result.mDecryptedMessage;
assertFalse(TextUtils.isEmpty(mBobDecryptedMessage));
assertEquals(0, result.mIndex);
}
@Test
public void test11InboundDecryptedMessageIdentical() {
// test decrypted message
assertEquals(mBobDecryptedMessage, CLEAR_MESSAGE1);
}
@Test
public void test12ReleaseOutboundSession() {
// release group sessions
mAliceOutboundGroupSession.releaseSession();
}
@Test
public void test13ReleaseInboundSession() {
// release group sessions
mBobInboundGroupSession.releaseSession();
}
@Test
public void test14CheckUnreleaseedCount() {
assertTrue(mAliceOutboundGroupSession.isReleased());
assertTrue(mBobInboundGroupSession.isReleased());
}
@Test
public void test15SerializeOutboundSession() {
OlmOutboundGroupSession outboundGroupSessionRef=null;
OlmOutboundGroupSession outboundGroupSessionSerial;
// create one OUTBOUND GROUP SESSION
try {
outboundGroupSessionRef = new OlmOutboundGroupSession();
} catch (OlmException e) {
fail("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode());
}
assertNotNull(outboundGroupSessionRef);
// serialize alice session
Context context = ApplicationProvider.getApplicationContext();
try {
FileOutputStream fileOutput = context.openFileOutput(FILE_NAME_SERIAL_OUT_SESSION, Context.MODE_PRIVATE);
ObjectOutputStream objectOutput = new ObjectOutputStream(fileOutput);
objectOutput.writeObject(outboundGroupSessionRef);
objectOutput.flush();
objectOutput.close();
// deserialize session
FileInputStream fileInput = context.openFileInput(FILE_NAME_SERIAL_OUT_SESSION);
ObjectInputStream objectInput = new ObjectInputStream(fileInput);
outboundGroupSessionSerial = (OlmOutboundGroupSession) objectInput.readObject();
assertNotNull(outboundGroupSessionSerial);
objectInput.close();
// get sessions keys
String sessionKeyRef = outboundGroupSessionRef.sessionKey();
String sessionKeySerial = outboundGroupSessionSerial.sessionKey();
assertFalse(TextUtils.isEmpty(sessionKeyRef));
assertFalse(TextUtils.isEmpty(sessionKeySerial));
// session keys comparison
assertEquals(sessionKeyRef, sessionKeySerial);
// get sessions IDs
String sessionIdRef = outboundGroupSessionRef.sessionIdentifier();
String sessionIdSerial = outboundGroupSessionSerial.sessionIdentifier();
assertFalse(TextUtils.isEmpty(sessionIdRef));
assertFalse(TextUtils.isEmpty(sessionIdSerial));
// session IDs comparison
assertEquals(sessionIdRef, sessionIdSerial);
outboundGroupSessionRef.releaseSession();
outboundGroupSessionSerial.releaseSession();
assertTrue(outboundGroupSessionRef.isReleased());
assertTrue(outboundGroupSessionSerial.isReleased());
} catch (FileNotFoundException e) {
Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception FileNotFoundException Msg=="+e.getMessage());
fail(e.getMessage());
} catch (ClassNotFoundException e) {
Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception ClassNotFoundException Msg==" + e.getMessage());
fail(e.getMessage());
} catch (OlmException e) {
Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception OlmException Msg==" + e.getMessage());
fail(e.getMessage());
} catch (IOException e) {
Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception IOException Msg==" + e.getMessage());
fail(e.getMessage());
} catch (Exception e) {
Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception Msg==" + e.getMessage());
fail(e.getMessage());
}
}
@Test
public void test16SerializeInboundSession() {
OlmOutboundGroupSession aliceOutboundGroupSession=null;
OlmInboundGroupSession bobInboundGroupSessionRef=null;
OlmInboundGroupSession bobInboundGroupSessionSerial;
// alice creates OUTBOUND GROUP SESSION
try {
aliceOutboundGroupSession = new OlmOutboundGroupSession();
} catch (OlmException e) {
fail("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode());
}
assertNotNull(aliceOutboundGroupSession);
// get the session key from the outbound group session
String sessionKeyRef = null;
try {
sessionKeyRef = aliceOutboundGroupSession.sessionKey();
} catch (Exception e) {
fail(e.getMessage());
}
assertNotNull(sessionKeyRef);
// bob creates INBOUND GROUP SESSION
try {
bobInboundGroupSessionRef = new OlmInboundGroupSession(sessionKeyRef);
} catch (OlmException e) {
fail("Exception in OlmInboundGroupSession, Exception code=" + e.getExceptionCode());
}
assertNotNull(bobInboundGroupSessionRef);
// serialize alice session
Context context = ApplicationProvider.getApplicationContext();
try {
FileOutputStream fileOutput = context.openFileOutput(FILE_NAME_SERIAL_IN_SESSION, Context.MODE_PRIVATE);
ObjectOutputStream objectOutput = new ObjectOutputStream(fileOutput);
objectOutput.writeObject(bobInboundGroupSessionRef);
objectOutput.flush();
objectOutput.close();
// deserialize session
FileInputStream fileInput = context.openFileInput(FILE_NAME_SERIAL_IN_SESSION);
ObjectInputStream objectInput = new ObjectInputStream(fileInput);
bobInboundGroupSessionSerial = (OlmInboundGroupSession)objectInput.readObject();
assertNotNull(bobInboundGroupSessionSerial);
objectInput.close();
// get sessions IDs
String aliceSessionId = aliceOutboundGroupSession.sessionIdentifier();
String sessionIdRef = bobInboundGroupSessionRef.sessionIdentifier();
String sessionIdSerial = bobInboundGroupSessionSerial.sessionIdentifier();
assertFalse(TextUtils.isEmpty(aliceSessionId));
assertFalse(TextUtils.isEmpty(sessionIdRef));
assertFalse(TextUtils.isEmpty(sessionIdSerial));
// session IDs comparison
assertEquals(aliceSessionId, sessionIdSerial);
assertEquals(sessionIdRef, sessionIdSerial);
aliceOutboundGroupSession.releaseSession();
bobInboundGroupSessionRef.releaseSession();
bobInboundGroupSessionSerial.releaseSession();
assertTrue(aliceOutboundGroupSession.isReleased());
assertTrue(bobInboundGroupSessionRef.isReleased());
assertTrue(bobInboundGroupSessionSerial.isReleased());
} catch (FileNotFoundException e) {
Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception FileNotFoundException Msg=="+e.getMessage());
fail(e.getMessage());
} catch (ClassNotFoundException e) {
Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception ClassNotFoundException Msg==" + e.getMessage());
fail(e.getMessage());
} catch (OlmException e) {
Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception OlmException Msg==" + e.getMessage());
fail(e.getMessage());
} catch (IOException e) {
Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception IOException Msg==" + e.getMessage());
fail(e.getMessage());
} catch (Exception e) {
Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception Msg==" + e.getMessage());
fail(e.getMessage());
}
}
/**
* Create multiple outbound group sessions and check that session Keys are different.
* This test validates random series are provide enough random values.
*/
@Test
public void test17MultipleOutboundSession() {
OlmOutboundGroupSession outboundGroupSession1;
OlmOutboundGroupSession outboundGroupSession2;
OlmOutboundGroupSession outboundGroupSession3;
OlmOutboundGroupSession outboundGroupSession4;
OlmOutboundGroupSession outboundGroupSession5;
OlmOutboundGroupSession outboundGroupSession6;
OlmOutboundGroupSession outboundGroupSession7;
OlmOutboundGroupSession outboundGroupSession8;
try {
outboundGroupSession1 = new OlmOutboundGroupSession();
outboundGroupSession2 = new OlmOutboundGroupSession();
outboundGroupSession3 = new OlmOutboundGroupSession();
outboundGroupSession4 = new OlmOutboundGroupSession();
outboundGroupSession5 = new OlmOutboundGroupSession();
outboundGroupSession6 = new OlmOutboundGroupSession();
outboundGroupSession7 = new OlmOutboundGroupSession();
outboundGroupSession8 = new OlmOutboundGroupSession();
// get the session key from the outbound group sessions
String sessionKey1 = outboundGroupSession1.sessionKey();
String sessionKey2 = outboundGroupSession2.sessionKey();
assertNotEquals(sessionKey1, sessionKey2);
String sessionKey3 = outboundGroupSession3.sessionKey();
assertNotEquals(sessionKey2, sessionKey3);
String sessionKey4 = outboundGroupSession4.sessionKey();
assertNotEquals(sessionKey3, sessionKey4);
String sessionKey5 = outboundGroupSession5.sessionKey();
assertNotEquals(sessionKey4, sessionKey5);
String sessionKey6 = outboundGroupSession6.sessionKey();
assertNotEquals(sessionKey5, sessionKey6);
String sessionKey7 = outboundGroupSession7.sessionKey();
assertNotEquals(sessionKey6, sessionKey7);
String sessionKey8 = outboundGroupSession8.sessionKey();
assertNotEquals(sessionKey7, sessionKey8);
// get the session IDs from the outbound group sessions
String sessionId1 = outboundGroupSession1.sessionIdentifier();
String sessionId2 = outboundGroupSession2.sessionIdentifier();
assertNotEquals(sessionId1, sessionId2);
String sessionId3 = outboundGroupSession3.sessionKey();
assertNotEquals(sessionId2, sessionId3);
String sessionId4 = outboundGroupSession4.sessionKey();
assertNotEquals(sessionId3, sessionId4);
String sessionId5 = outboundGroupSession5.sessionKey();
assertNotEquals(sessionId4, sessionId5);
String sessionId6 = outboundGroupSession6.sessionKey();
assertNotEquals(sessionId5, sessionId6);
String sessionId7 = outboundGroupSession7.sessionKey();
assertNotEquals(sessionId6, sessionId7);
String sessionId8 = outboundGroupSession8.sessionKey();
assertNotEquals(sessionId7, sessionId8);
outboundGroupSession1.releaseSession();
outboundGroupSession2.releaseSession();
outboundGroupSession3.releaseSession();
outboundGroupSession4.releaseSession();
outboundGroupSession5.releaseSession();
outboundGroupSession6.releaseSession();
outboundGroupSession7.releaseSession();
outboundGroupSession8.releaseSession();
assertTrue(outboundGroupSession1.isReleased());
assertTrue(outboundGroupSession2.isReleased());
assertTrue(outboundGroupSession3.isReleased());
assertTrue(outboundGroupSession4.isReleased());
assertTrue(outboundGroupSession5.isReleased());
assertTrue(outboundGroupSession6.isReleased());
assertTrue(outboundGroupSession7.isReleased());
assertTrue(outboundGroupSession8.isReleased());
} catch (OlmException e) {
fail("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode());
}
}
/**
* Specific test for the following run time error:
* "JNI DETECTED ERROR IN APPLICATION: input is not valid Modified UTF-8: illegal start byte 0xf0 in call to NewStringUTF".<br>
* When the msg to decrypt contain emojis, depending on the android platform, the NewStringUTF() behaves differently and
* can even crash.
* This issue is described in details here: https://github.com/eclipsesource/J2V8/issues/142
*/
@Test
public void test18TestBadCharacterCrashInDecrypt() {
OlmInboundGroupSession bobInboundGroupSession=null;
// values taken from a "real life" crash case
String sessionKeyRef = "AgAAAAycZE6AekIctJWYxd2AWLOY15YmxZODm/WkgbpWkyycp6ytSp/R+wo84jRrzBNWmv6ySLTZ9R0EDOk9VI2eZyQ6Efdwyo1mAvrWvTkZl9yALPdkOIVHywyG65f1SNiLrnsln3hgsT1vUrISGyKtsljoUgQpr3JDPEhD0ilAi63QBjhnGCW252b+7nF+43rb6O6lwm93LaVwe2341Gdp6EkhTUvetALezEqDOtKN00wVqAbq0RQAnUJIowxHbMswg+FyoR1K1oCjnVEoF23O9xlAn5g1XtuBZP3moJlR2lwsBA";
String msgToDecryptWithEmoji = "AwgNEpABpjs+tYF+0y8bWtzAgYAC3N55p5cPJEEiGPU1kxIHSY7f2aG5Fj4wmcsXUkhDv0UePj922kgf+Q4dFsPHKq2aVA93n8DJAQ/FRfcM98B9E6sKCZ/PsCF78uBvF12Aaq9D3pUHBopdd7llUfVq29d5y6ZwX5VDoqV2utsATkKjXYV9CbfZuvvBMQ30ZLjEtyUUBJDY9K4FxEFcULytA/IkVnATTG9ERuLF/yB6ukSFR+iUWRYAmtuOuU0k9BvaqezbGqNoK5Grlkes+dYX6/0yUObumcw9/iAI";
// bob creates INBOUND GROUP SESSION
try {
bobInboundGroupSession = new OlmInboundGroupSession(sessionKeyRef);
} catch (OlmException e) {
fail("Exception in test18TestBadCharacterCrashInDecrypt, Exception code=" + e.getExceptionCode());
}
OlmInboundGroupSession.DecryptMessageResult result = null;
try {
result = bobInboundGroupSession.decryptMessage(msgToDecryptWithEmoji);
} catch (Exception e) {
fail("Exception in test18TestBadCharacterCrashInDecrypt, Exception code=" + e.getMessage());
}
assertNotNull(result.mDecryptedMessage);
assertEquals(13, result.mIndex);
}
/**
* Specific test to check an error message is returned by decryptMessage() API.<br>
* A corrupted encrypted message is passed, and a INVALID_BASE64 is
* espexted.
**/
@Test
public void test19TestErrorMessageReturnedInDecrypt() {
OlmInboundGroupSession bobInboundGroupSession=null;
final String EXPECTED_ERROR_MESSAGE= "INVALID_BASE64";
String sessionKeyRef = "AgAAAAycZE6AekIctJWYxd2AWLOY15YmxZODm/WkgbpWkyycp6ytSp/R+wo84jRrzBNWmv6ySLTZ9R0EDOk9VI2eZyQ6Efdwyo1mAvrWvTkZl9yALPdkOIVHywyG65f1SNiLrnsln3hgsT1vUrISGyKtsljoUgQpr3JDPEhD0ilAi63QBjhnGCW252b+7nF+43rb6O6lwm93LaVwe2341Gdp6EkhTUvetALezEqDOtKN00wVqAbq0RQAnUJIowxHbMswg+FyoR1K1oCjnVEoF23O9xlAn5g1XtuBZP3moJlR2lwsBA";
String corruptedEncryptedMsg = "AwgANYTHINGf87ge45ge7gr*/rg5ganything4gr41rrgr4re55tanythingmcsXUkhDv0UePj922kgf+";
// valid INBOUND GROUP SESSION
try {
bobInboundGroupSession = new OlmInboundGroupSession(sessionKeyRef);
} catch (OlmException e) {
fail("Exception in test19TestErrorMessageReturnedInDecrypt, Exception code=" + e.getExceptionCode());
}
String exceptionMessage = null;
try {
bobInboundGroupSession.decryptMessage(corruptedEncryptedMsg);
} catch (OlmException e) {
exceptionMessage = e.getMessage();
}
assertEquals(EXPECTED_ERROR_MESSAGE, exceptionMessage);
}
/**
* Test the import/export functions.<br>
**/
@Test
public void test20TestInboundGroupSessionImportExport() {
String sessionKey = "AgAAAAAwMTIzNDU2Nzg5QUJERUYwMTIzNDU2Nzg5QUJDREVGMDEyMzQ1Njc4OUFCREVGM" +
"DEyMzQ1Njc4OUFCQ0RFRjAxMjM0NTY3ODlBQkRFRjAxMjM0NTY3ODlBQkNERUYwMTIzND" +
"U2Nzg5QUJERUYwMTIzNDU2Nzg5QUJDREVGMDEyMw0bdg1BDq4Px/slBow06q8n/B9WBfw" +
"WYyNOB8DlUmXGGwrFmaSb9bR/eY8xgERrxmP07hFmD9uqA2p8PMHdnV5ysmgufE6oLZ5+" +
"8/mWQOW3VVTnDIlnwd8oHUYRuk8TCQ";
String message = "AwgAEhAcbh6UpbByoyZxufQ+h2B+8XHMjhR69G8F4+qjMaFlnIXusJZX3r8LnRORG9T3D" +
"XFdbVuvIWrLyRfm4i8QRbe8VPwGRFG57B1CtmxanuP8bHtnnYqlwPsD";
OlmInboundGroupSession inboundGroupSession = null;
try {
inboundGroupSession = new OlmInboundGroupSession(sessionKey);
} catch (Exception e) {
fail("OlmInboundGroupSession failed " + e.getMessage());
}
boolean isVerified = false;
try {
isVerified = inboundGroupSession.isVerified();
} catch (Exception e) {
fail("isVerified failed " + e.getMessage());
}
assertTrue(isVerified);
OlmInboundGroupSession.DecryptMessageResult result = null;
try {
result = inboundGroupSession.decryptMessage(message);
} catch (Exception e) {
fail("decryptMessage failed " + e.getMessage());
}
assertTrue(TextUtils.equals(result.mDecryptedMessage, "Message"));
assertEquals(0, result.mIndex);
String export = null;
try {
export = inboundGroupSession.export(0);
} catch (Exception e) {
fail("export failed " + e.getMessage());
}
assertFalse(TextUtils.isEmpty(export));
long index = -1;
try {
index = inboundGroupSession.getFirstKnownIndex();
} catch (Exception e) {
fail("getFirstKnownIndex failed " + e.getMessage());
}
assertTrue(index >=0);
inboundGroupSession.releaseSession();
inboundGroupSession = null;
OlmInboundGroupSession inboundGroupSession2 = null;
try {
inboundGroupSession2 = inboundGroupSession.importSession(export);
} catch (Exception e) {
fail("OlmInboundGroupSession failed " + e.getMessage());
}
try {
isVerified = inboundGroupSession2.isVerified();
} catch (Exception e) {
fail("isVerified failed " + e.getMessage());
}
assertFalse(isVerified);
result = null;
try {
result = inboundGroupSession2.decryptMessage(message);
} catch (Exception e) {
fail("decryptMessage failed " + e.getMessage());
}
assertTrue(TextUtils.equals(result.mDecryptedMessage, "Message"));
assertEquals(0, result.mIndex);
try {
isVerified = inboundGroupSession2.isVerified();
} catch (Exception e) {
fail("isVerified failed " + e.getMessage());
}
assertTrue(isVerified);
inboundGroupSession2.releaseSession();
}
}

View File

@ -0,0 +1,202 @@
/*
* Copyright 2018,2019 New Vector 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.
*/
package org.matrix.olm;
import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import java.util.Arrays;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@RunWith(AndroidJUnit4.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class OlmPkTest {
private static final String LOG_TAG = "OlmPkEncryptionTest";
private static OlmPkEncryption mOlmPkEncryption;
private static OlmPkDecryption mOlmPkDecryption;
private static OlmPkSigning mOlmPkSigning;
@Test
public void test01EncryptAndDecrypt() {
try {
mOlmPkEncryption = new OlmPkEncryption();
} catch (OlmException e) {
e.printStackTrace();
fail("OlmPkEncryption failed " + e.getMessage());
}
try {
mOlmPkDecryption = new OlmPkDecryption();
} catch (OlmException e) {
e.printStackTrace();
fail("OlmPkEncryption failed " + e.getMessage());
}
assertNotNull(mOlmPkEncryption);
assertNotNull(mOlmPkDecryption);
String key = null;
try {
key = mOlmPkDecryption.generateKey();
} catch (OlmException e) {
fail("Exception in generateKey, Exception code=" + e.getExceptionCode());
}
Log.d(LOG_TAG, "Ephemeral Key: " + key);
try {
mOlmPkEncryption.setRecipientKey(key);
} catch (OlmException e) {
fail("Exception in setRecipientKey, Exception code=" + e.getExceptionCode());
}
String clearMessage = "Public key test";
OlmPkMessage message = null;
try {
message = mOlmPkEncryption.encrypt(clearMessage);
} catch (OlmException e) {
fail("Exception in encrypt, Exception code=" + e.getExceptionCode());
}
Log.d(LOG_TAG, "message: " + message.mCipherText + " " + message.mMac + " " + message.mEphemeralKey);
String decryptedMessage = null;
try {
decryptedMessage = mOlmPkDecryption.decrypt(message);
} catch (OlmException e) {
fail("Exception in decrypt, Exception code=" + e.getExceptionCode());
}
assertEquals(clearMessage, decryptedMessage);
mOlmPkEncryption.releaseEncryption();
mOlmPkDecryption.releaseDecryption();
assertTrue(mOlmPkEncryption.isReleased());
assertTrue(mOlmPkDecryption.isReleased());
}
@Test
public void test02PrivateKey() {
try {
mOlmPkDecryption = new OlmPkDecryption();
} catch (OlmException e) {
e.printStackTrace();
fail("OlmPkEncryption failed " + e.getMessage());
}
assertNotNull(mOlmPkDecryption);
byte[] privateKey = {
(byte) 0x77, (byte) 0x07, (byte) 0x6D, (byte) 0x0A,
(byte) 0x73, (byte) 0x18, (byte) 0xA5, (byte) 0x7D,
(byte) 0x3C, (byte) 0x16, (byte) 0xC1, (byte) 0x72,
(byte) 0x51, (byte) 0xB2, (byte) 0x66, (byte) 0x45,
(byte) 0xDF, (byte) 0x4C, (byte) 0x2F, (byte) 0x87,
(byte) 0xEB, (byte) 0xC0, (byte) 0x99, (byte) 0x2A,
(byte) 0xB1, (byte) 0x77, (byte) 0xFB, (byte) 0xA5,
(byte) 0x1D, (byte) 0xB9, (byte) 0x2C, (byte) 0x2A
};
assertEquals(privateKey.length, OlmPkDecryption.privateKeyLength());
try {
mOlmPkDecryption.setPrivateKey(privateKey);
} catch (OlmException e) {
fail("Exception in setPrivateKey, Exception code=" + e.getExceptionCode());
}
byte[] privateKeyCopy = null;
try {
privateKeyCopy = mOlmPkDecryption.privateKey();
} catch (OlmException e) {
fail("Exception in privateKey, Exception code=" + e.getExceptionCode());
}
assertArrayEquals(privateKey, privateKeyCopy);
mOlmPkDecryption.releaseDecryption();
assertTrue(mOlmPkDecryption.isReleased());
}
@Test
public void test03Signing() {
try {
mOlmPkSigning = new OlmPkSigning();
} catch (OlmException e) {
e.printStackTrace();
fail("OlmPkSigning failed " + e.getMessage());
}
assertNotNull(mOlmPkSigning);
byte[] seed = null;
try {
seed = OlmPkSigning.generateSeed();
} catch (OlmException e) {
e.printStackTrace();
fail("generateSeed failed " + e.getMessage());
}
assertEquals(seed.length, OlmPkSigning.seedLength());
String pubkey = null;
try {
pubkey = mOlmPkSigning.initWithSeed(seed);
} catch (OlmException e) {
e.printStackTrace();
fail("initWithSeed failed " + e.getMessage());
}
String message = "We hold these truths to be self-evident, that all men are created equal, that they are endowed by their Creator with certain unalienable Rights, that among these are Life, Liberty and the pursuit of Happiness.";
String signature = null;
try {
signature = mOlmPkSigning.sign(message);
} catch (OlmException e) {
e.printStackTrace();
fail("sign failed " + e.getMessage());
}
OlmUtility olmUtility = null;
try {
olmUtility = new OlmUtility();
} catch (OlmException e) {
e.printStackTrace();
fail("olmUtility failed " + e.getMessage());
}
try {
olmUtility.verifyEd25519Signature(signature, pubkey, message);
} catch (OlmException e) {
e.printStackTrace();
fail("Signature verification failed " + e.getMessage());
}
mOlmPkSigning.releaseSigning();
assertTrue(mOlmPkSigning.isReleased());
olmUtility.releaseUtility();
}
}

View File

@ -0,0 +1,107 @@
/*
* Copyright 2019 New Vector 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.
*/
package org.matrix.olm;
import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@RunWith(AndroidJUnit4.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class OlmSasTest {
private static OlmManager mOlmManager;
//Enable the native lib
@BeforeClass
public static void setUpClass() {
// load native librandomBytesOfLength
mOlmManager = new OlmManager();
}
@Test
public void testSASCode() {
OlmSAS aliceSas = null;
OlmSAS bobSas = null;
try {
aliceSas = new OlmSAS();
bobSas = new OlmSAS();
String alicePKey = aliceSas.getPublicKey();
String bobPKey = bobSas.getPublicKey();
Log.e(OlmSasTest.class.getSimpleName(), "#### Alice pub Key is " + alicePKey);
Log.e(OlmSasTest.class.getSimpleName(), "#### Bob pub Key is " + bobPKey);
aliceSas.setTheirPublicKey(bobPKey);
bobSas.setTheirPublicKey(alicePKey);
int codeLength = 6;
byte[] alice_sas = aliceSas.generateShortCode("SAS", codeLength);
byte[] bob_sas = bobSas.generateShortCode("SAS", codeLength);
Log.e(OlmSasTest.class.getSimpleName(), "#### Alice SAS is " + new String(alice_sas, "UTF-8"));
Log.e(OlmSasTest.class.getSimpleName(), "#### Bob SAS is " + new String(bob_sas, "UTF-8"));
assertEquals(codeLength, alice_sas.length);
assertEquals(codeLength, bob_sas.length);
assertArrayEquals(alice_sas, bob_sas);
String aliceMac = aliceSas.calculateMac("Hello world!", "SAS");
String bobMac = bobSas.calculateMac("Hello world!", "SAS");
assertEquals(aliceMac, bobMac);
Log.e(OlmSasTest.class.getSimpleName(), "#### Alice Mac is " + aliceMac);
Log.e(OlmSasTest.class.getSimpleName(), "#### Bob Mac is " + bobMac);
String aliceLongKdfMac = aliceSas.calculateMacLongKdf("Hello world!", "SAS");
String bobLongKdfMac = bobSas.calculateMacLongKdf("Hello world!", "SAS");
assertEquals("Mac should be the same", aliceLongKdfMac, bobLongKdfMac);
Log.e(OlmSasTest.class.getSimpleName(), "#### Alice lkdf Mac is " + aliceLongKdfMac);
Log.e(OlmSasTest.class.getSimpleName(), "#### Bob lkdf Mac is " + bobLongKdfMac);
} catch (Exception e) {
fail("OlmSas init failed " + e.getMessage());
e.printStackTrace();
} finally {
if (aliceSas != null) {
aliceSas.releaseSas();
}
if (bobSas != null) {
bobSas.releaseSas();
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,162 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016 Vector Creations 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.
*/
package org.matrix.olm;
import android.text.TextUtils;
import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import java.util.Map;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@RunWith(AndroidJUnit4.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class OlmUtilityTest {
private static final String LOG_TAG = "OlmAccountTest";
private static final int GENERATION_ONE_TIME_KEYS_NUMBER = 50;
private static OlmManager mOlmManager;
@BeforeClass
public static void setUpClass() {
// load native lib
mOlmManager = new OlmManager();
String version = mOlmManager.getOlmLibVersion();
assertNotNull(version);
Log.d(LOG_TAG, "## setUpClass(): lib version=" + version);
}
/**
* Test the signing API
*/
@Test
public void test01VerifyEd25519Signing() {
String fingerPrintKey = null;
String errorMsg = null;
String message = "{\"algorithms\":[\"m.megolm.v1.aes-sha2\",\"m.olm.v1.curve25519-aes-sha2\"],\"device_id\":\"YMBYCWTWCG\",\"keys\":{\"curve25519:YMBYCWTWCG\":\"KZFa5YUXV2EOdhK8dcGMMHWB67stdgAP4+xwiS69mCU\",\"ed25519:YMBYCWTWCG\":\"0cEgQJJqjgtXUGp4ZXQQmh36RAxwxr8HJw2E9v1gvA0\"},\"user_id\":\"@mxBob14774891254276b253f42-f267-43ec-bad9-767142bfea30:localhost:8480\"}";
OlmAccount account = null;
// create account
try {
account = new OlmAccount();
} catch (OlmException e) {
fail(e.getMessage());
}
assertNotNull(account);
// sign message
String messageSignature = null;
try {
messageSignature = account.signMessage(message);
} catch (Exception e) {
fail(e.getMessage());
}
assertNotNull(messageSignature);
// get identities key (finger print key)
Map<String, String> identityKeys = null;
try {
identityKeys = account.identityKeys();
} catch (Exception e) {
fail("identityKeys failed " + e.getMessage());
}
assertNotNull(identityKeys);
fingerPrintKey = TestHelper.getFingerprintKey(identityKeys);
assertFalse("fingerprint key missing", TextUtils.isEmpty(fingerPrintKey));
// instantiate utility object
OlmUtility utility = null;
try {
utility = new OlmUtility();
} catch (Exception e) {
fail("failed to create OlmUtility");
}
// verify signature
errorMsg = null;
try {
utility.verifyEd25519Signature(messageSignature, fingerPrintKey, message);
} catch (Exception e) {
errorMsg = e.getMessage();
}
assertTrue(TextUtils.isEmpty(errorMsg));
// check a bad signature is detected => errorMsg = BAD_MESSAGE_MAC
String badSignature = "Bad signature Bad signature Bad signature..";
errorMsg = null;
try {
utility.verifyEd25519Signature(badSignature, fingerPrintKey, message);
} catch (Exception e) {
errorMsg = e.getMessage();
}
assertFalse(TextUtils.isEmpty(errorMsg));
// check bad fingerprint size => errorMsg = INVALID_BASE64
String badSizeFingerPrintKey = fingerPrintKey.substring(fingerPrintKey.length() / 2);
errorMsg = null;
try {
utility.verifyEd25519Signature(messageSignature, badSizeFingerPrintKey, message);
} catch (Exception e) {
errorMsg = e.getMessage();
}
assertFalse(TextUtils.isEmpty(errorMsg));
utility.releaseUtility();
assertTrue(utility.isReleased());
account.releaseAccount();
assertTrue(account.isReleased());
}
@Test
public void test02sha256() {
OlmUtility utility = null;
try {
utility = new OlmUtility();
} catch (Exception e) {
fail("OlmUtility creation failed");
}
String msgToHash = "The quick brown fox jumps over the lazy dog";
String hashResult = utility.sha256(msgToHash);
assertFalse(TextUtils.isEmpty(hashResult));
utility.releaseUtility();
assertTrue(utility.isReleased());
}
}

View File

@ -0,0 +1,83 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016 Vector Creations 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.
*/
package org.matrix.olm;
import java.util.ArrayList;
import java.util.Map;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* Helper class providing helper methods used in the Olm Android SDK unit tests.
*/
public class TestHelper {
/**
* Return the identity key {@link OlmAccount#JSON_KEY_IDENTITY_KEY} from the JSON object.
* @param aIdentityKeysMap result of {@link OlmAccount#identityKeys()}
* @return identity key string if operation succeed, null otherwise
*/
static public String getIdentityKey(Map<String, String> aIdentityKeysMap){
String idKey = null;
try {
idKey = aIdentityKeysMap.get(OlmAccount.JSON_KEY_IDENTITY_KEY);
} catch (Exception e) {
fail("Exception MSg=" + e.getMessage());
}
return idKey;
}
/**
* Return the fingerprint key {@link OlmAccount#JSON_KEY_FINGER_PRINT_KEY} from the JSON object.
* @param aIdentityKeysMap result of {@link OlmAccount#identityKeys()}
* @return fingerprint key string if operation succeed, null otherwise
*/
static public String getFingerprintKey(Map<String, String> aIdentityKeysMap) {
String fingerprintKey = null;
try {
fingerprintKey = aIdentityKeysMap.get(OlmAccount.JSON_KEY_FINGER_PRINT_KEY);
} catch (Exception e) {
fail("Exception MSg=" + e.getMessage());
}
return fingerprintKey;
}
/**
* Return the first one time key from the JSON object.
* @param aIdentityKeysMap result of {@link OlmAccount#oneTimeKeys()}
* @param aKeyPosition the position of the key to be retrieved
* @return one time key string if operation succeed, null otherwise
*/
static public String getOneTimeKey(Map<String, Map<String, String>> aIdentityKeysMap, int aKeyPosition) {
String firstOneTimeKey = null;
try {
Map<String, String> generatedKeys = aIdentityKeysMap.get(OlmAccount.JSON_KEY_ONE_TIME_KEY);
assertNotNull(OlmAccount.JSON_KEY_ONE_TIME_KEY + " object is missing", generatedKeys);
firstOneTimeKey = (new ArrayList<>(generatedKeys.values())).get(aKeyPosition - 1);
} catch (Exception e) {
fail("Exception Msg=" + e.getMessage());
}
return firstOneTimeKey;
}
}

View File

@ -0,0 +1 @@
<manifest package="org.matrix.olm" />

View File

@ -0,0 +1,83 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016 Vector Creations 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.
*/
package org.matrix.olm;
import android.util.Log;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* Helper class dedicated to serialization mechanism (template method pattern).
*/
abstract class CommonSerializeUtils {
private static final String LOG_TAG = "CommonSerializeUtils";
/**
* Kick off the serialization mechanism.
* @param aOutStream output stream for serializing
* @throws IOException exception
*/
protected void serialize(ObjectOutputStream aOutStream) throws IOException {
aOutStream.defaultWriteObject();
// generate serialization key
byte[] key = OlmUtility.getRandomKey();
// compute pickle string
StringBuffer errorMsg = new StringBuffer();
byte[] pickledData = serialize(key, errorMsg);
if(null == pickledData) {
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_SERIALIZATION, String.valueOf(errorMsg));
} else {
aOutStream.writeObject(new String(key, "UTF-8"));
aOutStream.writeObject(new String(pickledData, "UTF-8"));
}
}
/**
* Kick off the deserialization mechanism.
* @param aInStream input stream
* @throws Exception the exception
*/
protected void deserialize(ObjectInputStream aInStream) throws Exception {
aInStream.defaultReadObject();
String keyAsString = (String)aInStream.readObject();
String pickledDataAsString = (String)aInStream.readObject();
byte[] key;
byte[] pickledData;
try {
key = keyAsString.getBytes("UTF-8");
pickledData = pickledDataAsString.getBytes("UTF-8");
deserialize(pickledData, key);
} catch (Exception e) {
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_DESERIALIZATION, e.getMessage());
}
Log.d(LOG_TAG,"## deserializeObject(): success");
}
protected abstract byte[] serialize(byte[] aKey, StringBuffer aErrorMsg);
protected abstract void deserialize(byte[] aSerializedData, byte[] aKey) throws Exception;
}

View File

@ -0,0 +1,515 @@
/*
* Copyright 2017 OpenMarket Ltd
* Copyright 2017 Vector Creations 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.
*/
package org.matrix.olm;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONObject;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Map;
/**
* Account class used to create Olm sessions in conjunction with {@link OlmSession} class.<br>
* OlmAccount provides APIs to retrieve the Olm keys.
*<br><br>Detailed implementation guide is available at <a href="http://matrix.org/docs/guides/e2e_implementation.html">Implementing End-to-End Encryption in Matrix clients</a>.
*/
public class OlmAccount extends CommonSerializeUtils implements Serializable {
private static final long serialVersionUID = 3497486121598434824L;
private static final String LOG_TAG = "OlmAccount";
// JSON keys used in the JSON objects returned by JNI
/** As well as the identity key, each device creates a number of Curve25519 key pairs which are
also used to establish Olm sessions, but can only be used once. Once again, the private part
remains on the device. but the public part is published to the Matrix network **/
public static final String JSON_KEY_ONE_TIME_KEY = "curve25519";
/** Curve25519 identity key is a public-key cryptographic system which can be used to establish a shared
secret.<br>In Matrix, each device has a long-lived Curve25519 identity key which is used to establish
Olm sessions with that device. The private key should never leave the device, but the
public part is signed with the Ed25519 fingerprint key ({@link #JSON_KEY_FINGER_PRINT_KEY}) and published to the network. **/
public static final String JSON_KEY_IDENTITY_KEY = "curve25519";
/** Ed25519 finger print is a public-key cryptographic system for signing messages.<br>In Matrix, each device has
an Ed25519 key pair which serves to identify that device. The private the key should
never leave the device, but the public part is published to the Matrix network. **/
public static final String JSON_KEY_FINGER_PRINT_KEY = "ed25519";
/** Account Id returned by JNI.
* This value identifies uniquely the native account instance.
*/
private transient long mNativeId;
public OlmAccount() throws OlmException {
try {
mNativeId = createNewAccountJni();
} catch (Exception e) {
throw new OlmException(OlmException.EXCEPTION_CODE_INIT_ACCOUNT_CREATION, e.getMessage());
}
}
/**
* Create a new account and return it to JAVA side.<br>
* Since a C prt is returned as a jlong, special care will be taken
* to make the cast (OlmAccount* to jlong) platform independent.
* @return the initialized OlmAccount* instance or throw an exception if fails
**/
private native long createNewAccountJni();
/**
* Getter on the account ID.
* @return native account ID
*/
long getOlmAccountId(){
return mNativeId;
}
/**
* Release native account and invalid its JAVA reference counter part.<br>
* Public API for {@link #releaseAccountJni()}.
*/
public void releaseAccount() {
if (0 != mNativeId) {
releaseAccountJni();
}
mNativeId = 0;
}
/**
* Destroy the corresponding OLM account native object.<br>
* This method must ALWAYS be called when this JAVA instance
* is destroyed (ie. garbage collected) to prevent memory leak in native side.
* See {@link #createNewAccountJni()}.
*/
private native void releaseAccountJni();
/**
* Return true the object resources have been released.<br>
* @return true the object resources have been released
*/
public boolean isReleased() {
return (0 == mNativeId);
}
/**
* Return the identity keys (identity and fingerprint keys) in a dictionary.<br>
* Public API for {@link #identityKeysJni()}.<br>
* Ex:<code>
* {
* "curve25519":"Vam++zZPMqDQM6ANKpO/uAl5ViJSHxV9hd+b0/fwRAg",
* "ed25519":"+v8SOlOASFTMrX3MCKBM4iVnYoZ+JIjpNt1fi8Z9O2I"
* }</code>
* @return identity keys dictionary if operation succeeds, null otherwise
* @exception OlmException the failure reason
*/
public Map<String, String> identityKeys() throws OlmException {
JSONObject identityKeysJsonObj = null;
byte[] identityKeysBuffer;
try {
identityKeysBuffer = identityKeysJni();
} catch (Exception e) {
Log.e(LOG_TAG, "## identityKeys(): Failure - " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_IDENTITY_KEYS, e.getMessage());
}
if (null != identityKeysBuffer) {
try {
identityKeysJsonObj = new JSONObject(new String(identityKeysBuffer, "UTF-8"));
} catch (Exception e) {
Log.e(LOG_TAG, "## identityKeys(): Exception - Msg=" + e.getMessage());
}
} else {
Log.e(LOG_TAG, "## identityKeys(): Failure - identityKeysJni()=null");
}
return OlmUtility.toStringMap(identityKeysJsonObj);
}
/**
* Get the public identity keys (Ed25519 fingerprint key and Curve25519 identity key).<br>
* Keys are Base64 encoded.
* These keys must be published on the server.
* @return the identity keys or throw an exception if it fails
*/
private native byte[] identityKeysJni();
/**
* Return the largest number of "one time keys" this account can store.
* @return the max number of "one time keys", -1 otherwise
*/
public long maxOneTimeKeys() {
return maxOneTimeKeysJni();
}
/**
* Return the largest number of "one time keys" this account can store.
* @return the max number of "one time keys", -1 otherwise
*/
private native long maxOneTimeKeysJni();
/**
* Generate a number of new one time keys.<br> If total number of keys stored
* by this account exceeds {@link #maxOneTimeKeys()}, the old keys are discarded.<br>
* The corresponding keys are retrieved by {@link #oneTimeKeys()}.
* @param aNumberOfKeys number of keys to generate
* @exception OlmException the failure reason
*/
public void generateOneTimeKeys(int aNumberOfKeys) throws OlmException {
try {
generateOneTimeKeysJni(aNumberOfKeys);
} catch (Exception e) {
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_GENERATE_ONE_TIME_KEYS, e.getMessage());
}
}
/**
* Generate a number of new one time keys.<br> If total number of keys stored
* by this account exceeds {@link #maxOneTimeKeys()}, the old keys are discarded.
* An exception is thrown if the operation fails.<br>
* @param aNumberOfKeys number of keys to generate
*/
private native void generateOneTimeKeysJni(int aNumberOfKeys);
/**
* Return the "one time keys" in a dictionary.<br>
* The number of "one time keys", is specified by {@link #generateOneTimeKeys(int)}<br>
* Ex:<code>
* { "curve25519":
* {
* "AAAABQ":"qefVZd8qvjOpsFzoKSAdfUnJVkIreyxWFlipCHjSQQg",
* "AAAABA":"/X8szMU+p+lsTnr56wKjaLgjTMQQkCk8EIWEAilZtQ8",
* "AAAAAw":"qxNxxFHzevFntaaPdT0fhhO7tc7pco4+xB/5VRG81hA",
* }
* }</code><br>
* Public API for {@link #oneTimeKeysJni()}.<br>
* Note: these keys are to be published on the server.
* @return one time keys in string dictionary.
* @exception OlmException the failure reason
*/
public Map<String, Map<String, String>> oneTimeKeys() throws OlmException {
JSONObject oneTimeKeysJsonObj = null;
byte[] oneTimeKeysBuffer;
try {
oneTimeKeysBuffer = oneTimeKeysJni();
} catch (Exception e) {
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_ONE_TIME_KEYS, e.getMessage());
}
if( null != oneTimeKeysBuffer) {
try {
oneTimeKeysJsonObj = new JSONObject(new String(oneTimeKeysBuffer, "UTF-8"));
} catch (Exception e) {
Log.e(LOG_TAG, "## oneTimeKeys(): Exception - Msg=" + e.getMessage());
}
} else {
Log.e(LOG_TAG, "## oneTimeKeys(): Failure - identityKeysJni()=null");
}
return OlmUtility.toStringMapMap(oneTimeKeysJsonObj);
}
/**
* Get the public parts of the unpublished "one time keys" for the account.<br>
* The returned data is a JSON-formatted object with the single property
* <code>curve25519</code>, which is itself an object mapping key id to
* base64-encoded Curve25519 key.<br>
* @return byte array containing the one time keys or throw an exception if it fails
*/
private native byte[] oneTimeKeysJni();
/**
* Remove the "one time keys" that the session used from the account.
* @param aSession session instance
* @throws OlmException the failure reason
*/
public void removeOneTimeKeys(OlmSession aSession) throws OlmException {
if (null != aSession) {
try {
removeOneTimeKeysJni(aSession.getOlmSessionId());
} catch (Exception e) {
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_REMOVE_ONE_TIME_KEYS, e.getMessage());
}
}
}
/**
* Remove the "one time keys" that the session used from the account.
* An exception is thrown if the operation fails.
* @param aNativeOlmSessionId native session instance identifier
*/
private native void removeOneTimeKeysJni(long aNativeOlmSessionId);
/**
* Marks the current set of "one time keys" as being published.
* @exception OlmException the failure reason
*/
public void markOneTimeKeysAsPublished() throws OlmException {
try {
markOneTimeKeysAsPublishedJni();
} catch (Exception e) {
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_MARK_ONE_KEYS_AS_PUBLISHED, e.getMessage());
}
}
/**
* Marks the current set of "one time keys" as being published.
* An exception is thrown if the operation fails.
*/
private native void markOneTimeKeysAsPublishedJni();
/**
* Sign a message with the ed25519 fingerprint key for this account.<br>
* The signed message is returned by the method.
* @param aMessage message to sign
* @return the signed message
* @exception OlmException the failure reason
*/
public String signMessage(String aMessage) throws OlmException {
String result = null;
if (null != aMessage) {
byte[] utf8String = null;
try {
utf8String = aMessage.getBytes("UTF-8");
if (null != utf8String) {
byte[] signedMessage = signMessageJni(utf8String);
if (null != signedMessage) {
result = new String(signedMessage, "UTF-8");
}
}
} catch (Exception e) {
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_SIGN_MESSAGE, e.getMessage());
} finally {
if (null != utf8String) {
Arrays.fill(utf8String, (byte) 0);
}
}
}
return result;
}
/**
* Sign a message with the ed25519 fingerprint key for this account.<br>
* The signed message is returned by the method.
* @param aMessage message to sign
* @return the signed message
*/
private native byte[] signMessageJni(byte[] aMessage);
//==============================================================================================================
// Serialization management
//==============================================================================================================
/**
* Kick off the serialization mechanism.
* @param aOutStream output stream for serializing
* @throws IOException exception
*/
private void writeObject(ObjectOutputStream aOutStream) throws IOException {
serialize(aOutStream);
}
/**
* Kick off the deserialization mechanism.
* @param aInStream input stream
* @throws Exception exception
*/
private void readObject(ObjectInputStream aInStream) throws Exception {
deserialize(aInStream);
}
/**
* Return an account as a bytes buffer.<br>
* The account is serialized and encrypted with aKey.
* In case of failure, an error human readable
* description is provide in aErrorMsg.
* @param aKey encryption key
* @param aErrorMsg error message description
* @return the account as bytes buffer
*/
@Override
protected byte[] serialize(byte[] aKey, StringBuffer aErrorMsg) {
byte[] pickleRetValue = null;
// sanity check
if(null == aErrorMsg) {
Log.e(LOG_TAG,"## serialize(): invalid parameter - aErrorMsg=null");
} else if (null == aKey) {
aErrorMsg.append("Invalid input parameters in serializeDataWithKey()");
} else {
aErrorMsg.setLength(0);
try {
pickleRetValue = serializeJni(aKey);
} catch (Exception e) {
Log.e(LOG_TAG, "## serialize() failed " + e.getMessage());
aErrorMsg.append(e.getMessage());
}
}
return pickleRetValue;
}
/**
* Serialize and encrypt account instance.<br>
* @param aKeyBuffer key used to encrypt the serialized account data
* @return the serialised account as bytes buffer.
**/
private native byte[] serializeJni(byte[] aKeyBuffer);
/**
* Loads an account from a pickled bytes buffer.<br>
* See {@link #serialize(byte[], StringBuffer)}
* @param aSerializedData bytes buffer
* @param aKey key used to encrypted
* @exception Exception the exception
*/
@Override
protected void deserialize(byte[] aSerializedData, byte[] aKey) throws Exception {
String errorMsg = null;
try {
if ((null == aSerializedData) || (null == aKey)) {
Log.e(LOG_TAG, "## deserialize(): invalid input parameters");
errorMsg = "invalid input parameters";
} else {
mNativeId = deserializeJni(aSerializedData, aKey);
}
} catch (Exception e) {
Log.e(LOG_TAG, "## deserialize() failed " + e.getMessage());
errorMsg = e.getMessage();
}
if (!TextUtils.isEmpty(errorMsg)) {
releaseAccount();
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_DESERIALIZATION, errorMsg);
}
}
/**
* Allocate a new account and initialize it with the serialisation data.<br>
* @param aSerializedDataBuffer the account serialisation buffer
* @param aKeyBuffer the key used to encrypt the serialized account data
* @return the deserialized account
**/
private native long deserializeJni(byte[] aSerializedDataBuffer, byte[] aKeyBuffer);
/**
* Return a pickled account as a bytes buffer.<br>
* The account is serialized and encrypted with aKey.
* In case of failure, an error human readable
* description is provide in aErrorMsg.
* @param aKey encryption key
* @param aErrorMsg error message description
* @return the pickled account as bytes buffer
*/
public byte[] pickle(byte[] aKey, StringBuffer aErrorMsg) {
return serialize(aKey, aErrorMsg);
}
/**
* Loads an account from a pickled bytes buffer.<br>
* See {@link #serialize(byte[], StringBuffer)}
* @param aSerializedData bytes buffer
* @param aKey key used to encrypted
* @exception Exception the exception
*/
public void unpickle(byte[] aSerializedData, byte[] aKey) throws Exception {
deserialize(aSerializedData, aKey);
}
/**
* Generates a new fallback key.
* @throws OlmException exception with a reason.
*/
public void generateFallbackKey() throws OlmException {
try {
generateFallbackKeyJni();
} catch (Exception e) {
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_GENERATE_FALLBACK_KEY, e.getMessage());
}
}
private native void generateFallbackKeyJni();
/**
* Return the "fallback key" in a dictionary.<br>
* Ex:<code>
* { "curve25519":
* {
* "AAAABQ":"qefVZd8qvjOpsFzoKSAdfUnJVkIreyxWFlipCHjSQQg"
* }
* }</code><br>
* Public API for {@link #fallbackKeyJni()}.<br>
* Note: the key is to be published on the server.
* @return fallback key in string dictionary.
* @exception OlmException the failure reason
*/
public Map<String, Map<String, String>> fallbackKey() throws OlmException {
JSONObject fallbackKeyJsonObj = null;
byte[] fallbackKeyBuffer;
try {
fallbackKeyBuffer = fallbackKeyJni();
} catch (Exception e) {
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_FALLBACK_KEY, e.getMessage());
}
if( null != fallbackKeyBuffer) {
try {
fallbackKeyJsonObj = new JSONObject(new String(fallbackKeyBuffer, "UTF-8"));
} catch (Exception e) {
Log.e(LOG_TAG, "## fallbackKey(): Exception - Msg=" + e.getMessage());
}
} else {
Log.e(LOG_TAG, "## fallbackKey(): Failure - identityKeysJni()=null");
}
return OlmUtility.toStringMapMap(fallbackKeyJsonObj);
}
private native byte[] fallbackKeyJni();
/**
* Forget about the old fallback key.
*
* This should be called once you are reasonably certain that you will not
* receive any more messages that use the old fallback key (e.g. 5 minutes
* after the new fallback key has been published).
* @throws OlmException the failure reason
**/
public void forgetFallbackKey() throws OlmException {
try {
forgetFallbackKeyJni();
} catch (Exception e) {
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_FORGET_FALLBACK_KEY, e.getMessage());
}
}
private native void forgetFallbackKeyJni();
}

View File

@ -0,0 +1,112 @@
/*
* Copyright 2017 OpenMarket Ltd
* Copyright 2017-2019 Vector Creations 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.
*/
package org.matrix.olm;
import java.io.IOException;
/**
* Exception class to identify specific Olm SDK exceptions.
*/
public class OlmException extends IOException {
// exception codes
public static final int EXCEPTION_CODE_INIT_ACCOUNT_CREATION = 10;
public static final int EXCEPTION_CODE_ACCOUNT_SERIALIZATION = 100;
public static final int EXCEPTION_CODE_ACCOUNT_DESERIALIZATION = 101;
public static final int EXCEPTION_CODE_ACCOUNT_IDENTITY_KEYS = 102;
public static final int EXCEPTION_CODE_ACCOUNT_GENERATE_ONE_TIME_KEYS = 103;
public static final int EXCEPTION_CODE_ACCOUNT_ONE_TIME_KEYS = 104;
public static final int EXCEPTION_CODE_ACCOUNT_REMOVE_ONE_TIME_KEYS = 105;
public static final int EXCEPTION_CODE_ACCOUNT_MARK_ONE_KEYS_AS_PUBLISHED = 106;
public static final int EXCEPTION_CODE_ACCOUNT_SIGN_MESSAGE = 107;
public static final int EXCEPTION_CODE_ACCOUNT_GENERATE_FALLBACK_KEY = 108;
public static final int EXCEPTION_CODE_ACCOUNT_FALLBACK_KEY = 109;
public static final int EXCEPTION_CODE_ACCOUNT_FORGET_FALLBACK_KEY = 110;
public static final int EXCEPTION_CODE_CREATE_INBOUND_GROUP_SESSION = 200;
public static final int EXCEPTION_CODE_INIT_INBOUND_GROUP_SESSION = 201;
public static final int EXCEPTION_CODE_INBOUND_GROUP_SESSION_IDENTIFIER = 202;
public static final int EXCEPTION_CODE_INBOUND_GROUP_SESSION_DECRYPT_SESSION = 203;
public static final int EXCEPTION_CODE_INBOUND_GROUP_SESSION_FIRST_KNOWN_INDEX = 204;
public static final int EXCEPTION_CODE_INBOUND_GROUP_SESSION_IS_VERIFIED = 205;
public static final int EXCEPTION_CODE_INBOUND_GROUP_SESSION_EXPORT = 206;
public static final int EXCEPTION_CODE_CREATE_OUTBOUND_GROUP_SESSION = 300;
public static final int EXCEPTION_CODE_INIT_OUTBOUND_GROUP_SESSION = 301;
public static final int EXCEPTION_CODE_OUTBOUND_GROUP_SESSION_IDENTIFIER = 302;
public static final int EXCEPTION_CODE_OUTBOUND_GROUP_SESSION_KEY = 303;
public static final int EXCEPTION_CODE_OUTBOUND_GROUP_ENCRYPT_MESSAGE = 304;
public static final int EXCEPTION_CODE_INIT_SESSION_CREATION = 400;
public static final int EXCEPTION_CODE_SESSION_INIT_OUTBOUND_SESSION = 401;
public static final int EXCEPTION_CODE_SESSION_INIT_INBOUND_SESSION = 402;
public static final int EXCEPTION_CODE_SESSION_INIT_INBOUND_SESSION_FROM = 403;
public static final int EXCEPTION_CODE_SESSION_ENCRYPT_MESSAGE = 404;
public static final int EXCEPTION_CODE_SESSION_DECRYPT_MESSAGE = 405;
public static final int EXCEPTION_CODE_SESSION_SESSION_IDENTIFIER = 406;
public static final int EXCEPTION_CODE_SESSION_SESSION_DESCRIBE = 407;
public static final int EXCEPTION_CODE_UTILITY_CREATION = 500;
public static final int EXCEPTION_CODE_UTILITY_VERIFY_SIGNATURE = 501;
public static final int EXCEPTION_CODE_PK_ENCRYPTION_CREATION = 600;
public static final int EXCEPTION_CODE_PK_ENCRYPTION_SET_RECIPIENT_KEY = 601;
public static final int EXCEPTION_CODE_PK_ENCRYPTION_ENCRYPT = 602;
public static final int EXCEPTION_CODE_PK_DECRYPTION_CREATION = 700;
public static final int EXCEPTION_CODE_PK_DECRYPTION_GENERATE_KEY = 701;
public static final int EXCEPTION_CODE_PK_DECRYPTION_DECRYPT = 702;
public static final int EXCEPTION_CODE_PK_DECRYPTION_SET_PRIVATE_KEY = 703;
public static final int EXCEPTION_CODE_PK_DECRYPTION_PRIVATE_KEY = 704;
public static final int EXCEPTION_CODE_PK_SIGNING_CREATION = 800;
public static final int EXCEPTION_CODE_PK_SIGNING_GENERATE_SEED = 801;
public static final int EXCEPTION_CODE_PK_SIGNING_INIT_WITH_SEED = 802;
public static final int EXCEPTION_CODE_PK_SIGNING_SIGN = 803;
public static final int EXCEPTION_CODE_SAS_CREATION = 900;
public static final int EXCEPTION_CODE_SAS_ERROR = 901;
public static final int EXCEPTION_CODE_SAS_MISSING_THEIR_PKEY = 902;
public static final int EXCEPTION_CODE_SAS_GENERATE_SHORT_CODE = 903;
// exception human readable messages
public static final String EXCEPTION_MSG_INVALID_PARAMS_DESERIALIZATION = "invalid de-serialized parameters";
/** exception code to be taken from: {@link #EXCEPTION_CODE_CREATE_OUTBOUND_GROUP_SESSION}, {@link #EXCEPTION_CODE_CREATE_INBOUND_GROUP_SESSION},
* {@link #EXCEPTION_CODE_INIT_OUTBOUND_GROUP_SESSION}, {@link #EXCEPTION_CODE_INIT_INBOUND_GROUP_SESSION}..**/
private final int mCode;
/** Human readable message description **/
private final String mMessage;
public OlmException(int aExceptionCode, String aExceptionMessage) {
super();
mCode = aExceptionCode;
mMessage = aExceptionMessage;
}
public int getExceptionCode() {
return mCode;
}
@Override
public String getMessage() {
return mMessage;
}
}

View File

@ -0,0 +1,397 @@
/*
* Copyright 2017 OpenMarket Ltd
* Copyright 2017 Vector Creations 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.
*/
package org.matrix.olm;
import android.text.TextUtils;
import android.util.Log;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Arrays;
/**
* Class used to create an inbound <a href="http://matrix.org/docs/guides/e2e_implementation.html#handling-an-m-room-key-event">Megolm session</a>.<br>
* Counter part of the outbound group session {@link OlmOutboundGroupSession}, this class decrypts the messages sent by the outbound side.
*
* <br><br>Detailed implementation guide is available at <a href="http://matrix.org/docs/guides/e2e_implementation.html">Implementing End-to-End Encryption in Matrix clients</a>.
*/
public class OlmInboundGroupSession extends CommonSerializeUtils implements Serializable {
private static final long serialVersionUID = -772028491251653253L;
private static final String LOG_TAG = "OlmInboundGroupSession";
/** Session Id returned by JNI.<br>
* This value uniquely identifies the native inbound group session instance.
*/
private transient long mNativeId;
/**
* Result in {@link #decryptMessage(String)}
*/
public static class DecryptMessageResult {
/** decrypt message **/
public String mDecryptedMessage;
/** decrypt index **/
public long mIndex;
}
/**
* Constructor.<br>
* Create and save a new native session instance ID and start a new inbound group session.
* The session key parameter is retrieved from an outbound group session.
* @param aSessionKey session key
* @throws OlmException constructor failure
*/
public OlmInboundGroupSession(String aSessionKey) throws OlmException {
this(aSessionKey, false);
}
/**
* Constructor.<br>
* Create and save a new native session instance ID and start a new inbound group session.
* The session key parameter is retrieved from an outbound group session.
* @param aSessionKey session key
* @param isImported true when the session key has been retrieved from a backup
* @throws OlmException constructor failure
*/
private OlmInboundGroupSession(String aSessionKey, boolean isImported) throws OlmException {
if (TextUtils.isEmpty(aSessionKey)) {
Log.e(LOG_TAG, "## initInboundGroupSession(): invalid session key");
throw new OlmException(OlmException.EXCEPTION_CODE_INIT_INBOUND_GROUP_SESSION, "invalid session key");
} else {
byte[] sessionBuffer = null;
try {
sessionBuffer = aSessionKey.getBytes("UTF-8");
mNativeId = createNewSessionJni(aSessionKey.getBytes("UTF-8"), isImported);
} catch (Exception e) {
throw new OlmException(OlmException.EXCEPTION_CODE_INIT_INBOUND_GROUP_SESSION, e.getMessage());
} finally {
if (null != sessionBuffer) {
Arrays.fill(sessionBuffer, (byte) 0);
}
}
}
}
/**
* Initialize a new inbound group session and return it to JAVA side.<br>
* Since a C prt is returned as a jlong, special care will be taken
* to make the cast (OlmInboundGroupSession* to jlong) platform independent.
* @param aSessionKeyBuffer session key from an outbound session
* @param isImported true when the session key has been retrieved from a backup
* @return the initialized OlmInboundGroupSession* instance or throw an exception it fails.
**/
private native long createNewSessionJni(byte[] aSessionKeyBuffer, boolean isImported);
/**
* Create an OlmInboundGroupSession from its exported session data.
* @param exported the exported data
* @return the created OlmException
* @throws OlmException the failure reason
*/
public static OlmInboundGroupSession importSession(String exported) throws OlmException {
return new OlmInboundGroupSession(exported, true);
}
/**
* Release native session and invalid its JAVA reference counter part.<br>
* Public API for {@link #releaseSessionJni()}.
*/
public void releaseSession(){
if (0 != mNativeId) {
releaseSessionJni();
}
mNativeId = 0;
}
/**
* Destroy the corresponding OLM inbound group session native object.<br>
* This method must ALWAYS be called when this JAVA instance
* is destroyed (ie. garbage collected) to prevent memory leak in native side.
* See {@link #createNewSessionJni(byte[], boolean)}.
*/
private native void releaseSessionJni();
/**
* Return true the object resources have been released.<br>
* @return true the object resources have been released
*/
public boolean isReleased() {
return (0 == mNativeId);
}
/**
* Retrieve the base64-encoded identifier for this inbound group session.
* @return the session ID
* @throws OlmException the failure reason
*/
public String sessionIdentifier() throws OlmException {
try {
return new String(sessionIdentifierJni(), "UTF-8");
} catch (Exception e) {
Log.e(LOG_TAG, "## sessionIdentifier() failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_INBOUND_GROUP_SESSION_IDENTIFIER, e.getMessage());
}
}
/**
* Get a base64-encoded identifier for this inbound group session.
* An exception is thrown if the operation fails.
* @return the base64-encoded identifier
*/
private native byte[] sessionIdentifierJni();
/**
* Provides the first known index.
* @return the first known index.
* @throws OlmException the failure reason
*/
public long getFirstKnownIndex() throws OlmException {
long index = 0;
try {
index = firstKnownIndexJni();
} catch (Exception e) {
Log.e(LOG_TAG, "## getFirstKnownIndex() failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_INBOUND_GROUP_SESSION_FIRST_KNOWN_INDEX, e.getMessage());
}
return index;
}
/**
* Provides the first known index.
* An exception is thrown if the operation fails.
* @return the first known index.
*/
private native long firstKnownIndexJni();
/**
* Tells if the session is verified.
* @return true if the session is verified
* @throws OlmException the failure reason
*/
public boolean isVerified() throws OlmException {
boolean isVerified;
try {
isVerified = isVerifiedJni();
} catch (Exception e) {
Log.e(LOG_TAG, "## isVerified() failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_INBOUND_GROUP_SESSION_IS_VERIFIED, e.getMessage());
}
return isVerified;
}
/**
* Tells if the session is verified.
* @return true if the session is verified
*/
private native boolean isVerifiedJni();
/**
* Export the session from a message index as String.
* @param messageIndex the message index
* @return the session as String
* @throws OlmException the failure reason
*/
public String export(long messageIndex) throws OlmException {
String result = null;
try {
byte[] bytesBuffer = exportJni(messageIndex);
if (null != bytesBuffer) {
result = new String(bytesBuffer, "UTF-8");
Arrays.fill(bytesBuffer, (byte) 0);
}
} catch (Exception e) {
Log.e(LOG_TAG, "## export() failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_INBOUND_GROUP_SESSION_EXPORT, e.getMessage());
}
return result;
}
/**
* Exports the session as byte array from a message index
* An exception is thrown if the operation fails.
* @param messageIndex key used to encrypt the serialized session data
* @return the session saved as bytes array
*/
private native byte[] exportJni(long messageIndex);
/**
* Decrypt the message passed in parameter.<br>
* In case of error, null is returned and an error message description is provided in aErrorMsg.
* @param aEncryptedMsg the message to be decrypted
* @return the decrypted message information
* @exception OlmException the failure reason
*/
public DecryptMessageResult decryptMessage(String aEncryptedMsg) throws OlmException {
DecryptMessageResult result = new DecryptMessageResult();
try {
byte[] decryptedMessageBuffer = decryptMessageJni(aEncryptedMsg.getBytes("UTF-8"), result);
if (null != decryptedMessageBuffer) {
result.mDecryptedMessage = new String(decryptedMessageBuffer, "UTF-8");
Arrays.fill(decryptedMessageBuffer, (byte) 0);
}
} catch (Exception e) {
Log.e(LOG_TAG, "## decryptMessage() failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_INBOUND_GROUP_SESSION_DECRYPT_SESSION, e.getMessage());
}
return result;
}
/**
* Decrypt a message.
* An exception is thrown if the operation fails.
* @param aEncryptedMsg the encrypted message
* @param aDecryptMessageResult the decryptMessage information
* @return the decrypted message
*/
private native byte[] decryptMessageJni(byte[] aEncryptedMsg, DecryptMessageResult aDecryptMessageResult);
//==============================================================================================================
// Serialization management
//==============================================================================================================
/**
* Kick off the serialization mechanism.
* @param aOutStream output stream for serializing
* @throws IOException exception
*/
private void writeObject(ObjectOutputStream aOutStream) throws IOException {
serialize(aOutStream);
}
/**
* Kick off the deserialization mechanism.
* @param aInStream input stream
* @throws Exception exception
*/
private void readObject(ObjectInputStream aInStream) throws Exception {
deserialize(aInStream);
}
/**
* Return the current inbound group session as a bytes buffer.<br>
* The session is serialized and encrypted with aKey.
* In case of failure, an error human readable
* description is provide in aErrorMsg.
* @param aKey encryption key
* @param aErrorMsg error message description
* @return pickled bytes buffer if operation succeed, null otherwise
*/
@Override
protected byte[] serialize(byte[] aKey, StringBuffer aErrorMsg) {
byte[] pickleRetValue = null;
// sanity check
if(null == aErrorMsg) {
Log.e(LOG_TAG,"## serialize(): invalid parameter - aErrorMsg=null");
} else if (null == aKey) {
aErrorMsg.append("Invalid input parameters in serialize()");
} else {
aErrorMsg.setLength(0);
try {
pickleRetValue = serializeJni(aKey);
} catch (Exception e) {
Log.e(LOG_TAG, "## serialize() failed " + e.getMessage());
aErrorMsg.append(e.getMessage());
}
}
return pickleRetValue;
}
/**
* JNI counter part of {@link #serialize(byte[], StringBuffer)}.
* @param aKey encryption key
* @return the serialized session
*/
private native byte[] serializeJni(byte[] aKey);
/**
* Loads an account from a pickled base64 string.<br>
* See {@link #serialize(byte[], StringBuffer)}
* @param aSerializedData pickled account in a bytes buffer
* @param aKey key used to encrypted
*/
@Override
protected void deserialize(byte[] aSerializedData, byte[] aKey) throws Exception {
String errorMsg = null;
try {
if ((null == aSerializedData) || (null == aKey)) {
Log.e(LOG_TAG, "## deserialize(): invalid input parameters");
errorMsg = "invalid input parameters";
} else {
mNativeId = deserializeJni(aSerializedData, aKey);
}
} catch (Exception e) {
Log.e(LOG_TAG, "## deserialize() failed " + e.getMessage());
errorMsg = e.getMessage();
}
if (!TextUtils.isEmpty(errorMsg)) {
releaseSession();
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_DESERIALIZATION, errorMsg);
}
}
/**
* Allocate a new session and initialize it with the serialisation data.<br>
* An exception is thrown if the operation fails.
* @param aSerializedData the session serialisation buffer
* @param aKey the key used to encrypt the serialized account data
* @return the deserialized session
**/
private native long deserializeJni(byte[] aSerializedData, byte[] aKey);
/**
* Return a pickled inbound group session as a bytes buffer.<br>
* The session is serialized and encrypted with aKey.
* In case of failure, an error human readable
* description is provide in aErrorMsg.
* @param aKey encryption key
* @param aErrorMsg error message description
* @return the pickled inbound group session as bytes buffer
*/
public byte[] pickle(byte[] aKey, StringBuffer aErrorMsg) {
return serialize(aKey, aErrorMsg);
}
/**
* Loads an inbound group session from a pickled bytes buffer.<br>
* See {@link #serialize(byte[], StringBuffer)}
* @param aSerializedData bytes buffer
* @param aKey key used to encrypted
* @exception Exception the exception
*/
public void unpickle(byte[] aSerializedData, byte[] aKey) throws Exception {
deserialize(aSerializedData, aKey);
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016 Vector Creations 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.
*/
package org.matrix.olm;
import android.content.Context;
import android.util.Log;
/**
* Olm SDK entry point class.<br> An OlmManager instance must be created at first to enable native library load.
* <br><br>Detailed implementation guide is available at <a href="http://matrix.org/docs/guides/e2e_implementation.html">Implementing End-to-End Encryption in Matrix clients</a>.
*/
public class OlmManager {
private static final String LOG_TAG = "OlmManager";
/**
* Constructor.
*/
public OlmManager() {
}
static {
try {
java.lang.System.loadLibrary("olm");
} catch(UnsatisfiedLinkError e) {
Log.e(LOG_TAG,"Exception loadLibrary() - Msg="+e.getMessage());
}
}
/**
* Provide the android library version
* @return the library version
*/
public String getVersion() {
return BuildConfig.OLM_VERSION;
}
/**
* Provide a detailed version.
* It contains the android and the native libraries versions.
* @param context the context
* @return the detailed version
*/
public String getDetailedVersion(Context context) {
String gitVersion = context.getResources().getString(R.string.git_olm_revision);
String date = context.getResources().getString(R.string.git_olm_revision_date);
return getVersion() + " - olm version (" + getOlmLibVersion() + ") - " + gitVersion + "-" + date;
}
/**
* Provide the native OLM lib version.
* @return the lib version as a string
*/
public String getOlmLibVersion(){
return getOlmLibVersionJni();
}
public native String getOlmLibVersionJni();
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016 Vector Creations 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.
*/
package org.matrix.olm;
/**
* Message class used in Olm sessions to contain the encrypted data.<br>
* See {@link OlmSession#decryptMessage(OlmMessage)} and {@link OlmSession#encryptMessage(String)}.
* <br>Detailed implementation guide is available at <a href="http://matrix.org/docs/guides/e2e_implementation.html">Implementing End-to-End Encryption in Matrix clients</a>.
*/
public class OlmMessage {
/** PRE KEY message type (used to establish new Olm session) **/
public final static int MESSAGE_TYPE_PRE_KEY = 0;
/** normal message type **/
public final static int MESSAGE_TYPE_MESSAGE = 1;
/** the encrypted message **/
public String mCipherText;
/** defined by {@link #MESSAGE_TYPE_MESSAGE} or {@link #MESSAGE_TYPE_PRE_KEY}**/
public long mType;
}

View File

@ -0,0 +1,320 @@
/*
* Copyright 2017 OpenMarket Ltd
* Copyright 2017 Vector Creations 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.
*/
package org.matrix.olm;
import android.text.TextUtils;
import android.util.Log;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Arrays;
/**
* Class used to create an outbound a <a href="http://matrix.org/docs/guides/e2e_implementation.html#starting-a-megolm-session">Megolm session</a>.<br>
* To send a first message in an encrypted room, the client should start a new outbound Megolm session.
* The session ID and the session key must be shared with each device in the room within.
*
* <br><br>Detailed implementation guide is available at <a href="http://matrix.org/docs/guides/e2e_implementation.html">Implementing End-to-End Encryption in Matrix clients</a>.
*/
public class OlmOutboundGroupSession extends CommonSerializeUtils implements Serializable {
private static final long serialVersionUID = -3133097431283604416L;
private static final String LOG_TAG = "OlmOutboundGroupSession";
/** Session Id returned by JNI.<br>
* This value uniquely identifies the native outbound group session instance.
*/
private transient long mNativeId;
/**
* Constructor.<br>
* Create and save a new session native instance ID and
* initialise a new outbound group session.<br>
* @throws OlmException constructor failure
*/
public OlmOutboundGroupSession() throws OlmException {
try {
mNativeId = createNewSessionJni();
} catch (Exception e) {
throw new OlmException(OlmException.EXCEPTION_CODE_CREATE_OUTBOUND_GROUP_SESSION, e.getMessage());
}
}
/**
* Create the corresponding OLM outbound group session in native side.<br>
* An exception is thrown if the operation fails.
* Do not forget to call {@link #releaseSession()} when JAVA side is done.
* @return native session instance identifier (see {@link #mNativeId})
*/
private native long createNewSessionJni();
/**
* Release native session and invalid its JAVA reference counter part.<br>
* Public API for {@link #releaseSessionJni()}.
*/
public void releaseSession() {
if (0 != mNativeId) {
releaseSessionJni();
}
mNativeId = 0;
}
/**
* Destroy the corresponding OLM outbound group session native object.<br>
* This method must ALWAYS be called when this JAVA instance
* is destroyed (ie. garbage collected) to prevent memory leak in native side.
* See {@link #createNewSessionJni()}.
*/
private native void releaseSessionJni();
/**
* Return true the object resources have been released.<br>
* @return true the object resources have been released
*/
public boolean isReleased() {
return (0 == mNativeId);
}
/**
* Get a base64-encoded identifier for this session.
* @return session identifier
* @throws OlmException the failure reason
*/
public String sessionIdentifier() throws OlmException {
try {
return new String(sessionIdentifierJni(), "UTF-8");
} catch (Exception e) {
Log.e(LOG_TAG, "## sessionIdentifier() failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_OUTBOUND_GROUP_SESSION_IDENTIFIER, e.getMessage());
}
}
/**
* Return the session identifier.
* An exception is thrown if the operation fails.
* @return the session identifier
*/
private native byte[] sessionIdentifierJni();
/**
* Get the current message index for this session.<br>
* Each message is sent with an increasing index, this
* method returns the index for the next message.
* @return current session index
*/
public int messageIndex() {
return messageIndexJni();
}
/**
* Get the current message index for this session.<br>
* Each message is sent with an increasing index, this
* method returns the index for the next message.
* An exception is thrown if the operation fails.
* @return current session index
*/
private native int messageIndexJni();
/**
* Get the base64-encoded current ratchet key for this session.<br>
* Each message is sent with a different ratchet key. This method returns the
* ratchet key that will be used for the next message.
* @return outbound session key
* @exception OlmException the failure reason
*/
public String sessionKey() throws OlmException {
try {
byte[] sessionKeyBuffer = sessionKeyJni();
String ret = new String(sessionKeyBuffer, "UTF-8");
Arrays.fill(sessionKeyBuffer, (byte) 0);
return ret;
} catch (Exception e) {
Log.e(LOG_TAG, "## sessionKey() failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_OUTBOUND_GROUP_SESSION_KEY, e.getMessage());
}
}
/**
* Return the session key.
* An exception is thrown if the operation fails.
* @return the session key
*/
private native byte[] sessionKeyJni();
/**
* Encrypt some plain-text message.<br>
* The message given as parameter is encrypted and returned as the return value.
* @param aClearMsg message to be encrypted
* @return the encrypted message
* @exception OlmException the encryption failure reason
*/
public String encryptMessage(String aClearMsg) throws OlmException {
String retValue = null;
if (!TextUtils.isEmpty(aClearMsg)) {
try {
byte[] clearMsgBuffer = aClearMsg.getBytes("UTF-8");
byte[] encryptedBuffer = encryptMessageJni(clearMsgBuffer);
Arrays.fill(clearMsgBuffer, (byte) 0);
if (null != encryptedBuffer) {
retValue = new String(encryptedBuffer , "UTF-8");
}
} catch (Exception e) {
Log.e(LOG_TAG, "## encryptMessage() failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_OUTBOUND_GROUP_ENCRYPT_MESSAGE, e.getMessage());
}
}
return retValue;
}
/**
* Encrypt a bytes buffer messages.
* An exception is thrown if the operation fails.
* @param aClearMsgBuffer the message to encode
* @return the encoded message
*/
private native byte[] encryptMessageJni(byte[] aClearMsgBuffer);
//==============================================================================================================
// Serialization management
//==============================================================================================================
/**
* Kick off the serialization mechanism.
* @param aOutStream output stream for serializing
* @throws IOException exception
*/
private void writeObject(ObjectOutputStream aOutStream) throws IOException {
serialize(aOutStream);
}
/**
* Kick off the deserialization mechanism.
* @param aInStream input stream
* @throws Exception exception
*/
private void readObject(ObjectInputStream aInStream) throws Exception {
deserialize(aInStream);
}
/**
* Return the current outbound group session as a base64 byte buffers.<br>
* The session is serialized and encrypted with aKey.
* In case of failure, an error human readable
* description is provide in aErrorMsg.
* @param aKey encryption key
* @param aErrorMsg error message description
* @return pickled base64 bytes buffer if operation succeed, null otherwise
*/
@Override
protected byte[] serialize(byte[] aKey, StringBuffer aErrorMsg) {
byte[] pickleRetValue = null;
// sanity check
if(null == aErrorMsg) {
Log.e(LOG_TAG,"## serialize(): invalid parameter - aErrorMsg=null");
} else if (null == aKey) {
aErrorMsg.append("Invalid input parameters in serialize()");
} else {
try {
pickleRetValue = serializeJni(aKey);
} catch (Exception e) {
Log.e(LOG_TAG,"## serialize(): failed " + e.getMessage());
aErrorMsg.append(e.getMessage());
}
}
return pickleRetValue;
}
/**
* JNI counter part of {@link #serialize(byte[], StringBuffer)}.
* An exception is thrown if the operation fails.
* @param aKey encryption key
* @return the serialized session
*/
private native byte[] serializeJni(byte[] aKey);
/**
* Loads an account from a pickled base64 string.<br>
* See {@link #serialize(byte[], StringBuffer)}
* @param aSerializedData pickled account in a base64 bytes buffer
* @param aKey key used to encrypted
* @exception Exception the exception
*/
@Override
protected void deserialize(byte[] aSerializedData, byte[] aKey) throws Exception {
String errorMsg = null;
try {
if ((null == aSerializedData) || (null == aKey)) {
Log.e(LOG_TAG, "## deserialize(): invalid input parameters");
errorMsg = "invalid input parameters";
} else {
mNativeId = deserializeJni(aSerializedData, aKey);
}
} catch (Exception e) {
Log.e(LOG_TAG, "## deserialize() failed " + e.getMessage());
errorMsg = e.getMessage();
}
if (!TextUtils.isEmpty(errorMsg)) {
releaseSession();
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_DESERIALIZATION, errorMsg);
}
}
/**
* Allocate a new session and initialize it with the serialisation data.<br>
* An exception is thrown if the operation fails.
* @param aSerializedData the session serialisation buffer
* @param aKey the key used to encrypt the serialized account data
* @return the deserialized session
**/
private native long deserializeJni(byte[] aSerializedData, byte[] aKey);
/**
* Return a pickled outbound group session as a bytes buffer.<br>
* The session is serialized and encrypted with aKey.
* In case of failure, an error human readable
* description is provide in aErrorMsg.
* @param aKey encryption key
* @param aErrorMsg error message description
* @return the pickled outbound group session as bytes buffer
*/
public byte[] pickle(byte[] aKey, StringBuffer aErrorMsg) {
return serialize(aKey, aErrorMsg);
}
/**
* Loads an outbound group session from a pickled bytes buffer.<br>
* See {@link #serialize(byte[], StringBuffer)}
* @param aSerializedData bytes buffer
* @param aKey key used to encrypted
* @exception Exception the exception
*/
public void unpickle(byte[] aSerializedData, byte[] aKey) throws Exception {
deserialize(aSerializedData, aKey);
}
}

View File

@ -0,0 +1,109 @@
/*
* Copyright 2018 New Vector 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.
*/
package org.matrix.olm;
import android.util.Log;
import java.util.Arrays;
public class OlmPkDecryption {
private static final String LOG_TAG = "OlmPkDecryption";
/** Session Id returned by JNI.
* This value uniquely identifies the native session instance.
**/
private transient long mNativeId;
public OlmPkDecryption() throws OlmException {
try {
mNativeId = createNewPkDecryptionJni();
} catch (Exception e) {
throw new OlmException(OlmException.EXCEPTION_CODE_PK_DECRYPTION_CREATION, e.getMessage());
}
}
private native long createNewPkDecryptionJni();
private native void releasePkDecryptionJni();
public void releaseDecryption() {
if (0 != mNativeId) {
releasePkDecryptionJni();
}
mNativeId = 0;
}
public boolean isReleased() {
return (0 == mNativeId);
}
public static native int privateKeyLength();
public String setPrivateKey(byte[] privateKey) throws OlmException {
try {
byte[] key = setPrivateKeyJni(privateKey);
return new String(key, "UTF-8");
} catch (Exception e) {
Log.e(LOG_TAG, "## setPrivateKey(): failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_PK_DECRYPTION_SET_PRIVATE_KEY, e.getMessage());
}
}
private native byte[] setPrivateKeyJni(byte[] privateKey);
public String generateKey() throws OlmException {
try {
byte[] key = generateKeyJni();
return new String(key, "UTF-8");
} catch (Exception e) {
Log.e(LOG_TAG, "## setRecipientKey(): failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_PK_DECRYPTION_GENERATE_KEY, e.getMessage());
}
}
private native byte[] generateKeyJni();
public byte[] privateKey() throws OlmException {
try {
return privateKeyJni();
} catch (Exception e) {
Log.e(LOG_TAG, "## privateKey(): failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_PK_DECRYPTION_PRIVATE_KEY, e.getMessage());
}
}
private native byte[] privateKeyJni();
public String decrypt(OlmPkMessage aMessage) throws OlmException {
if (null == aMessage) {
return null;
}
byte[] plaintextBuffer = decryptJni(aMessage);
try {
String plaintext = new String(plaintextBuffer, "UTF-8");
return plaintext;
} catch (Exception e) {
Log.e(LOG_TAG, "## pkDecrypt(): failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_PK_DECRYPTION_DECRYPT, e.getMessage());
} finally {
Arrays.fill(plaintextBuffer, (byte) 0);
}
}
private native byte[] decryptJni(OlmPkMessage aMessage);
}

View File

@ -0,0 +1,97 @@
/*
* Copyright 2018 New Vector 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.
*/
package org.matrix.olm;
import android.util.Log;
import java.util.Arrays;
public class OlmPkEncryption {
private static final String LOG_TAG = "OlmPkEncryption";
/** Session Id returned by JNI.
* This value uniquely identifies the native session instance.
**/
private transient long mNativeId;
public OlmPkEncryption() throws OlmException {
try {
mNativeId = createNewPkEncryptionJni();
} catch (Exception e) {
throw new OlmException(OlmException.EXCEPTION_CODE_PK_ENCRYPTION_CREATION, e.getMessage());
}
}
private native long createNewPkEncryptionJni();
private native void releasePkEncryptionJni();
public void releaseEncryption() {
if (0 != mNativeId) {
releasePkEncryptionJni();
}
mNativeId = 0;
}
public boolean isReleased() {
return (0 == mNativeId);
}
public void setRecipientKey(String aKey) throws OlmException {
if (null == aKey) {
return;
}
try {
setRecipientKeyJni(aKey.getBytes("UTF-8"));
} catch (Exception e) {
Log.e(LOG_TAG, "## setRecipientKey(): failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_PK_ENCRYPTION_SET_RECIPIENT_KEY, e.getMessage());
}
}
private native void setRecipientKeyJni(byte[] aKey);
public OlmPkMessage encrypt(String aPlaintext) throws OlmException {
if (null == aPlaintext) {
return null;
}
OlmPkMessage encryptedMsgRetValue = new OlmPkMessage();
byte[] plaintextBuffer = null;
try {
plaintextBuffer = aPlaintext.getBytes("UTF-8");
byte[] ciphertextBuffer = encryptJni(plaintextBuffer, encryptedMsgRetValue);
if (null != ciphertextBuffer) {
encryptedMsgRetValue.mCipherText = new String(ciphertextBuffer, "UTF-8");
}
} catch (Exception e) {
Log.e(LOG_TAG, "## pkEncrypt(): failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_PK_ENCRYPTION_ENCRYPT, e.getMessage());
} finally {
if (null != plaintextBuffer) {
Arrays.fill(plaintextBuffer, (byte) 0);
}
}
return encryptedMsgRetValue;
}
private native byte[] encryptJni(byte[] plaintext, OlmPkMessage aMessage);
}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2018 New Vector 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.
*/
package org.matrix.olm;
public class OlmPkMessage {
public String mCipherText;
public String mMac;
public String mEphemeralKey;
}

View File

@ -0,0 +1,100 @@
/*
* Copyright 2019 New Vector 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.
*/
package org.matrix.olm;
import android.util.Log;
import java.util.Arrays;
public class OlmPkSigning {
private static final String LOG_TAG = "OlmPkSigning";
/** PK Signing Id returned by JNI.
* This value uniquely identifies the native PK signing instance.
**/
private transient long mNativeId;
public OlmPkSigning() throws OlmException {
try {
mNativeId = createNewPkSigningJni();
} catch (Exception e) {
throw new OlmException(OlmException.EXCEPTION_CODE_PK_SIGNING_CREATION, e.getMessage());
}
}
private native long createNewPkSigningJni();
private native void releasePkSigningJni();
public void releaseSigning() {
if (0 != mNativeId) {
releasePkSigningJni();
}
mNativeId = 0;
}
public boolean isReleased() {
return (0 == mNativeId);
}
public static native int seedLength();
public static byte[] generateSeed() throws OlmException {
try {
return generateSeedJni();
} catch (Exception e) {
Log.e(LOG_TAG, "## generateSeed(): failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_PK_SIGNING_GENERATE_SEED, e.getMessage());
}
}
public static native byte[] generateSeedJni();
public String initWithSeed(byte[] seed) throws OlmException {
try {
byte[] pubKey = setKeyFromSeedJni(seed);
return new String(pubKey, "UTF-8");
} catch (Exception e) {
Log.e(LOG_TAG, "## initWithSeed(): failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_PK_SIGNING_INIT_WITH_SEED, e.getMessage());
}
}
public native byte[] setKeyFromSeedJni(byte[] seed);
public String sign(String aMessage) throws OlmException {
if (null == aMessage) {
return null;
}
byte[] messageBuffer = null;
try {
messageBuffer = aMessage.getBytes("UTF-8");
byte[] signature = pkSignJni(messageBuffer);
return new String(signature, "UTF-8");
} catch (Exception e) {
Log.e(LOG_TAG, "## pkSign(): failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_PK_SIGNING_SIGN, e.getMessage());
} finally {
if (null != messageBuffer) {
Arrays.fill(messageBuffer, (byte) 0);
}
}
}
private native byte[] pkSignJni(byte[] message);
}

View File

@ -0,0 +1,167 @@
/*
* Copyright 2019 New Vector 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.
*/
package org.matrix.olm;
import android.util.Log;
import java.io.UnsupportedEncodingException;
public class OlmSAS {
private static final String LOG_TAG = OlmSAS.class.getName();
/**
* Session Id returned by JNI.
* This value uniquely identifies the native SAS instance.
**/
private transient long mNativeId;
private String theirPublicKey = null;
public OlmSAS() throws OlmException {
try {
mNativeId = createNewSASJni();
} catch (Exception e) {
throw new OlmException(OlmException.EXCEPTION_CODE_SAS_CREATION, e.getMessage());
}
}
/**
* Gets the Public Key encoded in Base64 with no padding
* @return The public key
* @throws OlmException the failure reason
*/
public String getPublicKey() throws OlmException {
try {
byte[] buffer = getPubKeyJni();
if (null != buffer) {
return new String(buffer, "UTF-8");
}
} catch (Exception e) {
Log.e(LOG_TAG, "## sessionIdentifier(): " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_SAS_ERROR, e.getMessage());
}
return null;
}
/**
* Sets the public key of other user.
*
* @param otherPkey other user public key (base64 encoded with no padding)
* @throws OlmException the failure reason
*/
public void setTheirPublicKey(String otherPkey) throws OlmException {
try {
setTheirPubKey(otherPkey.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new OlmException(OlmException.EXCEPTION_CODE_SAS_ERROR, e.getMessage());
}
this.theirPublicKey = otherPkey;
}
/**
* Generate bytes to use for the short authentication string.
*
* @param info info extra information to mix in when generating the bytes, as
* per the Matrix spec.
* @param byteNumber The size of the short code to generate
* @return The generated shortcode
* @throws OlmException the failure reason
*/
public byte[] generateShortCode(String info, int byteNumber) throws OlmException {
if (theirPublicKey == null || theirPublicKey.isEmpty()) {
throw new OlmException(OlmException.EXCEPTION_CODE_SAS_MISSING_THEIR_PKEY, "call setTheirPublicKey first");
}
try {
return generateShortCodeJni(info.getBytes("UTF-8"), byteNumber);
} catch (Exception e) {
Log.e(LOG_TAG, "## sessionIdentifier(): " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_SAS_GENERATE_SHORT_CODE, e.getMessage());
}
}
public String calculateMac(String message, String info) throws OlmException {
try {
byte[] bytes = calculateMacJni(message.getBytes("UTF-8"), info.getBytes("UTF-8"));
if (bytes != null) return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new OlmException(OlmException.EXCEPTION_CODE_SAS_ERROR, e.getMessage());
}
return null;
}
public String calculateMacFixedBase64(String message, String info) throws OlmException {
try {
byte[] bytes = calculateMacFixedBase64Jni(message.getBytes("UTF-8"), info.getBytes("UTF-8"));
if (bytes != null) return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new OlmException(OlmException.EXCEPTION_CODE_SAS_ERROR, e.getMessage());
}
return null;
}
public String calculateMacLongKdf(String message, String info) throws OlmException {
try {
byte[] bytes = calculateMacLongKdfJni(message.getBytes("UTF-8"), info.getBytes("UTF-8"));
if (bytes != null) return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new OlmException(OlmException.EXCEPTION_CODE_SAS_ERROR, e.getMessage());
}
return null;
}
/**
* Create an OLM session in native side.<br>
* Do not forget to call {@link #releaseSASJni()} when JAVA side is done.
*
* @return native account instance identifier or throw an exception.
*/
private native long createNewSASJni();
/**
* Destroy the corresponding OLM session native object.<br>
* This method must ALWAYS be called when this JAVA instance
* is destroyed (ie. garbage collected) to prevent memory leak in native side.
* See {@link #createNewSASJni()}.
*/
private native void releaseSASJni();
private native byte[] getPubKeyJni();
private native void setTheirPubKey(byte[] pubKey);
private native byte[] generateShortCodeJni(byte[] info, int byteNumber);
private native byte[] calculateMacJni(byte[] message, byte[] info);
private native byte[] calculateMacFixedBase64Jni(byte[] message, byte[] info);
private native byte[] calculateMacLongKdfJni(byte[] message, byte[] info);
/**
* Release native session and invalid its JAVA reference counter part.<br>
* Public API for {@link #releaseSASJni()}.
*/
public void releaseSas() {
if (0 != mNativeId) {
releaseSASJni();
}
mNativeId = 0;
}
}

View File

@ -0,0 +1,494 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016 Vector Creations 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.
*/
package org.matrix.olm;
import android.text.TextUtils;
import android.util.Log;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Arrays;
/**
* Session class used to create Olm sessions in conjunction with {@link OlmAccount} class.<br>
* Olm session is used to encrypt data between devices, especially to create Olm group sessions (see {@link OlmOutboundGroupSession} and {@link OlmInboundGroupSession}).<br>
* To establish an Olm session with Bob, Alice calls {@link #initOutboundSession(OlmAccount, String, String)} with Bob's identity and onetime keys. Then Alice generates an encrypted PRE_KEY message ({@link #encryptMessage(String)})
* used by Bob to open the Olm session in his side with {@link #initOutboundSession(OlmAccount, String, String)}.
* From this step on, messages can be exchanged by using {@link #encryptMessage(String)} and {@link #decryptMessage(OlmMessage)}.
* <br><br>Detailed implementation guide is available at <a href="http://matrix.org/docs/guides/e2e_implementation.html">Implementing End-to-End Encryption in Matrix clients</a>.
*/
public class OlmSession extends CommonSerializeUtils implements Serializable {
private static final long serialVersionUID = -8975488639186976419L;
private static final String LOG_TAG = "OlmSession";
/** Session Id returned by JNI.
* This value uniquely identifies the native session instance.
**/
private transient long mNativeId;
public OlmSession() throws OlmException {
try {
mNativeId = createNewSessionJni();
} catch (Exception e) {
throw new OlmException(OlmException.EXCEPTION_CODE_INIT_SESSION_CREATION, e.getMessage());
}
}
/**
* Create an OLM session in native side.<br>
* Do not forget to call {@link #releaseSession()} when JAVA side is done.
* @return native account instance identifier or throw an exception.
*/
private native long createNewSessionJni();
/**
* Getter on the session ID.
* @return native session ID
*/
long getOlmSessionId(){
return mNativeId;
}
/**
* Destroy the corresponding OLM session native object.<br>
* This method must ALWAYS be called when this JAVA instance
* is destroyed (ie. garbage collected) to prevent memory leak in native side.
* See {@link #createNewSessionJni()}.
*/
private native void releaseSessionJni();
/**
* Release native session and invalid its JAVA reference counter part.<br>
* Public API for {@link #releaseSessionJni()}.
*/
public void releaseSession() {
if (0 != mNativeId) {
releaseSessionJni();
}
mNativeId = 0;
}
/**
* Return true the object resources have been released.<br>
* @return true the object resources have been released
*/
public boolean isReleased() {
return (0 == mNativeId);
}
/**
* Creates a new out-bound session for sending messages to a recipient
* identified by an identity key and a one time key.<br>
* @param aAccount the account to associate with this session
* @param aTheirIdentityKey the identity key of the recipient
* @param aTheirOneTimeKey the one time key of the recipient
* @exception OlmException the failure reason
*/
public void initOutboundSession(OlmAccount aAccount, String aTheirIdentityKey, String aTheirOneTimeKey) throws OlmException {
if ((null == aAccount) || TextUtils.isEmpty(aTheirIdentityKey) || TextUtils.isEmpty(aTheirOneTimeKey)) {
Log.e(LOG_TAG, "## initOutboundSession(): invalid input parameters");
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_INIT_OUTBOUND_SESSION, "invalid input parameters");
} else {
try {
initOutboundSessionJni(aAccount.getOlmAccountId(), aTheirIdentityKey.getBytes("UTF-8"), aTheirOneTimeKey.getBytes("UTF-8"));
} catch (Exception e) {
Log.e(LOG_TAG, "## initOutboundSession(): " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_INIT_OUTBOUND_SESSION, e.getMessage());
}
}
}
/**
* Create a new in-bound session for sending/receiving messages from an
* incoming PRE_KEY message.<br> The recipient is defined as the entity
* with whom the session is established.
* An exception is thrown if the operation fails.
* @param aOlmAccountId account instance
* @param aTheirIdentityKey the identity key of the recipient
* @param aTheirOneTimeKey the one time key of the recipient
**/
private native void initOutboundSessionJni(long aOlmAccountId, byte[] aTheirIdentityKey, byte[] aTheirOneTimeKey);
/**
* Create a new in-bound session for sending/receiving messages from an
* incoming PRE_KEY message ({@link OlmMessage#MESSAGE_TYPE_PRE_KEY}).<br>
* This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
* @param aAccount the account to associate with this session
* @param aPreKeyMsg PRE KEY message
* @exception OlmException the failure reason
*/
public void initInboundSession(OlmAccount aAccount, String aPreKeyMsg) throws OlmException {
if ((null == aAccount) || TextUtils.isEmpty(aPreKeyMsg)){
Log.e(LOG_TAG, "## initInboundSession(): invalid input parameters");
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_INIT_INBOUND_SESSION, "invalid input parameters");
} else {
try {
initInboundSessionJni(aAccount.getOlmAccountId(), aPreKeyMsg.getBytes("UTF-8"));
} catch (Exception e) {
Log.e(LOG_TAG, "## initInboundSession(): " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_INIT_INBOUND_SESSION, e.getMessage());
}
}
}
/**
* Create a new in-bound session for sending/receiving messages from an
* incoming PRE_KEY message.<br>
* An exception is thrown if the operation fails.
* @param aOlmAccountId account instance
* @param aOneTimeKeyMsg PRE_KEY message
*/
private native void initInboundSessionJni(long aOlmAccountId, byte[] aOneTimeKeyMsg);
/**
* Create a new in-bound session for sending/receiving messages from an
* incoming PRE_KEY({@link OlmMessage#MESSAGE_TYPE_PRE_KEY}) message based on the sender identity key.<br>
* Public API for {@link #initInboundSessionFromIdKeyJni(long, byte[], byte[])}.
* This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
* This method must only be called the first time a pre-key message is received from an inbound session.
* @param aAccount the account to associate with this session
* @param aTheirIdentityKey the sender identity key
* @param aPreKeyMsg PRE KEY message
* @exception OlmException the failure reason
*/
public void initInboundSessionFrom(OlmAccount aAccount, String aTheirIdentityKey, String aPreKeyMsg) throws OlmException {
if ( (null==aAccount) || TextUtils.isEmpty(aPreKeyMsg)){
Log.e(LOG_TAG, "## initInboundSessionFrom(): invalid input parameters");
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_INIT_INBOUND_SESSION_FROM, "invalid input parameters");
} else {
try {
initInboundSessionFromIdKeyJni(aAccount.getOlmAccountId(), aTheirIdentityKey.getBytes("UTF-8"), aPreKeyMsg.getBytes("UTF-8"));
} catch (Exception e) {
Log.e(LOG_TAG, "## initInboundSessionFrom(): " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_INIT_INBOUND_SESSION_FROM, e.getMessage());
}
}
}
/**
* Create a new in-bound session for sending/receiving messages from an
* incoming PRE_KEY message based on the recipient identity key.<br>
* An exception is thrown if the operation fails.
* @param aOlmAccountId account instance
* @param aTheirIdentityKey the identity key of the recipient
* @param aOneTimeKeyMsg encrypted message
*/
private native void initInboundSessionFromIdKeyJni(long aOlmAccountId, byte[] aTheirIdentityKey, byte[] aOneTimeKeyMsg);
/**
* Get the session identifier.<br> Will be the same for both ends of the
* conversation. The session identifier is returned as a String object.
* Session Id sample: "session_id":"M4fOVwD6AABrkTKl"
* Public API for {@link #getSessionIdentifierJni()}.
* @return the session ID
* @exception OlmException the failure reason
*/
public String sessionIdentifier() throws OlmException {
try {
byte[] buffer = getSessionIdentifierJni();
if (null != buffer) {
return new String(buffer, "UTF-8");
}
} catch (Exception e) {
Log.e(LOG_TAG, "## sessionIdentifier(): " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_SESSION_IDENTIFIER, e.getMessage());
}
return null;
}
/**
* Get the session identifier for this session.
* An exception is thrown if the operation fails.
* @return the session identifier
*/
private native byte[] getSessionIdentifierJni();
public String sessionDescribe() throws OlmException {
try {
byte[] buffer = olmSessionDescribeJni();
if (null != buffer) {
return new String(buffer, "UTF-8");
}
} catch (Exception e) {
Log.e(LOG_TAG, "## sessionDescribe(): " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_SESSION_DESCRIBE, e.getMessage());
}
return null;
}
private native byte[] olmSessionDescribeJni();
/**
* Checks if the PRE_KEY({@link OlmMessage#MESSAGE_TYPE_PRE_KEY}) message is for this in-bound session.<br>
* This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
* Public API for {@link #matchesInboundSessionJni(byte[])}.
* @param aOneTimeKeyMsg PRE KEY message
* @return true if the one time key matches.
*/
public boolean matchesInboundSession(String aOneTimeKeyMsg) {
boolean retCode = false;
try {
retCode = matchesInboundSessionJni(aOneTimeKeyMsg.getBytes("UTF-8"));
} catch (Exception e) {
Log.e(LOG_TAG, "## matchesInboundSession(): failed " + e.getMessage());
}
return retCode;
}
/**
* Checks if the PRE_KEY message is for this in-bound session.<br>
* This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
* An exception is thrown if the operation fails.
* @param aOneTimeKeyMsg PRE KEY message
* @return true if the PRE_KEY message matches
*/
private native boolean matchesInboundSessionJni(byte[] aOneTimeKeyMsg);
/**
* Checks if the PRE_KEY({@link OlmMessage#MESSAGE_TYPE_PRE_KEY}) message is for this in-bound session based on the sender identity key.<br>
* This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
* Public API for {@link #matchesInboundSessionJni(byte[])}.
* @param aTheirIdentityKey the sender identity key
* @param aOneTimeKeyMsg PRE KEY message
* @return this if operation succeed, null otherwise
*/
public boolean matchesInboundSessionFrom(String aTheirIdentityKey, String aOneTimeKeyMsg) {
boolean retCode = false;
try {
retCode = matchesInboundSessionFromIdKeyJni(aTheirIdentityKey.getBytes("UTF-8"), aOneTimeKeyMsg.getBytes("UTF-8"));
} catch (Exception e) {
Log.e(LOG_TAG, "## matchesInboundSessionFrom(): failed " + e.getMessage());
}
return retCode;
}
/**
* Checks if the PRE_KEY message is for this in-bound session based on the sender identity key.<br>
* This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
* An exception is thrown if the operation fails.
* @param aTheirIdentityKey the identity key of the sender
* @param aOneTimeKeyMsg PRE KEY message
* @return true if the PRE_KEY message matches.
*/
private native boolean matchesInboundSessionFromIdKeyJni(byte[] aTheirIdentityKey, byte[] aOneTimeKeyMsg);
/**
* Encrypt a message using the session.<br>
* The encrypted message is returned in a OlmMessage object.
* Public API for {@link #encryptMessageJni(byte[], OlmMessage)}.
* @param aClearMsg message to encrypted
* @return the encrypted message
* @exception OlmException the failure reason
*/
public OlmMessage encryptMessage(String aClearMsg) throws OlmException {
if (null == aClearMsg) {
return null;
}
OlmMessage encryptedMsgRetValue = new OlmMessage();
try {
byte[] clearMsgBuffer = aClearMsg.getBytes("UTF-8");
byte[] encryptedMessageBuffer = encryptMessageJni(clearMsgBuffer, encryptedMsgRetValue);
Arrays.fill(clearMsgBuffer, (byte) 0);
if (null != encryptedMessageBuffer) {
encryptedMsgRetValue.mCipherText = new String(encryptedMessageBuffer, "UTF-8");
}
} catch (Exception e) {
Log.e(LOG_TAG, "## encryptMessage(): failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_ENCRYPT_MESSAGE, e.getMessage());
}
return encryptedMsgRetValue;
}
/**
* Encrypt a message using the session.<br>
* An exception is thrown if the operation fails.
* @param aClearMsg clear text message
* @param aEncryptedMsg ciphered message
* @return the encrypted message
*/
private native byte[] encryptMessageJni(byte[] aClearMsg, OlmMessage aEncryptedMsg);
/**
* Decrypt a message using the session.<br>
* The encrypted message is given as a OlmMessage object.
* @param aEncryptedMsg message to decrypt
* @return the decrypted message
* @exception OlmException the failure reason
*/
public String decryptMessage(OlmMessage aEncryptedMsg) throws OlmException {
if (null == aEncryptedMsg) {
return null;
}
try {
byte[] plaintextBuffer = decryptMessageJni(aEncryptedMsg);
String plaintext = new String(plaintextBuffer, "UTF-8");
Arrays.fill(plaintextBuffer, (byte) 0);
return plaintext;
} catch (Exception e) {
Log.e(LOG_TAG, "## decryptMessage(): failed " + e.getMessage());
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_DECRYPT_MESSAGE, e.getMessage());
}
}
/**
* Decrypt a message using the session.<br>
* An exception is thrown if the operation fails.
* @param aEncryptedMsg message to decrypt
* @return the decrypted message
*/
private native byte[] decryptMessageJni(OlmMessage aEncryptedMsg);
//==============================================================================================================
// Serialization management
//==============================================================================================================
/**
* Kick off the serialization mechanism.
* @param aOutStream output stream for serializing
* @throws IOException exception
*/
private void writeObject(ObjectOutputStream aOutStream) throws IOException {
serialize(aOutStream);
}
/**
* Kick off the deserialization mechanism.
* @param aInStream input stream
* @throws IOException exception
* @throws ClassNotFoundException exception
*/
private void readObject(ObjectInputStream aInStream) throws Exception {
deserialize(aInStream);
}
/**
* Return a session as a bytes buffer.<br>
* The account is serialized and encrypted with aKey.
* In case of failure, an error human readable
* description is provide in aErrorMsg.
* @param aKey encryption key
* @param aErrorMsg error message description
* @return session as a bytes buffer
*/
@Override
protected byte[] serialize(byte[] aKey, StringBuffer aErrorMsg) {
byte[] pickleRetValue = null;
// sanity check
if(null == aErrorMsg) {
Log.e(LOG_TAG,"## serializeDataWithKey(): invalid parameter - aErrorMsg=null");
} else if (null == aKey) {
aErrorMsg.append("Invalid input parameters in serializeDataWithKey()");
} else {
aErrorMsg.setLength(0);
try {
pickleRetValue = serializeJni(aKey);
} catch (Exception e) {
Log.e(LOG_TAG,"## serializeDataWithKey(): failed " + e.getMessage());
aErrorMsg.append(e.getMessage());
}
}
return pickleRetValue;
}
/**
* Serialize and encrypt session instance.<br>
* An exception is thrown if the operation fails.
* @param aKeyBuffer key used to encrypt the serialized account data
* @return the serialised account as bytes buffer.
**/
private native byte[] serializeJni(byte[] aKeyBuffer);
/**
* Loads an account from a pickled base64 string.<br>
* See {@link #serialize(byte[], StringBuffer)}
* @param aSerializedData pickled account in a base64 string format
* @param aKey key used to encrypted
*/
@Override
protected void deserialize(byte[] aSerializedData, byte[] aKey) throws Exception {
String errorMsg = null;
try {
if ((null == aSerializedData) || (null == aKey)) {
Log.e(LOG_TAG, "## deserialize(): invalid input parameters");
errorMsg = "invalid input parameters";
} else {
mNativeId = deserializeJni(aSerializedData, aKey);
}
} catch (Exception e) {
Log.e(LOG_TAG, "## deserialize() failed " + e.getMessage());
errorMsg = e.getMessage();
}
if (!TextUtils.isEmpty(errorMsg)) {
releaseSession();
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_DESERIALIZATION, errorMsg);
}
}
/**
* Allocate a new session and initialize it with the serialisation data.<br>
* An exception is thrown if the operation fails.
* @param aSerializedData the session serialisation buffer
* @param aKey the key used to encrypt the serialized account data
* @return the deserialized session
**/
private native long deserializeJni(byte[] aSerializedData, byte[] aKey);
/**
* Return a pickled session as a bytes buffer.<br>
* The session is serialized and encrypted with aKey.
* In case of failure, an error human readable
* description is provide in aErrorMsg.
* @param aKey encryption key
* @param aErrorMsg error message description
* @return the pickled session as bytes buffer
*/
public byte[] pickle(byte[] aKey, StringBuffer aErrorMsg) {
return serialize(aKey, aErrorMsg);
}
/**
* Loads a session from a pickled bytes buffer.<br>
* See {@link #serialize(byte[], StringBuffer)}
* @param aSerializedData bytes buffer
* @param aKey key used to encrypted
* @exception Exception the exception
*/
public void unpickle(byte[] aSerializedData, byte[] aKey) throws Exception {
deserialize(aSerializedData, aKey);
}
}

View File

@ -0,0 +1,240 @@
/*
* Copyright 2017 OpenMarket Ltd
* Copyright 2017 Vector Creations 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.
*/
package org.matrix.olm;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONObject;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Olm SDK helper class.
*/
public class OlmUtility {
private static final String LOG_TAG = "OlmUtility";
public static final int RANDOM_KEY_SIZE = 32;
/** Instance Id returned by JNI.
* This value uniquely identifies this utility instance.
**/
private long mNativeId;
public OlmUtility() throws OlmException {
initUtility();
}
/**
* Create a native utility instance.
* To be called before any other API call.
* @exception OlmException the exception
*/
private void initUtility() throws OlmException {
try {
mNativeId = createUtilityJni();
} catch (Exception e) {
throw new OlmException(OlmException.EXCEPTION_CODE_UTILITY_CREATION, e.getMessage());
}
}
private native long createUtilityJni();
/**
* Release native instance.<br>
* Public API for {@link #releaseUtilityJni()}.
*/
public void releaseUtility() {
if (0 != mNativeId) {
releaseUtilityJni();
}
mNativeId = 0;
}
private native void releaseUtilityJni();
/**
* Verify an ed25519 signature.<br>
* An exception is thrown if the operation fails.
* @param aSignature the base64-encoded message signature to be checked.
* @param aFingerprintKey the ed25519 key (fingerprint key)
* @param aMessage the signed message
* @exception OlmException the failure reason
*/
public void verifyEd25519Signature(String aSignature, String aFingerprintKey, String aMessage) throws OlmException {
String errorMessage;
byte[] messageBuffer = null;
try {
if (TextUtils.isEmpty(aSignature) || TextUtils.isEmpty(aFingerprintKey) || TextUtils.isEmpty(aMessage)) {
Log.e(LOG_TAG, "## verifyEd25519Signature(): invalid input parameters");
errorMessage = "JAVA sanity check failure - invalid input parameters";
} else {
messageBuffer = aMessage.getBytes("UTF-8");
errorMessage = verifyEd25519SignatureJni(aSignature.getBytes("UTF-8"), aFingerprintKey.getBytes("UTF-8"), messageBuffer);
}
} catch (Exception e) {
Log.e(LOG_TAG, "## verifyEd25519Signature(): failed " + e.getMessage());
errorMessage = e.getMessage();
} finally {
if (messageBuffer != null) {
Arrays.fill(messageBuffer, (byte) 0);
}
}
if (!TextUtils.isEmpty(errorMessage)) {
throw new OlmException(OlmException.EXCEPTION_CODE_UTILITY_VERIFY_SIGNATURE, errorMessage);
}
}
/**
* Verify an ed25519 signature.
* Return a human readable error message in case of verification failure.
* @param aSignature the base64-encoded message signature to be checked.
* @param aFingerprintKey the ed25519 key
* @param aMessage the signed message
* @return null if validation succeed, the error message string if operation failed
*/
private native String verifyEd25519SignatureJni(byte[] aSignature, byte[] aFingerprintKey, byte[] aMessage);
/**
* Compute the hash(SHA-256) value of the string given in parameter(aMessageToHash).<br>
* The hash value is the returned by the method.
* @param aMessageToHash message to be hashed
* @return hash value if operation succeed, null otherwise
*/
public String sha256(String aMessageToHash) {
String hashRetValue = null;
if (null != aMessageToHash) {
byte[] messageBuffer = null;
try {
messageBuffer = aMessageToHash.getBytes("UTF-8");
hashRetValue = new String(sha256Jni(messageBuffer), "UTF-8");
} catch (Exception e) {
Log.e(LOG_TAG, "## sha256(): failed " + e.getMessage());
} finally {
if (null != messageBuffer) {
Arrays.fill(messageBuffer, (byte) 0);
}
}
}
return hashRetValue;
}
/**
* Compute the digest (SHA 256) for the message passed in parameter.<br>
* The digest value is the function return value.
* An exception is thrown if the operation fails.
* @param aMessage the message
* @return digest of the message.
**/
private native byte[] sha256Jni(byte[] aMessage);
/**
* Helper method to compute a string based on random integers.
* @return bytes buffer containing randoms integer values
*/
public static byte[] getRandomKey() {
SecureRandom secureRandom = new SecureRandom();
byte[] buffer = new byte[RANDOM_KEY_SIZE];
secureRandom.nextBytes(buffer);
// the key is saved as string
// so avoid the UTF8 marker bytes
for(int i = 0; i < RANDOM_KEY_SIZE; i++) {
buffer[i] = (byte)(buffer[i] & 0x7F);
}
return buffer;
}
/**
* Return true the object resources have been released.<br>
* @return true the object resources have been released
*/
public boolean isReleased() {
return (0 == mNativeId);
}
/**
* Build a string-string dictionary from a jsonObject.<br>
* @param jsonObject the object to parse
* @return the map
*/
public static Map<String, String> toStringMap(JSONObject jsonObject) {
if (null != jsonObject) {
HashMap<String, String> map = new HashMap<>();
Iterator<String> keysItr = jsonObject.keys();
while(keysItr.hasNext()) {
String key = keysItr.next();
try {
Object value = jsonObject.get(key);
if (value instanceof String) {
map.put(key, (String) value);
} else {
Log.e(LOG_TAG, "## toStringMap(): unexpected type " + value.getClass());
}
} catch (Exception e) {
Log.e(LOG_TAG, "## toStringMap(): failed " + e.getMessage());
}
}
return map;
}
return null;
}
/**
* Build a string-string dictionary of string dictionary from a jsonObject.<br>
* @param jsonObject the object to parse
* @return the map
*/
public static Map<String, Map<String, String>> toStringMapMap(JSONObject jsonObject) {
if (null != jsonObject) {
HashMap<String, Map<String, String>> map = new HashMap<>();
Iterator<String> keysItr = jsonObject.keys();
while(keysItr.hasNext()) {
String key = keysItr.next();
try {
Object value = jsonObject.get(key);
if (value instanceof JSONObject) {
map.put(key, toStringMap((JSONObject) value));
} else {
Log.e(LOG_TAG, "## toStringMapMap(): unexpected type " + value.getClass());
}
} catch (Exception e) {
Log.e(LOG_TAG, "## toStringMapMap(): failed " + e.getMessage());
}
}
return map;
}
return null;
}
}

View File

@ -0,0 +1,67 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := olm
SRC_ROOT_DIR := ../../../../..
include $(LOCAL_PATH)/$(SRC_ROOT_DIR)/common.mk
OLM_VERSION := $(MAJOR).$(MINOR).$(PATCH)
$(info LOCAL_PATH=$(LOCAL_PATH))
$(info SRC_ROOT_DIR=$(SRC_ROOT_DIR))
$(info OLM_VERSION=$(OLM_VERSION))
LOCAL_CPPFLAGS+= -std=c++11 -Wall
LOCAL_CONLYFLAGS+= -std=c99
LOCAL_CFLAGS+= -DOLMLIB_VERSION_MAJOR=$(MAJOR) \
-DOLMLIB_VERSION_MINOR=$(MINOR) \
-DOLMLIB_VERSION_PATCH=$(PATCH)
#LOCAL_CFLAGS+= -DNDK_DEBUG
LOCAL_CFLAGS+=-fstack-protector-all -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Wall
LOCAL_LDFLAGS=-z relro -z now
LOCAL_C_INCLUDES+= $(LOCAL_PATH)/$(SRC_ROOT_DIR)/include/ \
$(LOCAL_PATH)/$(SRC_ROOT_DIR)/lib
$(info LOCAL_C_INCLUDES=$(LOCAL_C_INCLUDES))
LOCAL_SRC_FILES := $(SRC_ROOT_DIR)/src/account.cpp \
$(SRC_ROOT_DIR)/src/base64.cpp \
$(SRC_ROOT_DIR)/src/cipher.cpp \
$(SRC_ROOT_DIR)/src/crypto.cpp \
$(SRC_ROOT_DIR)/src/memory.cpp \
$(SRC_ROOT_DIR)/src/message.cpp \
$(SRC_ROOT_DIR)/src/olm.cpp \
$(SRC_ROOT_DIR)/src/pickle.cpp \
$(SRC_ROOT_DIR)/src/ratchet.cpp \
$(SRC_ROOT_DIR)/src/session.cpp \
$(SRC_ROOT_DIR)/src/utility.cpp \
$(SRC_ROOT_DIR)/src/pk.cpp \
$(SRC_ROOT_DIR)/src/sas.c \
$(SRC_ROOT_DIR)/src/ed25519.c \
$(SRC_ROOT_DIR)/src/error.c \
$(SRC_ROOT_DIR)/src/inbound_group_session.c \
$(SRC_ROOT_DIR)/src/megolm.c \
$(SRC_ROOT_DIR)/src/outbound_group_session.c \
$(SRC_ROOT_DIR)/src/pickle_encoding.c \
$(SRC_ROOT_DIR)/lib/crypto-algorithms/sha256.c \
$(SRC_ROOT_DIR)/lib/crypto-algorithms/aes.c \
$(SRC_ROOT_DIR)/lib/curve25519-donna/curve25519-donna.c \
olm_account.cpp \
olm_session.cpp \
olm_jni_helper.cpp \
olm_inbound_group_session.cpp \
olm_outbound_group_session.cpp \
olm_utility.cpp \
olm_manager.cpp \
olm_pk.cpp \
olm_sas.cpp
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)

View File

@ -0,0 +1,3 @@
APP_PLATFORM := android-16
APP_ABI := arm64-v8a armeabi-v7a x86_64 x86
APP_STL := c++_static

View File

@ -0,0 +1,850 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016 Vector Creations Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "olm_account.h"
using namespace AndroidOlmSdk;
/**
* Init memory allocation for account creation.
* @return valid memory allocation, NULL otherwise
**/
OlmAccount* initializeAccountMemory()
{
size_t accountSize = olm_account_size();
OlmAccount* accountPtr = (OlmAccount*)malloc(accountSize);
if (accountPtr)
{
// init account object
accountPtr = olm_account(accountPtr);
LOGD("## initializeAccountMemory(): success - OLM account size=%lu",static_cast<long unsigned int>(accountSize));
}
else
{
LOGE("## initializeAccountMemory(): failure - OOM");
}
return accountPtr;
}
/**
* Create a new account and return it to JAVA side.<br>
* Since a C prt is returned as a jlong, special care will be taken
* to make the cast (OlmAccount* => jlong) platform independent.
* @return the initialized OlmAccount* instance or throw an exception if fails
**/
JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(createNewAccountJni)(JNIEnv *env, jobject thiz)
{
const char* errorMessage = NULL;
OlmAccount *accountPtr = initializeAccountMemory();
// init account memory allocation
if (!accountPtr)
{
LOGE("## initNewAccount(): failure - init account OOM");
errorMessage = "init account OOM";
}
else
{
// get random buffer size
size_t randomSize = olm_create_account_random_length(accountPtr);
LOGD("## initNewAccount(): randomSize=%lu", static_cast<long unsigned int>(randomSize));
uint8_t *randomBuffPtr = NULL;
size_t accountRetCode;
// allocate random buffer
if ((0 != randomSize) && !setRandomInBuffer(env, &randomBuffPtr, randomSize))
{
LOGE("## initNewAccount(): failure - random buffer init");
errorMessage = "random buffer init";
}
else
{
// create account
accountRetCode = olm_create_account(accountPtr, (void*)randomBuffPtr, randomSize);
if (accountRetCode == olm_error())
{
LOGE("## initNewAccount(): failure - account creation failed Msg=%s", olm_account_last_error(accountPtr));
errorMessage = olm_account_last_error(accountPtr);
}
LOGD("## initNewAccount(): success - OLM account created");
LOGD("## initNewAccount(): success - accountPtr=%p (jlong)(intptr_t)accountPtr=%lld",accountPtr,(jlong)(intptr_t)accountPtr);
}
if (randomBuffPtr)
{
memset(randomBuffPtr, 0, randomSize);
free(randomBuffPtr);
}
}
if (errorMessage)
{
// release the allocated data
if (accountPtr)
{
olm_clear_account(accountPtr);
free(accountPtr);
}
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return (jlong)(intptr_t)accountPtr;
}
/**
* Release the account allocation made by initializeAccountMemory().<br>
* This method MUST be called when java counter part account instance is done.
*/
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(releaseAccountJni)(JNIEnv *env, jobject thiz)
{
LOGD("## releaseAccountJni(): IN");
OlmAccount* accountPtr = getAccountInstanceId(env, thiz);
if (!accountPtr)
{
LOGE(" ## releaseAccountJni(): failure - invalid Account ptr=NULL");
}
else
{
LOGD(" ## releaseAccountJni(): accountPtr=%p",accountPtr);
olm_clear_account(accountPtr);
LOGD(" ## releaseAccountJni(): IN");
// even if free(NULL) does not crash, logs are performed for debug purpose
free(accountPtr);
LOGD(" ## releaseAccountJni(): OUT");
}
}
// *********************************************************************
// ************************* IDENTITY KEYS API *************************
// *********************************************************************
/**
* Get identity keys: Ed25519 fingerprint key and Curve25519 identity key.<br>
* The keys are returned in the byte array.
* @return the identity keys or throw an exception if it fails
**/
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(identityKeysJni)(JNIEnv *env, jobject thiz)
{
const char* errorMessage = NULL;
jbyteArray byteArrayRetValue = NULL;
OlmAccount* accountPtr = getAccountInstanceId(env, thiz);
if (!accountPtr)
{
LOGE("## identityKeys(): failure - invalid Account ptr=NULL");
errorMessage = "invalid Account ptr";
}
else
{
LOGD("## identityKeys(): accountPtr =%p", accountPtr);
// identity keys allocation
size_t identityKeysLength = olm_account_identity_keys_length(accountPtr);
uint8_t *identityKeysBytesPtr = (uint8_t*)malloc(identityKeysLength);
if (!identityKeysBytesPtr)
{
LOGE("## identityKeys(): failure - identity keys array OOM");
errorMessage = "identity keys array OOM";
}
else
{
// retrieve key pairs in identityKeysBytesPtr
size_t keysResult = olm_account_identity_keys(accountPtr, identityKeysBytesPtr, identityKeysLength);
if (keysResult == olm_error())
{
errorMessage = (const char *)olm_account_last_error(accountPtr);
LOGE("## identityKeys(): failure - error getting identity keys Msg=%s", errorMessage);
}
else
{
// allocate the byte array to be returned to java
byteArrayRetValue = env->NewByteArray(identityKeysLength);
if (!byteArrayRetValue)
{
LOGE("## identityKeys(): failure - return byte array OOM");
errorMessage = "byte array OOM";
}
else
{
env->SetByteArrayRegion(byteArrayRetValue, 0/*offset*/, identityKeysLength, (const jbyte*)identityKeysBytesPtr);
LOGD("## identityKeys(): success - result=%lu", static_cast<long unsigned int>(keysResult));
}
}
free(identityKeysBytesPtr);
}
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return byteArrayRetValue;
}
// *********************************************************************
// ************************* ONE TIME KEYS API *************************
// *********************************************************************
/**
* Get the public parts of the unpublished "one time keys" for the account.<br>
* The returned data is a JSON-formatted object with the single property
* <tt>curve25519</tt>, which is itself an object mapping key id to
* base64-encoded Curve25519 key.<br>
* @return byte array containing the one time keys or throw an exception if it fails
*/
JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(maxOneTimeKeysJni)(JNIEnv *env, jobject thiz)
{
OlmAccount* accountPtr = getAccountInstanceId(env, thiz);
size_t maxKeys = -1;
if (!accountPtr)
{
LOGE("## maxOneTimeKey(): failure - invalid Account ptr=NULL");
}
else
{
maxKeys = olm_account_max_number_of_one_time_keys(accountPtr);
}
LOGD("## maxOneTimeKey(): Max keys=%lu", static_cast<long unsigned int>(maxKeys));
return (jlong)maxKeys;
}
/**
* Generate "one time keys".
* An exception is thrown if the operation fails.
* @param aNumberOfKeys number of keys to generate
**/
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(generateOneTimeKeysJni)(JNIEnv *env, jobject thiz, jint aNumberOfKeys)
{
const char* errorMessage = NULL;
OlmAccount *accountPtr = getAccountInstanceId(env, thiz);
if (!accountPtr)
{
LOGE("## generateOneTimeKeysJni(): failure - invalid Account ptr");
errorMessage = "invalid Account ptr";
}
else
{
// keys memory allocation
size_t randomLength = olm_account_generate_one_time_keys_random_length(accountPtr, (size_t)aNumberOfKeys);
LOGD("## generateOneTimeKeysJni(): randomLength=%lu", static_cast<long unsigned int>(randomLength));
uint8_t *randomBufferPtr = NULL;
if ((0 != randomLength) && !setRandomInBuffer(env, &randomBufferPtr, randomLength))
{
LOGE("## generateOneTimeKeysJni(): failure - random buffer init");
errorMessage = "random buffer init";
}
else
{
LOGD("## generateOneTimeKeysJni(): accountPtr =%p aNumberOfKeys=%d",accountPtr, aNumberOfKeys);
// retrieve key pairs in keysBytesPtr
size_t result = olm_account_generate_one_time_keys(accountPtr, (size_t)aNumberOfKeys, (void*)randomBufferPtr, randomLength);
if (result == olm_error())
{
errorMessage = olm_account_last_error(accountPtr);
LOGE("## generateOneTimeKeysJni(): failure - error generating one time keys Msg=%s", errorMessage);
}
else
{
LOGD("## generateOneTimeKeysJni(): success - result=%lu", static_cast<long unsigned int>(result));
}
}
if (randomBufferPtr)
{
memset(randomBufferPtr, 0, randomLength);
free(randomBufferPtr);
}
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
}
/**
* Get "one time keys".<br>
* Return the public parts of the unpublished "one time keys" for the account
* @return a valid byte array if operation succeed, null otherwise
**/
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(oneTimeKeysJni)(JNIEnv *env, jobject thiz)
{
const char* errorMessage = NULL;
jbyteArray byteArrayRetValue = NULL;
OlmAccount* accountPtr = getAccountInstanceId(env, thiz);
LOGD("## oneTimeKeysJni(): IN");
if (!accountPtr)
{
LOGE("## oneTimeKeysJni(): failure - invalid Account ptr");
errorMessage = "invalid Account ptr";
}
else
{
// keys memory allocation
size_t keysLength = olm_account_one_time_keys_length(accountPtr);
uint8_t *keysBytesPtr = (uint8_t *)malloc(keysLength*sizeof(uint8_t));
if (!keysBytesPtr)
{
LOGE("## oneTimeKeysJni(): failure - one time keys array OOM");
errorMessage = "one time keys array OOM";
}
else
{
// retrieve key pairs in keysBytesPtr
size_t keysResult = olm_account_one_time_keys(accountPtr, keysBytesPtr, keysLength);
if (keysResult == olm_error()) {
LOGE("## oneTimeKeysJni(): failure - error getting one time keys Msg=%s",(const char *)olm_account_last_error(accountPtr));
errorMessage = (const char *)olm_account_last_error(accountPtr);
}
else
{
// allocate the byte array to be returned to java
byteArrayRetValue = env->NewByteArray(keysLength);
if (!byteArrayRetValue)
{
LOGE("## oneTimeKeysJni(): failure - return byte array OOM");
errorMessage = "return byte array OOM";
}
else
{
env->SetByteArrayRegion(byteArrayRetValue, 0/*offset*/, keysLength, (const jbyte*)keysBytesPtr);
LOGD("## oneTimeKeysJni(): success");
}
}
free(keysBytesPtr);
}
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return byteArrayRetValue;
}
/**
* Remove the "one time keys" that the session used from the account.
* An exception is thrown if the operation fails.
* @param aNativeOlmSessionId session instance
**/
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(removeOneTimeKeysJni)(JNIEnv *env, jobject thiz, jlong aNativeOlmSessionId)
{
const char* errorMessage = NULL;
OlmAccount* accountPtr = NULL;
OlmSession* sessionPtr = (OlmSession*)aNativeOlmSessionId;
if (!sessionPtr)
{
LOGE("## removeOneTimeKeysJni(): failure - invalid session ptr");
errorMessage = "invalid session ptr";
}
else if (!(accountPtr = getAccountInstanceId(env, thiz)))
{
LOGE("## removeOneTimeKeysJni(): failure - invalid account ptr");
errorMessage = "invalid account ptr";
}
else
{
size_t result = olm_remove_one_time_keys(accountPtr, sessionPtr);
if (result == olm_error())
{ // the account doesn't have any matching "one time keys"..
LOGW("## removeOneTimeKeysJni(): failure - removing one time keys Msg=%s", olm_account_last_error(accountPtr));
errorMessage = (const char *)olm_account_last_error(accountPtr);
}
else
{
LOGD("## removeOneTimeKeysJni(): success");
}
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
}
/**
* Mark the current set of "one time keys" as being published.
* An exception is thrown if the operation fails.
**/
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(markOneTimeKeysAsPublishedJni)(JNIEnv *env, jobject thiz)
{
const char* errorMessage = NULL;
OlmAccount* accountPtr = getAccountInstanceId(env, thiz);
if (!accountPtr)
{
LOGE("## markOneTimeKeysAsPublishedJni(): failure - invalid account ptr");
errorMessage = "invalid account ptr";
}
else
{
size_t result = olm_account_mark_keys_as_published(accountPtr);
if (result == olm_error())
{
LOGW("## markOneTimeKeysAsPublishedJni(): failure - Msg=%s",(const char *)olm_account_last_error(accountPtr));
errorMessage = (const char *)olm_account_last_error(accountPtr);
}
else
{
LOGD("## markOneTimeKeysAsPublishedJni(): success - retCode=%lu",static_cast<long unsigned int>(result));
}
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
}
/**
* Generate "fallback key".
* An exception is thrown if the operation fails.
**/
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(generateFallbackKeyJni)(JNIEnv *env, jobject thiz)
{
const char* errorMessage = NULL;
OlmAccount *accountPtr = getAccountInstanceId(env, thiz);
if (!accountPtr)
{
LOGE("## generateFallbackKeyJni(): failure - invalid Account ptr");
errorMessage = "invalid Account ptr";
}
else
{
// keys memory allocation
size_t randomLength = olm_account_generate_fallback_key_random_length(accountPtr);
LOGD("## generateFallbackKeyJni(): randomLength=%lu", static_cast<long unsigned int>(randomLength));
uint8_t *randomBufferPtr = NULL;
if ((0 != randomLength) && !setRandomInBuffer(env, &randomBufferPtr, randomLength))
{
LOGE("## generateFallbackKeyJni(): failure - random buffer init");
errorMessage = "random buffer init";
}
else
{
LOGD("## generateFallbackKeyJni(): accountPtr =%p", accountPtr);
// retrieve key pairs in keysBytesPtr
size_t result = olm_account_generate_fallback_key(accountPtr, (void*)randomBufferPtr, randomLength);
if (result == olm_error())
{
errorMessage = olm_account_last_error(accountPtr);
LOGE("## generateFallbackKeyJni(): failure - error generating fallback keys Msg=%s", errorMessage);
}
else
{
LOGD("## generateFallbackKeyJni(): success - result=%lu", static_cast<long unsigned int>(result));
}
}
if (randomBufferPtr)
{
memset(randomBufferPtr, 0, randomLength);
free(randomBufferPtr);
}
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
}
/**
* Get "fallback key".<br>
* Return the public parts of the unpublished "fallback key" for the account
* @return a valid byte array if operation succeed, null otherwise
**/
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(fallbackKeyJni)(JNIEnv *env, jobject thiz)
{
const char* errorMessage = NULL;
jbyteArray byteArrayRetValue = NULL;
OlmAccount* accountPtr = getAccountInstanceId(env, thiz);
LOGD("## fallbackKeyJni(): IN");
if (!accountPtr)
{
LOGE("## fallbackKeyJni(): failure - invalid Account ptr");
errorMessage = "invalid Account ptr";
}
else
{
// keys memory allocation
size_t keysLength = olm_account_unpublished_fallback_key_length(accountPtr);
uint8_t *keysBytesPtr = (uint8_t *)malloc(keysLength*sizeof(uint8_t));
if (!keysBytesPtr)
{
LOGE("## fallbackKeyJni(): failure - fallback key OOM");
errorMessage = "fallback key OOM";
}
else
{
// retrieve key pairs in keysBytesPtr
size_t keysResult = olm_account_unpublished_fallback_key(accountPtr, keysBytesPtr, keysLength);
if (keysResult == olm_error()) {
LOGE("## fallbackKeyJni(): failure - error getting fallback key Msg=%s",(const char *)olm_account_last_error(accountPtr));
errorMessage = (const char *)olm_account_last_error(accountPtr);
}
else
{
// allocate the byte array to be returned to java
byteArrayRetValue = env->NewByteArray(keysLength);
if (!byteArrayRetValue)
{
LOGE("## fallbackKeyJni(): failure - return byte array OOM");
errorMessage = "return byte array OOM";
}
else
{
env->SetByteArrayRegion(byteArrayRetValue, 0/*offset*/, keysLength, (const jbyte*)keysBytesPtr);
LOGD("## fallbackKeyJni(): success");
}
}
free(keysBytesPtr);
}
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return byteArrayRetValue;
}
/**
* Forget about the old fallback key.
*
* This should be called once you are reasonably certain that you will not
* receive any more messages that use the old fallback key (e.g. 5 minutes
* after the new fallback key has been published).
**/
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(forgetFallbackKeyJni)(JNIEnv *env, jobject thiz)
{
const char* errorMessage = NULL;
OlmAccount *accountPtr = getAccountInstanceId(env, thiz);
if (!accountPtr)
{
LOGE("## forgetFallbackKeyJni(): failure - invalid Account ptr");
errorMessage = "invalid Account ptr";
}
else
{
olm_account_forget_old_fallback_key(accountPtr);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
}
/**
* Sign a message with the ed25519 key (fingerprint) for this account.<br>
* The signed message is returned by the function.
* @param aMessage message to sign
* @return the signed message, null otherwise
**/
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(signMessageJni)(JNIEnv *env, jobject thiz, jbyteArray aMessage)
{
const char* errorMessage = NULL;
OlmAccount* accountPtr = NULL;
jbyteArray signedMsgRetValueBuffer = NULL;
if (!aMessage)
{
LOGE("## signMessageJni(): failure - invalid aMessage param");
errorMessage = "invalid aMessage param";
}
else if (!(accountPtr = getAccountInstanceId(env, thiz)))
{
LOGE("## signMessageJni(): failure - invalid account ptr");
errorMessage = "invalid account ptr";
}
else
{
int messageLength = env->GetArrayLength(aMessage);
jbyte* messageToSign = env->GetByteArrayElements(aMessage, NULL);
// signature memory allocation
size_t signatureLength = olm_account_signature_length(accountPtr);
void* signedMsgPtr = malloc(signatureLength * sizeof(uint8_t));
if (!signedMsgPtr)
{
LOGE("## signMessageJni(): failure - signature allocation OOM");
errorMessage = "signature allocation OOM";
}
else
{
// sign message
size_t resultSign = olm_account_sign(accountPtr,
(void*)messageToSign,
(size_t)messageLength,
signedMsgPtr,
signatureLength);
if (resultSign == olm_error())
{
LOGE("## signMessageJni(): failure - error signing message Msg=%s",(const char *)olm_account_last_error(accountPtr));
errorMessage = (const char *)olm_account_last_error(accountPtr);
}
else
{
LOGD("## signMessageJni(): success - retCode=%lu signatureLength=%lu", static_cast<long unsigned int>(resultSign), static_cast<long unsigned int>(signatureLength));
signedMsgRetValueBuffer = env->NewByteArray(signatureLength);
env->SetByteArrayRegion(signedMsgRetValueBuffer, 0 , signatureLength, (jbyte*)signedMsgPtr);
}
free(signedMsgPtr);
}
// release messageToSign
if (messageToSign)
{
env->ReleaseByteArrayElements(aMessage, messageToSign, JNI_ABORT);
}
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return signedMsgRetValueBuffer;
}
/**
* Serialize and encrypt account instance.<br>
* @param aKeyBuffer key used to encrypt the serialized account data
* @return the serialised account as bytes buffer.
**/
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(serializeJni)(JNIEnv *env, jobject thiz, jbyteArray aKeyBuffer)
{
const char* errorMessage = NULL;
jbyteArray pickledDataRetValue = 0;
jbyte* keyPtr = NULL;
jboolean keyIsCopied = JNI_FALSE;
OlmAccount* accountPtr = NULL;
LOGD("## serializeJni(): IN");
if (!aKeyBuffer)
{
LOGE(" ## serializeJni(): failure - invalid key");
errorMessage = "invalid key";
}
else if (!(accountPtr = getAccountInstanceId(env, thiz)))
{
LOGE(" ## serializeJni(): failure - invalid account ptr");
errorMessage = "invalid account ptr";
}
else if (!(keyPtr = env->GetByteArrayElements(aKeyBuffer, &keyIsCopied)))
{
LOGE(" ## serializeJni(): failure - keyPtr JNI allocation OOM");
errorMessage = "keyPtr JNI allocation OOM";
}
else
{
size_t pickledLength = olm_pickle_account_length(accountPtr);
size_t keyLength = (size_t)env->GetArrayLength(aKeyBuffer);
LOGD(" ## serializeJni(): pickledLength=%lu keyLength=%lu",static_cast<long unsigned int>(pickledLength), static_cast<long unsigned int>(keyLength));
void* pickledPtr = malloc(pickledLength * sizeof(uint8_t));
if (!pickledPtr)
{
LOGE(" ## serializeJni(): failure - pickledPtr buffer OOM");
errorMessage = "pickledPtr buffer OOM";
}
else
{
size_t result = olm_pickle_account(accountPtr,
(void const *)keyPtr,
keyLength,
(void*)pickledPtr,
pickledLength);
if (result == olm_error())
{
errorMessage = olm_account_last_error(accountPtr);
LOGE(" ## serializeJni(): failure - olm_pickle_account() Msg=%s", errorMessage);
}
else
{
LOGD(" ## serializeJni(): success - result=%lu pickled=%.*s", static_cast<long unsigned int>(result), static_cast<int>(pickledLength), static_cast<char*>(pickledPtr));
pickledDataRetValue = env->NewByteArray(pickledLength);
env->SetByteArrayRegion(pickledDataRetValue, 0 , pickledLength, (jbyte*)pickledPtr);
}
free(pickledPtr);
}
}
// free alloc
if (keyPtr)
{
if (keyIsCopied) {
memset(keyPtr, 0, (size_t)env->GetArrayLength(aKeyBuffer));
}
env->ReleaseByteArrayElements(aKeyBuffer, keyPtr, JNI_ABORT);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return pickledDataRetValue;
}
/**
* Allocate a new account and initialise it with the serialisation data.<br>
* @param aSerializedDataBuffer the account serialisation buffer
* @param aKeyBuffer the key used to encrypt the serialized account data
* @return the deserialised account
**/
JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(deserializeJni)(JNIEnv *env, jobject thiz, jbyteArray aSerializedDataBuffer, jbyteArray aKeyBuffer)
{
const char* errorMessage = NULL;
OlmAccount* accountPtr = NULL;
jbyte* keyPtr = NULL;
jboolean keyIsCopied = JNI_FALSE;
jbyte* pickledPtr = NULL;
LOGD("## deserializeJni(): IN");
if (!aKeyBuffer)
{
LOGE(" ## deserializeJni(): failure - invalid key");
errorMessage = "invalid key";
}
else if (!aSerializedDataBuffer)
{
LOGE(" ## deserializeJni(): failure - invalid serialized data");
errorMessage = "invalid serialized data";
}
else if (!(accountPtr = initializeAccountMemory()))
{
LOGE(" ## deserializeJni(): failure - account failure OOM");
errorMessage = "account failure OOM";
}
else if (!(keyPtr = env->GetByteArrayElements(aKeyBuffer, &keyIsCopied)))
{
LOGE(" ## deserializeJni(): failure - keyPtr JNI allocation OOM");
errorMessage = "keyPtr JNI allocation OOM";
}
else if (!(pickledPtr = env->GetByteArrayElements(aSerializedDataBuffer, 0)))
{
LOGE(" ## deserializeJni(): failure - pickledPtr JNI allocation OOM");
errorMessage = "pickledPtr JNI allocation OOM";
}
else
{
size_t pickledLength = (size_t)env->GetArrayLength(aSerializedDataBuffer);
size_t keyLength = (size_t)env->GetArrayLength(aKeyBuffer);
LOGD(" ## deserializeJni(): pickledLength=%lu keyLength=%lu",static_cast<long unsigned int>(pickledLength), static_cast<long unsigned int>(keyLength));
LOGD(" ## deserializeJni(): pickled=%.*s", static_cast<int> (pickledLength), (char const *)pickledPtr);
size_t result = olm_unpickle_account(accountPtr,
(void const *)keyPtr,
keyLength,
(void*)pickledPtr,
pickledLength);
if (result == olm_error())
{
errorMessage = olm_account_last_error(accountPtr);
LOGE(" ## deserializeJni(): failure - olm_unpickle_account() Msg=%s", errorMessage);
}
else
{
LOGD(" ## deserializeJni(): success - result=%lu ", static_cast<long unsigned int>(result));
}
}
// free alloc
if (keyPtr)
{
if (keyIsCopied) {
memset(keyPtr, 0, (size_t)env->GetArrayLength(aKeyBuffer));
}
env->ReleaseByteArrayElements(aKeyBuffer, keyPtr, JNI_ABORT);
}
if (pickledPtr)
{
env->ReleaseByteArrayElements(aSerializedDataBuffer, pickledPtr, JNI_ABORT);
}
if (errorMessage)
{
if (accountPtr)
{
olm_clear_account(accountPtr);
free(accountPtr);
}
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return (jlong)(intptr_t)accountPtr;
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2017 OpenMarket Ltd
* Copyright 2017 Vector Creations 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 _OLMACCOUNT_H
#define _OLMACCOUNT_H
#include "olm_jni.h"
#include "olm/olm.h"
#define OLM_ACCOUNT_FUNC_DEF(func_name) FUNC_DEF(OlmAccount,func_name)
#define OLM_MANAGER_FUNC_DEF(func_name) FUNC_DEF(OlmManager,func_name)
#ifdef __cplusplus
extern "C" {
#endif
// account creation/destruction
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(releaseAccountJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(createNewAccountJni)(JNIEnv *env, jobject thiz);
// identity keys
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(identityKeysJni)(JNIEnv *env, jobject thiz);
// one time keys
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(oneTimeKeysJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(maxOneTimeKeysJni)(JNIEnv *env, jobject thiz);
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(generateOneTimeKeysJni)(JNIEnv *env, jobject thiz, jint aNumberOfKeys);
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(removeOneTimeKeysJni)(JNIEnv *env, jobject thiz, jlong aNativeOlmSessionId);
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(markOneTimeKeysAsPublishedJni)(JNIEnv *env, jobject thiz);
// fallback keys
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(generateFallbackKeyJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(fallbackKeyJni)(JNIEnv *env, jobject thiz);
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(forgetFallbackKeyJni)(JNIEnv *env, jobject thiz);
// signing
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(signMessageJni)(JNIEnv *env, jobject thiz, jbyteArray aMessage);
// serialization
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(serializeJni)(JNIEnv *env, jobject thiz, jbyteArray aKeyBuffer);
JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(deserializeJni)(JNIEnv *env, jobject thiz, jbyteArray aSerializedDataBuffer, jbyteArray aKeyBuffer);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,654 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016 Vector Creations Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "olm_inbound_group_session.h"
using namespace AndroidOlmSdk;
/**
* Release the session allocation made by initializeInboundGroupSessionMemory().<br>
* This method MUST be called when java counter part account instance is done.
*/
JNIEXPORT void OLM_INBOUND_GROUP_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env, jobject thiz)
{
OlmInboundGroupSession* sessionPtr = getInboundGroupSessionInstanceId(env,thiz);
LOGD("## releaseSessionJni(): InBound group session IN");
if (!sessionPtr)
{
LOGE("## releaseSessionJni(): failure - invalid inbound group session instance");
}
else
{
LOGD(" ## releaseSessionJni(): sessionPtr=%p", sessionPtr);
#ifdef ENABLE_JNI_LOG
size_t retCode = olm_clear_inbound_group_session(sessionPtr);
LOGD(" ## releaseSessionJni(): clear_inbound_group_session=%lu",static_cast<long unsigned int>(retCode));
#else
olm_clear_inbound_group_session(sessionPtr);
#endif
LOGD(" ## releaseSessionJni(): free IN");
free(sessionPtr);
LOGD(" ## releaseSessionJni(): free OUT");
}
}
/**
* Initialize a new inbound group session and return it to JAVA side.<br>
* Since a C prt is returned as a jlong, special care will be taken
* to make the cast (OlmInboundGroupSession* => jlong) platform independent.
* @param aSessionKeyBuffer session key from an outbound session
* @param isImported true when the session key has been retrieved from a backup
* @return the initialized OlmInboundGroupSession* instance or throw an exception it fails.
**/
JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(createNewSessionJni)(JNIEnv *env, jobject thiz, jbyteArray aSessionKeyBuffer, jboolean isImported)
{
const char* errorMessage = NULL;
OlmInboundGroupSession* sessionPtr = NULL;
jbyte* sessionKeyPtr = NULL;
jboolean sessionWasCopied = JNI_FALSE;
size_t sessionSize = olm_inbound_group_session_size();
LOGD("## createNewSessionJni(): inbound group session IN");
if (!sessionSize)
{
LOGE(" ## createNewSessionJni(): failure - inbound group session size = 0");
errorMessage = "inbound group session size = 0";
}
else if (!(sessionPtr = (OlmInboundGroupSession*)malloc(sessionSize)))
{
LOGE(" ## createNewSessionJni(): failure - inbound group session OOM");
errorMessage = "inbound group session OOM";
}
else if (!aSessionKeyBuffer)
{
LOGE(" ## createNewSessionJni(): failure - invalid aSessionKey");
errorMessage = "invalid aSessionKey";
}
else if (!(sessionKeyPtr = env->GetByteArrayElements(aSessionKeyBuffer, &sessionWasCopied)))
{
LOGE(" ## createNewSessionJni(): failure - session key JNI allocation OOM");
errorMessage = "Session key JNI allocation OOM";
}
else
{
sessionPtr = olm_inbound_group_session(sessionPtr);
size_t sessionKeyLength = (size_t)env->GetArrayLength(aSessionKeyBuffer);
LOGD(" ## createNewSessionJni(): sessionKeyLength=%lu", static_cast<long unsigned int>(sessionKeyLength));
size_t sessionResult;
if (JNI_FALSE == isImported)
{
LOGD(" ## createNewSessionJni(): init");
sessionResult = olm_init_inbound_group_session(sessionPtr, (const uint8_t*)sessionKeyPtr, sessionKeyLength);
}
else
{
LOGD(" ## createNewSessionJni(): import");
sessionResult = olm_import_inbound_group_session(sessionPtr, (const uint8_t*)sessionKeyPtr, sessionKeyLength);
}
if (sessionResult == olm_error())
{
errorMessage = olm_inbound_group_session_last_error(sessionPtr);
LOGE(" ## createNewSessionJni(): failure - init inbound session creation Msg=%s", errorMessage);
}
else
{
LOGD(" ## createNewSessionJni(): success - result=%lu", static_cast<long unsigned int>(sessionResult));
}
}
if (sessionKeyPtr)
{
if (sessionWasCopied) {
memset(sessionKeyPtr, 0, (size_t)env->GetArrayLength(aSessionKeyBuffer));
}
env->ReleaseByteArrayElements(aSessionKeyBuffer, sessionKeyPtr, JNI_ABORT);
}
if (errorMessage)
{
// release the allocated session
if (sessionPtr)
{
olm_clear_inbound_group_session(sessionPtr);
free(sessionPtr);
}
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return (jlong)(intptr_t)sessionPtr;
}
/**
* Get a base64-encoded identifier for this inbound group session.
* An exception is thrown if the operation fails.
* @return the base64-encoded identifier
*/
JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(sessionIdentifierJni)(JNIEnv *env, jobject thiz)
{
const char* errorMessage = NULL;
OlmInboundGroupSession *sessionPtr = getInboundGroupSessionInstanceId(env, thiz);
jbyteArray returnValue = 0;
LOGD("## sessionIdentifierJni(): inbound group session IN");
if (!sessionPtr)
{
LOGE(" ## sessionIdentifierJni(): failure - invalid inbound group session instance");
errorMessage = "invalid inbound group session instance";
}
else
{
// get the size to alloc
size_t lengthSessionId = olm_inbound_group_session_id_length(sessionPtr);
LOGD(" ## sessionIdentifierJni(): inbound group session lengthSessionId=%lu",static_cast<long unsigned int>(lengthSessionId));
uint8_t *sessionIdPtr = (uint8_t*)malloc(lengthSessionId*sizeof(uint8_t));
if (!sessionIdPtr)
{
LOGE(" ## sessionIdentifierJni(): failure - inbound group session identifier allocation OOM");
errorMessage = "inbound group session identifier allocation OOM";
}
else
{
size_t result = olm_inbound_group_session_id(sessionPtr, sessionIdPtr, lengthSessionId);
if (result == olm_error())
{
errorMessage = (const char *)olm_inbound_group_session_last_error(sessionPtr);
LOGE(" ## sessionIdentifierJni(): failure - get inbound group session identifier failure Msg=%s",(const char *)olm_inbound_group_session_last_error(sessionPtr));
}
else
{
LOGD(" ## sessionIdentifierJni(): success - inbound group session result=%lu sessionId=%.*s",static_cast<long unsigned int>(result), static_cast<int>(result), (char*)sessionIdPtr);
returnValue = env->NewByteArray(result);
env->SetByteArrayRegion(returnValue, 0 , result, (jbyte*)sessionIdPtr);
}
free(sessionIdPtr);
}
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return returnValue;
}
/**
* Decrypt a message.
* An exception is thrown if the operation fails.
* @param aEncryptedMsg the encrypted message
* @param aDecryptMessageResult the decryptMessage information
* @return the decrypted message
*/
JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(decryptMessageJni)(JNIEnv *env, jobject thiz, jbyteArray aEncryptedMsgBuffer, jobject aDecryptionResult)
{
jbyteArray decryptedMsgBuffer = 0;
const char* errorMessage = NULL;
OlmInboundGroupSession *sessionPtr = getInboundGroupSessionInstanceId(env, thiz);
jbyte *encryptedMsgPtr = NULL;
jclass indexObjJClass = 0;
jfieldID indexMsgFieldId;
LOGD("## decryptMessageJni(): inbound group session IN");
if (!sessionPtr)
{
LOGE(" ## decryptMessageJni(): failure - invalid inbound group session ptr=NULL");
errorMessage = "invalid inbound group session ptr=NULL";
}
else if (!aEncryptedMsgBuffer)
{
LOGE(" ## decryptMessageJni(): failure - invalid encrypted message");
errorMessage = "invalid encrypted message";
}
else if (!aDecryptionResult)
{
LOGE(" ## decryptMessageJni(): failure - invalid index object");
errorMessage = "invalid index object";
}
else if (!(encryptedMsgPtr = env->GetByteArrayElements(aEncryptedMsgBuffer, 0)))
{
LOGE(" ## decryptMessageJni(): failure - encrypted message JNI allocation OOM");
errorMessage = "encrypted message JNI allocation OOM";
}
else if (!(indexObjJClass = env->GetObjectClass(aDecryptionResult)))
{
LOGE("## decryptMessageJni(): failure - unable to get index class");
errorMessage = "unable to get index class";
}
else if (!(indexMsgFieldId = env->GetFieldID(indexObjJClass,"mIndex","J")))
{
LOGE("## decryptMessageJni(): failure - unable to get index type field");
errorMessage = "unable to get index type field";
}
else
{
// get encrypted message length
size_t encryptedMsgLength = (size_t)env->GetArrayLength(aEncryptedMsgBuffer);
uint8_t *tempEncryptedPtr = static_cast<uint8_t*>(malloc(encryptedMsgLength*sizeof(uint8_t)));
// create a dedicated temp buffer to be used in next Olm API calls
if (!tempEncryptedPtr)
{
LOGE(" ## decryptMessageJni(): failure - tempEncryptedPtr allocation OOM");
errorMessage = "tempEncryptedPtr allocation OOM";
}
else
{
memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength);
LOGD(" ## decryptMessageJni(): encryptedMsgLength=%lu encryptedMsg=%.*s",static_cast<long unsigned int>(encryptedMsgLength), static_cast<int>(encryptedMsgLength), encryptedMsgPtr);
// get max plaintext length
size_t maxPlainTextLength = olm_group_decrypt_max_plaintext_length(sessionPtr,
tempEncryptedPtr,
encryptedMsgLength);
if (maxPlainTextLength == olm_error())
{
errorMessage = olm_inbound_group_session_last_error(sessionPtr);
LOGE(" ## decryptMessageJni(): failure - olm_group_decrypt_max_plaintext_length Msg=%s", errorMessage);
}
else
{
LOGD(" ## decryptMessageJni(): maxPlaintextLength=%lu",static_cast<long unsigned int>(maxPlainTextLength));
uint32_t messageIndex = 0;
// allocate output decrypted message
uint8_t *plainTextMsgPtr = static_cast<uint8_t*>(malloc(maxPlainTextLength*sizeof(uint8_t)));
// decrypt, but before reload encrypted buffer (previous one was destroyed)
memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength);
size_t plaintextLength = olm_group_decrypt(sessionPtr,
tempEncryptedPtr,
encryptedMsgLength,
plainTextMsgPtr,
maxPlainTextLength,
&messageIndex);
if (plaintextLength == olm_error())
{
errorMessage = olm_inbound_group_session_last_error(sessionPtr);
LOGE(" ## decryptMessageJni(): failure - olm_group_decrypt Msg=%s", errorMessage);
}
else
{
// update index
env->SetLongField(aDecryptionResult, indexMsgFieldId, (jlong)messageIndex);
decryptedMsgBuffer = env->NewByteArray(plaintextLength);
env->SetByteArrayRegion(decryptedMsgBuffer, 0 , plaintextLength, (jbyte*)plainTextMsgPtr);
LOGD(" ## decryptMessageJni(): UTF-8 Conversion - decrypted returnedLg=%lu OK",static_cast<long unsigned int>(plaintextLength));
}
if (plainTextMsgPtr)
{
memset(plainTextMsgPtr, 0, maxPlainTextLength*sizeof(uint8_t));
free(plainTextMsgPtr);
}
}
if (tempEncryptedPtr)
{
free(tempEncryptedPtr);
}
}
}
// free alloc
if (encryptedMsgPtr)
{
env->ReleaseByteArrayElements(aEncryptedMsgBuffer, encryptedMsgPtr, JNI_ABORT);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return decryptedMsgBuffer;
}
/**
* Provides the first known index.
* An exception is thrown if the operation fails.
* @return the first known index
*/
JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(firstKnownIndexJni)(JNIEnv *env, jobject thiz)
{
const char* errorMessage = NULL;
OlmInboundGroupSession *sessionPtr = getInboundGroupSessionInstanceId(env, thiz);
long returnValue = 0;
LOGD("## firstKnownIndexJni(): inbound group session IN");
if (!sessionPtr)
{
LOGE(" ## firstKnownIndexJni(): failure - invalid inbound group session instance");
errorMessage = "invalid inbound group session instance";
}
else
{
returnValue = olm_inbound_group_session_first_known_index(sessionPtr);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return returnValue;
}
/**
* Tells if the session is verified.
* An exception is thrown if the operation fails.
* @return true if the session is verified
*/
JNIEXPORT jboolean OLM_INBOUND_GROUP_SESSION_FUNC_DEF(isVerifiedJni)(JNIEnv *env, jobject thiz)
{
const char* errorMessage = NULL;
OlmInboundGroupSession *sessionPtr = getInboundGroupSessionInstanceId(env, thiz);
jboolean returnValue = JNI_FALSE;
LOGD("## isVerifiedJni(): inbound group session IN");
if (!sessionPtr)
{
LOGE(" ## isVerifiedJni(): failure - invalid inbound group session instance");
errorMessage = "invalid inbound group session instance";
}
else
{
LOGE(" ## isVerifiedJni(): faaa %d", olm_inbound_group_session_is_verified(sessionPtr));
returnValue = (0 != olm_inbound_group_session_is_verified(sessionPtr)) ? JNI_TRUE : JNI_FALSE;
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return returnValue;
}
/**
* Exports the session as byte array from a message index
* An exception is thrown if the operation fails.
* @param messageIndex key used to encrypt the serialized session data
* @return the session saved as bytes array
**/
JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(exportJni)(JNIEnv *env, jobject thiz, jlong messageIndex) {
jbyteArray exportedByteArray = 0;
const char* errorMessage = NULL;
OlmInboundGroupSession *sessionPtr = getInboundGroupSessionInstanceId(env, thiz);
LOGD("## exportJni(): inbound group session IN");
if (!sessionPtr)
{
LOGE(" ## exportJni (): failure - invalid inbound group session instance");
errorMessage = "invalid inbound group session instance";
}
else
{
size_t length = olm_export_inbound_group_session_length(sessionPtr);
LOGD(" ## exportJni(): length =%lu", static_cast<long unsigned int>(length));
void *bufferPtr = malloc(length * sizeof(uint8_t));
if (!bufferPtr)
{
LOGE(" ## exportJni(): failure - pickledPtr buffer OOM");
errorMessage = "pickledPtr buffer OOM";
}
else
{
size_t result = olm_export_inbound_group_session(sessionPtr,
(uint8_t*)bufferPtr,
length,
(long) messageIndex);
if (result == olm_error())
{
errorMessage = olm_inbound_group_session_last_error(sessionPtr);
LOGE(" ## exportJni(): failure - olm_export_inbound_group_session() Msg=%s", errorMessage);
}
else
{
LOGD(" ## exportJni(): success - result=%lu buffer=%.*s", static_cast<long unsigned int>(result), static_cast<int>(length), static_cast<char*>(bufferPtr));
exportedByteArray = env->NewByteArray(length);
env->SetByteArrayRegion(exportedByteArray, 0 , length, (jbyte*)bufferPtr);
// clean before leaving
memset(bufferPtr, 0, length);
}
free(bufferPtr);
}
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return exportedByteArray;
}
/**
* Serialize and encrypt session instance into a base64 string.<br>
* An exception is thrown if the operation fails.
* @param aKeyBuffer key used to encrypt the serialized session data
* @return a base64 string if operation succeed, null otherwise
**/
JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(serializeJni)(JNIEnv *env, jobject thiz, jbyteArray aKeyBuffer)
{
const char* errorMessage = NULL;
jbyteArray pickledDataRet = 0;
jbyte* keyPtr = NULL;
jboolean keyWasCopied = JNI_FALSE;
OlmInboundGroupSession* sessionPtr = getInboundGroupSessionInstanceId(env, thiz);
LOGD("## inbound group session serializeJni(): IN");
if (!sessionPtr)
{
LOGE(" ## serializeJni(): failure - invalid session ptr");
errorMessage = "invalid session ptr";
}
else if (!aKeyBuffer)
{
LOGE(" ## serializeJni(): failure - invalid key");
errorMessage = "invalid key";
}
else if (!(keyPtr = env->GetByteArrayElements(aKeyBuffer, &keyWasCopied)))
{
LOGE(" ## serializeJni(): failure - keyPtr JNI allocation OOM");
errorMessage = "keyPtr JNI allocation OOM";
}
else
{
size_t pickledLength = olm_pickle_inbound_group_session_length(sessionPtr);
size_t keyLength = (size_t)env->GetArrayLength(aKeyBuffer);
LOGD(" ## serializeJni(): pickledLength=%lu keyLength=%lu", static_cast<long unsigned int>(pickledLength), static_cast<long unsigned int>(keyLength));
void *pickledPtr = malloc(pickledLength*sizeof(uint8_t));
if (!pickledPtr)
{
LOGE(" ## serializeJni(): failure - pickledPtr buffer OOM");
errorMessage = "pickledPtr buffer OOM";
}
else
{
size_t result = olm_pickle_inbound_group_session(sessionPtr,
(void const *)keyPtr,
keyLength,
(void*)pickledPtr,
pickledLength);
if (result == olm_error())
{
errorMessage = olm_inbound_group_session_last_error(sessionPtr);
LOGE(" ## serializeJni(): failure - olm_pickle_outbound_group_session() Msg=%s", errorMessage);
}
else
{
LOGD(" ## serializeJni(): success - result=%lu pickled=%.*s", static_cast<long unsigned int>(result), static_cast<int>(pickledLength), static_cast<char*>(pickledPtr));
pickledDataRet = env->NewByteArray(pickledLength);
env->SetByteArrayRegion(pickledDataRet, 0 , pickledLength, (jbyte*)pickledPtr);
}
free(pickledPtr);
}
}
// free alloc
if (keyPtr)
{
if (keyWasCopied) {
memset(keyPtr, 0, (size_t)env->GetArrayLength(aKeyBuffer));
}
env->ReleaseByteArrayElements(aKeyBuffer, keyPtr, JNI_ABORT);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return pickledDataRet;
}
/**
* Allocate a new session and initialize it with the serialisation data.<br>
* An exception is thrown if the operation fails.
* @param aSerializedData the session serialisation buffer
* @param aKey the key used to encrypt the serialized account data
* @return the deserialized session
**/
JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(deserializeJni)(JNIEnv *env, jobject thiz, jbyteArray aSerializedDataBuffer, jbyteArray aKeyBuffer)
{
const char* errorMessage = NULL;
OlmInboundGroupSession* sessionPtr = NULL;
size_t sessionSize = olm_inbound_group_session_size();
jbyte* keyPtr = NULL;
jboolean keyWasCopied = JNI_FALSE;
jbyte* pickledPtr = NULL;
LOGD("## deserializeJni(): IN");
if (!sessionSize)
{
LOGE(" ## deserializeJni(): failure - inbound group session size = 0");
errorMessage = "inbound group session size = 0";
}
else if (!(sessionPtr = (OlmInboundGroupSession*)malloc(sessionSize)))
{
LOGE(" ## deserializeJni(): failure - session failure OOM");
errorMessage = "session failure OOM";
}
else if (!aKeyBuffer)
{
LOGE(" ## deserializeJni(): failure - invalid key");
errorMessage = "invalid key";
}
else if (!aSerializedDataBuffer)
{
LOGE(" ## deserializeJni(): failure - serialized data");
errorMessage = "serialized data";
}
else if (!(keyPtr = env->GetByteArrayElements(aKeyBuffer, &keyWasCopied)))
{
LOGE(" ## deserializeJni(): failure - keyPtr JNI allocation OOM");
errorMessage = "keyPtr JNI allocation OOM";
}
else if (!(pickledPtr = env->GetByteArrayElements(aSerializedDataBuffer, 0)))
{
LOGE(" ## deserializeJni(): failure - pickledPtr JNI allocation OOM");
errorMessage = "pickledPtr JNI allocation OOM";
}
else
{
sessionPtr = olm_inbound_group_session(sessionPtr);
size_t pickledLength = (size_t)env->GetArrayLength(aSerializedDataBuffer);
size_t keyLength = (size_t)env->GetArrayLength(aKeyBuffer);
LOGD(" ## deserializeJni(): pickledLength=%lu keyLength=%lu",static_cast<long unsigned int>(pickledLength), static_cast<long unsigned int>(keyLength));
LOGD(" ## deserializeJni(): pickled=%.*s", static_cast<int>(pickledLength), (char const *)pickledPtr);
size_t result = olm_unpickle_inbound_group_session(sessionPtr,
(void const *)keyPtr,
keyLength,
(void*)pickledPtr,
pickledLength);
if (result == olm_error())
{
errorMessage = olm_inbound_group_session_last_error(sessionPtr);
LOGE(" ## deserializeJni(): failure - olm_unpickle_inbound_group_session() Msg=%s", errorMessage);
}
else
{
LOGD(" ## deserializeJni(): success - result=%lu ", static_cast<long unsigned int>(result));
}
}
// free alloc
if (keyPtr)
{
if (keyWasCopied) {
memset(keyPtr, 0, (size_t)env->GetArrayLength(aKeyBuffer));
}
env->ReleaseByteArrayElements(aKeyBuffer, keyPtr, JNI_ABORT);
}
if (pickledPtr)
{
env->ReleaseByteArrayElements(aSerializedDataBuffer, pickledPtr, JNI_ABORT);
}
if (errorMessage)
{
if (sessionPtr)
{
olm_clear_inbound_group_session(sessionPtr);
free(sessionPtr);
}
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return (jlong)(intptr_t)sessionPtr;
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016 Vector Creations 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 _OLMINBOUND_GROUP_SESSION_H
#define _OLMINBOUND_GROUP_SESSION_H
#include "olm_jni.h"
#include "olm/olm.h"
#include "olm/inbound_group_session.h"
#define OLM_INBOUND_GROUP_SESSION_FUNC_DEF(func_name) FUNC_DEF(OlmInboundGroupSession,func_name)
#ifdef __cplusplus
extern "C" {
#endif
// session creation/destruction
JNIEXPORT void OLM_INBOUND_GROUP_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(createNewSessionJni)(JNIEnv *env, jobject thiz, jbyteArray aSessionKeyBuffer, jboolean isImported);
JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(sessionIdentifierJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(decryptMessageJni)(JNIEnv *env, jobject thiz, jbyteArray aEncryptedMsg, jobject aDecryptIndex);
JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(firstKnownIndexJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jboolean OLM_INBOUND_GROUP_SESSION_FUNC_DEF(isVerifiedJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(exportJni)(JNIEnv *env, jobject thiz, jlong messageIndex);
// serialization
JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(serializeJni)(JNIEnv *env, jobject thiz, jbyteArray aKey);
JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(deserializeJni)(JNIEnv *env, jobject thiz, jbyteArray aSerializedData, jbyteArray aKey);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,83 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016,2018,2019 Vector Creations 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 _OLMJNI_H
#define _OLMJNI_H
#include <cstdlib>
#include <cstdio>
#include <string>
#include <string.h>
#include <sstream>
#include <jni.h>
#include <android/log.h>
#define TAG "OlmJniNative"
/* logging macros */
//#define ENABLE_JNI_LOG
#ifdef NDK_DEBUG
#warning NDK_DEBUG is defined!
#endif
#ifdef ENABLE_JNI_LOG
#warning ENABLE_JNI_LOG is defined!
#endif
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
#ifdef ENABLE_JNI_LOG
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
#else
#define LOGD(...)
#define LOGW(...)
#endif
#define FUNC_DEF(class_name,func_name) JNICALL Java_org_matrix_olm_##class_name##_##func_name
namespace AndroidOlmSdk
{
}
#ifdef __cplusplus
extern "C" {
#endif
// internal helper functions
bool setRandomInBuffer(JNIEnv *env, uint8_t **aBuffer2Ptr, size_t aRandomSize);
struct OlmSession* getSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
struct OlmAccount* getAccountInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
struct OlmInboundGroupSession* getInboundGroupSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
struct OlmOutboundGroupSession* getOutboundGroupSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
struct OlmUtility* getUtilityInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
struct OlmPkDecryption* getPkDecryptionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
struct OlmPkEncryption* getPkEncryptionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
struct OlmPkSigning* getPkSigningInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
struct OlmSAS* getOlmSasInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,234 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016,2018,2019 Vector Creations Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "olm_jni_helper.h"
#include "olm/olm.h"
#include <sys/time.h>
using namespace AndroidOlmSdk;
/**
* Init a buffer with a given number of random values.
* @param aBuffer2Ptr the buffer to be initialized
* @param aRandomSize the number of random values to apply
* @return true if operation succeed, false otherwise
**/
bool setRandomInBuffer(JNIEnv *env, uint8_t **aBuffer2Ptr, size_t aRandomSize)
{
bool retCode = false;
int bufferLen = aRandomSize*sizeof(uint8_t);
if (!aBuffer2Ptr)
{
LOGE("## setRandomInBuffer(): failure - aBuffer=NULL");
}
else if (!aRandomSize)
{
LOGE("## setRandomInBuffer(): failure - random size=0");
}
else if (!(*aBuffer2Ptr = (uint8_t*)malloc(bufferLen)))
{
LOGE("## setRandomInBuffer(): failure - alloc mem OOM");
}
else
{
LOGD("## setRandomInBuffer(): randomSize=%lu",static_cast<long unsigned int>(aRandomSize));
// use the secureRandom class
jclass cls = env->FindClass("java/security/SecureRandom");
if (cls)
{
jobject newObj = 0;
jmethodID constructor = env->GetMethodID(cls, "<init>", "()V");
jmethodID nextByteMethod = env->GetMethodID(cls, "nextBytes", "([B)V");
if (constructor)
{
newObj = env->NewObject(cls, constructor);
jbyteArray tempByteArray = env->NewByteArray(bufferLen);
if (newObj && tempByteArray)
{
env->CallVoidMethod(newObj, nextByteMethod, tempByteArray);
if (!env->ExceptionOccurred())
{
jbyte* buffer = env->GetByteArrayElements(tempByteArray, NULL);
if (buffer)
{
memcpy(*aBuffer2Ptr, buffer, bufferLen);
retCode = true;
// clear tempByteArray to hide sensitive data.
memset(buffer, 0, bufferLen);
env->SetByteArrayRegion(tempByteArray, 0, bufferLen, buffer);
// ensure that the buffer is released
env->ReleaseByteArrayElements(tempByteArray, buffer, JNI_ABORT);
}
}
}
if (tempByteArray)
{
env->DeleteLocalRef(tempByteArray);
}
if (newObj)
{
env->DeleteLocalRef(newObj);
}
}
}
// debug purpose
/*for(int i = 0; i < aRandomSize; i++)
{
LOGD("## setRandomInBuffer(): randomBuffPtr[%ld]=%d",i, (*aBuffer2Ptr)[i]);
}*/
}
return retCode;
}
/**
* Read the instance ID of the calling object.
* @param aJniEnv pointer pointing on the JNI function table
* @param aJavaObject reference to the object on which the method is invoked
* @param aCallingClass java calling class name
* @return the related instance ID
**/
jlong getInstanceId(JNIEnv* aJniEnv, jobject aJavaObject, const char *aCallingClass)
{
jlong instanceId = 0;
if (aJniEnv)
{
jclass requiredClass = aJniEnv->FindClass(aCallingClass);
jclass loaderClass = 0;
if (requiredClass && (JNI_TRUE != aJniEnv->IsInstanceOf(aJavaObject, requiredClass)))
{
LOGE("## getInstanceId() failure - invalid instance of");
}
else if ((loaderClass = aJniEnv->GetObjectClass(aJavaObject)))
{
jfieldID instanceIdField = aJniEnv->GetFieldID(loaderClass, "mNativeId", "J");
if (instanceIdField)
{
instanceId = aJniEnv->GetLongField(aJavaObject, instanceIdField);
LOGD("## getInstanceId(): read from java instanceId=%lld",instanceId);
}
else
{
LOGE("## getInstanceId() ERROR! GetFieldID=null");
}
aJniEnv->DeleteLocalRef(loaderClass);
}
else
{
LOGE("## getInstanceId() ERROR! GetObjectClass=null");
}
}
else
{
LOGE("## getInstanceId() ERROR! aJniEnv=NULL");
}
LOGD("## getInstanceId() success - instanceId=%p (jlong)(intptr_t)instanceId=%lld",(void*)instanceId, (jlong)(intptr_t)instanceId);
return instanceId;
}
/**
* Read the account instance ID of the calling object.
* @param aJniEnv pointer pointing on the JNI function table
* @param aJavaObject reference to the object on which the method is invoked
* @return the related OlmAccount.
**/
struct OlmAccount* getAccountInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
{
return (struct OlmAccount*)getInstanceId(aJniEnv, aJavaObject, CLASS_OLM_ACCOUNT);
}
/**
* Read the session instance ID of the calling object (aJavaObject).<br>
* @param aJniEnv pointer pointing on the JNI function table
* @param aJavaObject reference to the object on which the method is invoked
* @return the related OlmSession.
**/
struct OlmSession* getSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
{
return (struct OlmSession*)getInstanceId(aJniEnv, aJavaObject, CLASS_OLM_SESSION);
}
/**
* Read the inbound group session instance ID of the calling object (aJavaObject).<br>
* @param aJniEnv pointer pointing on the JNI function table
* @param aJavaObject reference to the object on which the method is invoked
* @return the related OlmInboundGroupSession.
**/
struct OlmInboundGroupSession* getInboundGroupSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
{
return (struct OlmInboundGroupSession*)getInstanceId(aJniEnv, aJavaObject, CLASS_OLM_INBOUND_GROUP_SESSION);
}
/**
* Read the outbound group session instance ID of the calling object (aJavaObject).<br>
* @param aJniEnv pointer pointing on the JNI function table
* @param aJavaObject reference to the object on which the method is invoked
* @return the related OlmOutboundGroupSession
**/
struct OlmOutboundGroupSession* getOutboundGroupSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
{
return (struct OlmOutboundGroupSession*)getInstanceId(aJniEnv, aJavaObject, CLASS_OLM_OUTBOUND_GROUP_SESSION);
}
/**
* Read the utility instance ID of the calling object (aJavaObject).<br>
* @param aJniEnv pointer pointing on the JNI function table
* @param aJavaObject reference to the object on which the method is invoked
* @return the related OlmUtility
**/
struct OlmUtility* getUtilityInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
{
return (struct OlmUtility*)getInstanceId(aJniEnv, aJavaObject, CLASS_OLM_UTILITY);
}
struct OlmPkDecryption* getPkDecryptionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
{
return (struct OlmPkDecryption*)getInstanceId(aJniEnv, aJavaObject, CLASS_OLM_PK_DECRYPTION);
}
struct OlmPkEncryption* getPkEncryptionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
{
return (struct OlmPkEncryption*)getInstanceId(aJniEnv, aJavaObject, CLASS_OLM_PK_ENCRYPTION);
}
struct OlmPkSigning* getPkSigningInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
{
return (struct OlmPkSigning*)getInstanceId(aJniEnv, aJavaObject, CLASS_OLM_PK_SIGNING);
}
struct OlmSAS* getOlmSasInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
{
return (struct OlmSAS*)getInstanceId(aJniEnv, aJavaObject, CLASS_OLM_SAS);
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016,2018,2019 Vector Creations Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "olm_jni.h"
// constant strings
namespace AndroidOlmSdk
{
static const char *CLASS_OLM_INBOUND_GROUP_SESSION = "org/matrix/olm/OlmInboundGroupSession";
static const char *CLASS_OLM_OUTBOUND_GROUP_SESSION = "org/matrix/olm/OlmOutboundGroupSession";
static const char *CLASS_OLM_SESSION = "org/matrix/olm/OlmSession";
static const char *CLASS_OLM_ACCOUNT = "org/matrix/olm/OlmAccount";
static const char *CLASS_OLM_UTILITY = "org/matrix/olm/OlmUtility";
static const char *CLASS_OLM_PK_ENCRYPTION = "org/matrix/olm/OlmPkEncryption";
static const char *CLASS_OLM_PK_DECRYPTION = "org/matrix/olm/OlmPkDecryption";
static const char *CLASS_OLM_PK_SIGNING = "org/matrix/olm/OlmPkSigning";
static const char *CLASS_OLM_SAS = "org/matrix/olm/OlmSAS";
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016 Vector Creations Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "olm_manager.h"
using namespace AndroidOlmSdk;
JNIEXPORT jstring OLM_MANAGER_FUNC_DEF(getOlmLibVersionJni)(JNIEnv* env, jobject thiz)
{
uint8_t majorVer=0, minorVer=0, patchVer=0;
jstring returnValueStr=0;
char buff[150];
olm_get_library_version(&majorVer, &minorVer, &patchVer);
LOGD("## getOlmLibVersionJni(): Major=%d Minor=%d Patch=%d", majorVer, minorVer, patchVer);
snprintf(buff, sizeof(buff), "%d.%d.%d", majorVer, minorVer, patchVer);
returnValueStr = env->NewStringUTF((const char*)buff);
return returnValueStr;
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016 Vector Creations 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 _OLMMANAGER_H
#define _OLMMANAGER_H
#include "olm_jni.h"
#include "olm/olm.h"
#define OLM_MANAGER_FUNC_DEF(func_name) FUNC_DEF(OlmManager,func_name)
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jstring OLM_MANAGER_FUNC_DEF(getOlmLibVersionJni)(JNIEnv *env, jobject thiz);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,563 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016 Vector Creations Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "olm_outbound_group_session.h"
using namespace AndroidOlmSdk;
/**
* Release the session allocation made by initializeOutboundGroupSessionMemory().<br>
* This method MUST be called when java counter part account instance is done.
*
*/
JNIEXPORT void OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env, jobject thiz)
{
LOGD("## releaseSessionJni(): OutBound group session IN");
OlmOutboundGroupSession* sessionPtr = (OlmOutboundGroupSession*)getOutboundGroupSessionInstanceId(env,thiz);
if (!sessionPtr)
{
LOGE(" ## releaseSessionJni(): failure - invalid outbound group session instance");
}
else
{
LOGD(" ## releaseSessionJni(): sessionPtr=%p",sessionPtr);
#ifdef ENABLE_JNI_LOG
size_t retCode = olm_clear_outbound_group_session(sessionPtr);
LOGD(" ## releaseSessionJni(): clear_outbound_group_session=%lu",static_cast<long unsigned int>(retCode));
#else
olm_clear_outbound_group_session(sessionPtr);
#endif
LOGD(" ## releaseSessionJni(): free IN");
free(sessionPtr);
LOGD(" ## releaseSessionJni(): free OUT");
}
}
/**
* Initialize a new outbound group session and return it to JAVA side.<br>
* Since a C prt is returned as a jlong, special care will be taken
* to make the cast (OlmOutboundGroupSession* => jlong) platform independent.
* @return the initialized OlmOutboundGroupSession* instance or throw an exception
**/
JNIEXPORT jlong OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(createNewSessionJni)(JNIEnv *env, jobject thiz)
{
const char* errorMessage = NULL;
OlmOutboundGroupSession* sessionPtr = NULL;
size_t sessionSize = 0;
LOGD("## createNewSessionJni(): outbound group session IN");
sessionSize = olm_outbound_group_session_size();
if (0 == sessionSize)
{
LOGE(" ## createNewSessionJni(): failure - outbound group session size = 0");
errorMessage = "outbound group session size = 0";
}
else if (!(sessionPtr = (OlmOutboundGroupSession*)malloc(sessionSize)))
{
LOGE(" ## createNewSessionJni(): failure - outbound group session OOM");
errorMessage = "outbound group session OOM";
}
else
{
sessionPtr = olm_outbound_group_session(sessionPtr);
LOGD(" ## createNewSessionJni(): success - outbound group session size=%lu",static_cast<long unsigned int>(sessionSize));
// compute random buffer
size_t randomLength = olm_init_outbound_group_session_random_length(sessionPtr);
uint8_t *randomBuffPtr = NULL;
LOGW(" ## createNewSessionJni(): randomLength=%lu",static_cast<long unsigned int>(randomLength));
if ((0 != randomLength) && !setRandomInBuffer(env, &randomBuffPtr, randomLength))
{
LOGE(" ## createNewSessionJni(): failure - random buffer init");
errorMessage = "random buffer init";
}
else
{
if (0 == randomLength)
{
LOGW(" ## createNewSessionJni(): random buffer is not required");
}
size_t sessionResult = olm_init_outbound_group_session(sessionPtr, randomBuffPtr, randomLength);
if (sessionResult == olm_error()) {
errorMessage = (const char *)olm_outbound_group_session_last_error(sessionPtr);
LOGE(" ## createNewSessionJni(): failure - init outbound session creation Msg=%s", errorMessage);
}
else
{
LOGD(" ## createNewSessionJni(): success - result=%lu", static_cast<long unsigned int>(sessionResult));
}
// clear the random buffer
memset(randomBuffPtr, 0, randomLength);
free(randomBuffPtr);
}
}
if (errorMessage)
{
if (sessionPtr)
{
olm_clear_outbound_group_session(sessionPtr);
free(sessionPtr);
}
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return (jlong)(intptr_t)sessionPtr;
}
/**
* Return the session identifier.
* An exception is thrown if the operation fails.
* @return the session identifier
*/
JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(sessionIdentifierJni)(JNIEnv *env, jobject thiz)
{
LOGD("## sessionIdentifierJni(): outbound group session IN");
const char* errorMessage = NULL;
OlmOutboundGroupSession *sessionPtr = (OlmOutboundGroupSession*)getOutboundGroupSessionInstanceId(env,thiz);
jbyteArray returnValue = 0;
if (!sessionPtr)
{
LOGE(" ## sessionIdentifierJni(): failure - invalid outbound group session instance");
errorMessage = "invalid outbound group session instance";
}
else
{
// get the size to alloc
size_t lengthSessionId = olm_outbound_group_session_id_length(sessionPtr);
LOGD(" ## sessionIdentifierJni(): outbound group session lengthSessionId=%lu",static_cast<long unsigned int>(lengthSessionId));
uint8_t *sessionIdPtr = (uint8_t*)malloc(lengthSessionId*sizeof(uint8_t));
if (!sessionIdPtr)
{
LOGE(" ## sessionIdentifierJni(): failure - outbound identifier allocation OOM");
errorMessage = "outbound identifier allocation OOM";
}
else
{
size_t result = olm_outbound_group_session_id(sessionPtr, sessionIdPtr, lengthSessionId);
if (result == olm_error())
{
errorMessage = reinterpret_cast<const char*>(olm_outbound_group_session_last_error(sessionPtr));
LOGE(" ## sessionIdentifierJni(): failure - outbound group session identifier failure Msg=%s", errorMessage);
}
else
{
returnValue = env->NewByteArray(result);
env->SetByteArrayRegion(returnValue, 0 , result, (jbyte*)sessionIdPtr);
LOGD(" ## sessionIdentifierJni(): success - outbound group session identifier result=%lu sessionId= %.*s",static_cast<long unsigned int>(result), static_cast<int>(result), reinterpret_cast<char*>(sessionIdPtr));
}
// free alloc
free(sessionIdPtr);
}
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return returnValue;
}
/**
* Get the current message index for this session.<br>
* Each message is sent with an increasing index, this
* method returns the index for the next message.
* An exception is thrown if the operation fails.
* @return current session index
*/
JNIEXPORT jint OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(messageIndexJni)(JNIEnv *env, jobject thiz)
{
OlmOutboundGroupSession *sessionPtr = NULL;
jint indexRetValue = 0;
LOGD("## messageIndexJni(): IN");
if (!(sessionPtr = (OlmOutboundGroupSession*)getOutboundGroupSessionInstanceId(env,thiz)))
{
LOGE(" ## messageIndexJni(): failure - invalid outbound group session instance");
}
else
{
indexRetValue = static_cast<jint>(olm_outbound_group_session_message_index(sessionPtr));
}
LOGD(" ## messageIndexJni(): success - index=%d",indexRetValue);
return indexRetValue;
}
/**
* Return the session key.
* An exception is thrown if the operation fails.
* @return the session key
*/
JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(sessionKeyJni)(JNIEnv *env, jobject thiz)
{
LOGD("## sessionKeyJni(): outbound group session IN");
const char* errorMessage = NULL;
OlmOutboundGroupSession *sessionPtr = (OlmOutboundGroupSession*)getOutboundGroupSessionInstanceId(env,thiz);
jbyteArray returnValue = 0;
if (!sessionPtr)
{
LOGE(" ## sessionKeyJni(): failure - invalid outbound group session instance");
errorMessage = "invalid outbound group session instance";
}
else
{
// get the size to alloc
size_t sessionKeyLength = olm_outbound_group_session_key_length(sessionPtr);
LOGD(" ## sessionKeyJni(): sessionKeyLength=%lu",static_cast<long unsigned int>(sessionKeyLength));
uint8_t *sessionKeyPtr = (uint8_t*)malloc(sessionKeyLength*sizeof(uint8_t));
if (!sessionKeyPtr)
{
LOGE(" ## sessionKeyJni(): failure - session key allocation OOM");
errorMessage = "session key allocation OOM";
}
else
{
size_t result = olm_outbound_group_session_key(sessionPtr, sessionKeyPtr, sessionKeyLength);
if (result == olm_error())
{
errorMessage = (const char *)olm_outbound_group_session_last_error(sessionPtr);
LOGE(" ## sessionKeyJni(): failure - session key failure Msg=%s", errorMessage);
}
else
{
LOGD(" ## sessionKeyJni(): success - outbound group session key result=%lu sessionKey=%.*s",static_cast<long unsigned int>(result), static_cast<int>(result), reinterpret_cast<char*>(sessionKeyPtr));
returnValue = env->NewByteArray(result);
env->SetByteArrayRegion(returnValue, 0 , result, (jbyte*)sessionKeyPtr);
}
// free alloc
free(sessionKeyPtr);
}
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return returnValue;
}
/**
* Encrypt a bytes buffer messages.
* An exception is thrown if the operation fails.
* @param aClearMsgBuffer the message to encode
* @return the encoded message
*/
JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobject thiz, jbyteArray aClearMsgBuffer)
{
LOGD("## encryptMessageJni(): IN");
const char* errorMessage = NULL;
jbyteArray encryptedMsgRet = 0;
OlmOutboundGroupSession *sessionPtr = NULL;
jbyte* clearMsgPtr = NULL;
jboolean clearMsgIsCopied = JNI_FALSE;
if (!(sessionPtr = (OlmOutboundGroupSession*)getOutboundGroupSessionInstanceId(env,thiz)))
{
LOGE(" ## encryptMessageJni(): failure - invalid outbound group session ptr=NULL");
errorMessage = "invalid outbound group session ptr=NULL";
}
else if (!aClearMsgBuffer)
{
LOGE(" ## encryptMessageJni(): failure - invalid clear message");
errorMessage = "invalid clear message";
}
else if (!(clearMsgPtr = env->GetByteArrayElements(aClearMsgBuffer, &clearMsgIsCopied)))
{
LOGE(" ## encryptMessageJni(): failure - clear message JNI allocation OOM");
errorMessage = "clear message JNI allocation OOM";
}
else
{
// get clear message length
size_t clearMsgLength = (size_t)env->GetArrayLength(aClearMsgBuffer);
LOGD(" ## encryptMessageJni(): clearMsgLength=%lu",static_cast<long unsigned int>(clearMsgLength));
// compute max encrypted length
size_t encryptedMsgLength = olm_group_encrypt_message_length(sessionPtr,clearMsgLength);
uint8_t *encryptedMsgPtr = (uint8_t*)malloc(encryptedMsgLength*sizeof(uint8_t));
if (!encryptedMsgPtr)
{
LOGE(" ## encryptMessageJni(): failure - encryptedMsgPtr buffer OOM");
errorMessage = "encryptedMsgPtr buffer OOM";
}
else
{
LOGD(" ## encryptMessageJni(): estimated encryptedMsgLength=%lu",static_cast<long unsigned int>(encryptedMsgLength));
size_t encryptedLength = olm_group_encrypt(sessionPtr,
(uint8_t*)clearMsgPtr,
clearMsgLength,
encryptedMsgPtr,
encryptedMsgLength);
if (encryptedLength == olm_error())
{
errorMessage = olm_outbound_group_session_last_error(sessionPtr);
LOGE(" ## encryptMessageJni(): failure - olm_group_decrypt_max_plaintext_length Msg=%s", errorMessage);
}
else
{
LOGD(" ## encryptMessageJni(): encrypted returnedLg=%lu plainTextMsgPtr=%.*s",static_cast<long unsigned int>(encryptedLength), static_cast<int>(encryptedLength), reinterpret_cast<char*>(encryptedMsgPtr));
encryptedMsgRet = env->NewByteArray(encryptedLength);
env->SetByteArrayRegion(encryptedMsgRet, 0 , encryptedLength, (jbyte*)encryptedMsgPtr);
}
free(encryptedMsgPtr);
}
}
// free alloc
if (clearMsgPtr)
{
if (clearMsgIsCopied)
{
memset(clearMsgPtr, 0, (size_t)env->GetArrayLength(aClearMsgBuffer));
}
env->ReleaseByteArrayElements(aClearMsgBuffer, clearMsgPtr, JNI_ABORT);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return encryptedMsgRet;
}
/**
* Serialize and encrypt session instance into a base64 string.<br>
* An exception is thrown if the operation fails.
* @param aKey key used to encrypt the serialized session data
* @return a base64 string if operation succeed, null otherwise
**/
JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(serializeJni)(JNIEnv *env, jobject thiz, jbyteArray aKeyBuffer)
{
const char* errorMessage = NULL;
jbyteArray returnValue = 0;
jbyte* keyPtr = NULL;
jboolean keyWasCopied = JNI_FALSE;
OlmOutboundGroupSession* sessionPtr = NULL;
LOGD("## outbound group session serializeJni(): IN");
if (!(sessionPtr = (OlmOutboundGroupSession*)getOutboundGroupSessionInstanceId(env,thiz)))
{
LOGE(" ## serializeJni(): failure - invalid session ptr");
errorMessage = "invalid session ptr";
}
else if (!aKeyBuffer)
{
LOGE(" ## serializeJni(): failure - invalid key");
errorMessage = "invalid key";
}
else if (!(keyPtr = env->GetByteArrayElements(aKeyBuffer, &keyWasCopied)))
{
LOGE(" ## serializeJni(): failure - keyPtr JNI allocation OOM");
errorMessage = "keyPtr JNI allocation OOM";
}
else
{
size_t pickledLength = olm_pickle_outbound_group_session_length(sessionPtr);
size_t keyLength = (size_t)env->GetArrayLength(aKeyBuffer);
LOGD(" ## serializeJni(): pickledLength=%lu keyLength=%lu",static_cast<long unsigned int>(pickledLength), static_cast<long unsigned int>(keyLength));
void *pickledPtr = malloc(pickledLength*sizeof(uint8_t));
if(!pickledPtr)
{
LOGE(" ## serializeJni(): failure - pickledPtr buffer OOM");
errorMessage = "pickledPtr buffer OOM";
}
else
{
size_t result = olm_pickle_outbound_group_session(sessionPtr,
(void const *)keyPtr,
keyLength,
(void*)pickledPtr,
pickledLength);
if (result == olm_error())
{
errorMessage = olm_outbound_group_session_last_error(sessionPtr);
LOGE(" ## serializeJni(): failure - olm_pickle_outbound_group_session() Msg=%s", errorMessage);
}
else
{
LOGD(" ## serializeJni(): success - result=%lu pickled=%.*s", static_cast<long unsigned int>(result), static_cast<int>(result), static_cast<char*>(pickledPtr));
returnValue = env->NewByteArray(pickledLength);
env->SetByteArrayRegion(returnValue, 0 , pickledLength, (jbyte*)pickledPtr);
}
}
free(pickledPtr);
}
// free alloc
if (keyPtr)
{
if (keyWasCopied) {
memset(keyPtr, 0, (size_t)env->GetArrayLength(aKeyBuffer));
}
env->ReleaseByteArrayElements(aKeyBuffer, keyPtr, JNI_ABORT);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return returnValue;
}
/**
* Allocate a new session and initialize it with the serialisation data.<br>
* An exception is thrown if the operation fails.
* @param aSerializedData the session serialisation buffer
* @param aKey the key used to encrypt the serialized account data
* @return the deserialized session
**/
JNIEXPORT jlong OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(deserializeJni)(JNIEnv *env, jobject thiz, jbyteArray aSerializedDataBuffer, jbyteArray aKeyBuffer)
{
const char* errorMessage = NULL;
size_t sessionSize = olm_outbound_group_session_size();
OlmOutboundGroupSession* sessionPtr = NULL;
jbyte* keyPtr = NULL;
jboolean keyWasCopied = JNI_FALSE;
jbyte* pickledPtr = NULL;
LOGD("## deserializeJni(): IN");
if (!sessionSize)
{
LOGE(" ## deserializeJni(): failure - outbound group session size = 0");
errorMessage = "outbound group session size = 0";
}
else if (!(sessionPtr = (OlmOutboundGroupSession*)malloc(sessionSize)))
{
LOGE(" ## deserializeJni(): failure - session failure OOM");
errorMessage = "session failure OOM";
}
else if (!aKeyBuffer)
{
LOGE(" ## deserializeJni(): failure - invalid key");
errorMessage = "invalid key";
}
else if (!aSerializedDataBuffer)
{
LOGE(" ## deserializeJni(): failure - serialized data");
errorMessage = "invalid serialized data";
}
else if (!(keyPtr = env->GetByteArrayElements(aKeyBuffer, &keyWasCopied)))
{
LOGE(" ## deserializeJni(): failure - keyPtr JNI allocation OOM");
errorMessage = "keyPtr JNI allocation OOM";
}
else if (!(pickledPtr = env->GetByteArrayElements(aSerializedDataBuffer, 0)))
{
LOGE(" ## deserializeJni(): failure - pickledPtr JNI allocation OOM");
errorMessage = "pickledPtr JNI allocation OOM";
}
else
{
sessionPtr = olm_outbound_group_session(sessionPtr);
size_t pickledLength = (size_t)env->GetArrayLength(aSerializedDataBuffer);
size_t keyLength = (size_t)env->GetArrayLength(aKeyBuffer);
LOGD(" ## deserializeJni(): pickledLength=%lu keyLength=%lu",static_cast<long unsigned int>(pickledLength), static_cast<long unsigned int>(keyLength));
LOGD(" ## deserializeJni(): pickled=%.*s", static_cast<int>(pickledLength), (char const *)pickledPtr);
size_t result = olm_unpickle_outbound_group_session(sessionPtr,
(void const *)keyPtr,
keyLength,
(void*)pickledPtr,
pickledLength);
if (result == olm_error())
{
errorMessage = olm_outbound_group_session_last_error(sessionPtr);
LOGE(" ## deserializeJni(): failure - olm_unpickle_outbound_group_session() Msg=%s", errorMessage);
}
else
{
LOGD(" ## deserializeJni(): success - result=%lu ", static_cast<long unsigned int>(result));
}
}
// free alloc
if (keyPtr)
{
if (keyWasCopied) {
memset(keyPtr, 0, (size_t)env->GetArrayLength(aKeyBuffer));
}
env->ReleaseByteArrayElements(aKeyBuffer, keyPtr, JNI_ABORT);
}
if (pickledPtr)
{
env->ReleaseByteArrayElements(aSerializedDataBuffer, pickledPtr, JNI_ABORT);
}
if (errorMessage)
{
if (sessionPtr)
{
olm_clear_outbound_group_session(sessionPtr);
free(sessionPtr);
}
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return (jlong)(intptr_t)sessionPtr;
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016 Vector Creations 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 _OLMOUTBOUND_GROUP_SESSION_H
#define _OLMOUTBOUND_GROUP_SESSION_H
#include "olm_jni.h"
#include "olm/olm.h"
#include "olm/outbound_group_session.h"
#define OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(func_name) FUNC_DEF(OlmOutboundGroupSession,func_name)
#ifdef __cplusplus
extern "C" {
#endif
// session creation/destruction
JNIEXPORT void OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jlong OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(createNewSessionJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(sessionIdentifierJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jint OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(messageIndexJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(sessionKeyJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobject thiz, jbyteArray aClearMsgBuffer);
// serialization
JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(serializeJni)(JNIEnv *env, jobject thiz, jbyteArray aKey);
JNIEXPORT jlong OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(deserializeJni)(JNIEnv *env, jobject thiz, jbyteArray aSerializedData, jbyteArray aKey);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,992 @@
/*
* Copyright 2018,2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "olm_pk.h"
#include "olm/olm.h"
using namespace AndroidOlmSdk;
OlmPkEncryption * initializePkEncryptionMemory()
{
size_t encryptionSize = olm_pk_encryption_size();
OlmPkEncryption *encryptionPtr = (OlmPkEncryption *)malloc(encryptionSize);
if (encryptionPtr)
{
// init encryption object
encryptionPtr = olm_pk_encryption(encryptionPtr);
LOGD(
"## initializePkEncryptionMemory(): success - OLM encryption size=%lu",
static_cast<long unsigned int>(encryptionSize)
);
}
else
{
LOGE("## initializePkEncryptionMemory(): failure - OOM");
}
return encryptionPtr;
}
JNIEXPORT jlong OLM_PK_ENCRYPTION_FUNC_DEF(createNewPkEncryptionJni)(JNIEnv *env, jobject thiz)
{
const char* errorMessage = NULL;
OlmPkEncryption *encryptionPtr = initializePkEncryptionMemory();
// init encryption memory allocation
if (!encryptionPtr)
{
LOGE("## createNewPkEncryptionJni(): failure - init encryption OOM");
errorMessage = "init encryption OOM";
}
else
{
LOGD("## createNewPkEncryptionJni(): success - OLM encryption created");
LOGD(
"## createNewPkEncryptionJni(): encryptionPtr=%p (jlong)(intptr_t)encryptionPtr=%lld",
encryptionPtr, (jlong)(intptr_t)encryptionPtr
);
}
if (errorMessage)
{
// release the allocated data
if (encryptionPtr)
{
olm_clear_pk_encryption(encryptionPtr);
free(encryptionPtr);
}
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return (jlong)(intptr_t)encryptionPtr;
}
JNIEXPORT void OLM_PK_ENCRYPTION_FUNC_DEF(releasePkEncryptionJni)(JNIEnv *env, jobject thiz)
{
LOGD("## releasePkEncryptionJni(): IN");
OlmPkEncryption* encryptionPtr = getPkEncryptionInstanceId(env, thiz);
if (!encryptionPtr)
{
LOGE(" ## releasePkEncryptionJni(): failure - invalid Encryption ptr=NULL");
}
else
{
LOGD(" ## releasePkEncryptionJni(): encryptionPtr=%p", encryptionPtr);
olm_clear_pk_encryption(encryptionPtr);
LOGD(" ## releasePkEncryptionJni(): IN");
// even if free(NULL) does not crash, logs are performed for debug
// purpose
free(encryptionPtr);
LOGD(" ## releasePkEncryptionJni(): OUT");
}
}
JNIEXPORT void OLM_PK_ENCRYPTION_FUNC_DEF(setRecipientKeyJni)(
JNIEnv *env, jobject thiz, jbyteArray aKeyBuffer
) {
const char *errorMessage = NULL;
jbyte *keyPtr = NULL;
OlmPkEncryption *encryptionPtr = getPkEncryptionInstanceId(env, thiz);
if (!encryptionPtr)
{
LOGE(" ## pkSetRecipientKeyJni(): failure - invalid Encryption ptr=NULL");
}
else if (!aKeyBuffer)
{
LOGE(" ## pkSetRecipientKeyJni(): failure - invalid key");
errorMessage = "invalid key";
}
else if (!(keyPtr = env->GetByteArrayElements(aKeyBuffer, 0)))
{
LOGE(" ## pkSetRecipientKeyJni(): failure - key JNI allocation OOM");
errorMessage = "key JNI allocation OOM";
}
else
{
if (olm_pk_encryption_set_recipient_key(encryptionPtr, keyPtr, (size_t)env->GetArrayLength(aKeyBuffer)) == olm_error())
{
errorMessage = olm_pk_encryption_last_error(encryptionPtr);
LOGE(
" ## pkSetRecipientKeyJni(): failure - olm_pk_encryption_set_recipient_key Msg=%s",
errorMessage
);
}
}
if (keyPtr)
{
env->ReleaseByteArrayElements(aKeyBuffer, keyPtr, JNI_ABORT);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
}
JNIEXPORT jbyteArray OLM_PK_ENCRYPTION_FUNC_DEF(encryptJni)(
JNIEnv *env, jobject thiz, jbyteArray aPlaintextBuffer, jobject aEncryptedMsg
) {
jbyteArray encryptedMsgRet = 0;
const char* errorMessage = NULL;
jbyte *plaintextPtr = NULL;
jboolean plaintextIsCopied = JNI_FALSE;
OlmPkEncryption *encryptionPtr = getPkEncryptionInstanceId(env, thiz);
jclass encryptedMsgJClass = 0;
jfieldID macFieldId;
jfieldID ephemeralFieldId;
if (!encryptionPtr)
{
LOGE(" ## pkEncryptJni(): failure - invalid Encryption ptr=NULL");
}
else if (!aPlaintextBuffer)
{
LOGE(" ## pkEncryptJni(): failure - invalid clear message");
errorMessage = "invalid clear message";
}
else if (!(plaintextPtr = env->GetByteArrayElements(aPlaintextBuffer, &plaintextIsCopied)))
{
LOGE(" ## pkEncryptJni(): failure - plaintext JNI allocation OOM");
errorMessage = "plaintext JNI allocation OOM";
}
else if (!(encryptedMsgJClass = env->GetObjectClass(aEncryptedMsg)))
{
LOGE(" ## pkEncryptJni(): failure - unable to get encrypted message class");
errorMessage = "unable to get encrypted message class";
}
else if (!(macFieldId = env->GetFieldID(encryptedMsgJClass, "mMac", "Ljava/lang/String;")))
{
LOGE("## pkEncryptJni(): failure - unable to get MAC field");
errorMessage = "unable to get MAC field";
}
else if (!(ephemeralFieldId = env->GetFieldID(encryptedMsgJClass, "mEphemeralKey", "Ljava/lang/String;")))
{
LOGE("## pkEncryptJni(): failure - unable to get ephemeral key field");
errorMessage = "unable to get ephemeral key field";
}
else
{
size_t plaintextLength = (size_t)env->GetArrayLength(aPlaintextBuffer);
size_t ciphertextLength = olm_pk_ciphertext_length(encryptionPtr, plaintextLength);
size_t macLength = olm_pk_mac_length(encryptionPtr);
size_t ephemeralLength = olm_pk_key_length();
uint8_t *ciphertextPtr = NULL, *macPtr = NULL, *ephemeralPtr = NULL;
size_t randomLength = olm_pk_encrypt_random_length(encryptionPtr);
uint8_t *randomBuffPtr = NULL;
LOGD("## pkEncryptJni(): randomLength=%lu",static_cast<long unsigned int>(randomLength));
if (!(ciphertextPtr = (uint8_t*)malloc(ciphertextLength)))
{
LOGE("## pkEncryptJni(): failure - ciphertext JNI allocation OOM");
errorMessage = "ciphertext JNI allocation OOM";
}
else if (!(macPtr = (uint8_t*)malloc(macLength + 1)))
{
LOGE("## pkEncryptJni(): failure - MAC JNI allocation OOM");
errorMessage = "MAC JNI allocation OOM";
}
else if (!(ephemeralPtr = (uint8_t*)malloc(ephemeralLength + 1)))
{
LOGE("## pkEncryptJni(): failure: ephemeral key JNI allocation OOM");
errorMessage = "ephemeral JNI allocation OOM";
}
else if (!setRandomInBuffer(env, &randomBuffPtr, randomLength))
{
LOGE("## pkEncryptJni(): failure - random buffer init");
errorMessage = "random buffer init";
}
else
{
macPtr[macLength] = '\0';
ephemeralPtr[ephemeralLength] = '\0';
size_t returnValue = olm_pk_encrypt(
encryptionPtr,
plaintextPtr, plaintextLength,
ciphertextPtr, ciphertextLength,
macPtr, macLength,
ephemeralPtr, ephemeralLength,
randomBuffPtr, randomLength
);
if (returnValue == olm_error())
{
errorMessage = olm_pk_encryption_last_error(encryptionPtr);
LOGE("## pkEncryptJni(): failure - olm_pk_encrypt Msg=%s", errorMessage);
}
else
{
encryptedMsgRet = env->NewByteArray(ciphertextLength);
env->SetByteArrayRegion(
encryptedMsgRet, 0, ciphertextLength, (jbyte*)ciphertextPtr
);
jstring macStr = env->NewStringUTF((char*)macPtr);
env->SetObjectField(aEncryptedMsg, macFieldId, macStr);
jstring ephemeralStr = env->NewStringUTF((char*)ephemeralPtr);
env->SetObjectField(aEncryptedMsg, ephemeralFieldId, ephemeralStr);
}
}
if (randomBuffPtr)
{
memset(randomBuffPtr, 0, randomLength);
free(randomBuffPtr);
}
if (ephemeralPtr)
{
free(ephemeralPtr);
}
if (macPtr)
{
free(macPtr);
}
if (ciphertextPtr)
{
free(ciphertextPtr);
}
}
if (plaintextPtr)
{
if (plaintextIsCopied)
{
memset(plaintextPtr, 0, (size_t)env->GetArrayLength(aPlaintextBuffer));
}
env->ReleaseByteArrayElements(aPlaintextBuffer, plaintextPtr, JNI_ABORT);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return encryptedMsgRet;
}
OlmPkDecryption * initializePkDecryptionMemory()
{
size_t decryptionSize = olm_pk_decryption_size();
OlmPkDecryption *decryptionPtr = (OlmPkDecryption *)malloc(decryptionSize);
if (decryptionPtr)
{
// init decryption object
decryptionPtr = olm_pk_decryption(decryptionPtr);
LOGD(
"## initializePkDecryptionMemory(): success - OLM decryption size=%lu",
static_cast<long unsigned int>(decryptionSize)
);
}
else
{
LOGE("## initializePkDecryptionMemory(): failure - OOM");
}
return decryptionPtr;
}
JNIEXPORT jlong OLM_PK_DECRYPTION_FUNC_DEF(createNewPkDecryptionJni)(JNIEnv *env, jobject thiz)
{
const char* errorMessage = NULL;
OlmPkDecryption *decryptionPtr = initializePkDecryptionMemory();
// init encryption memory allocation
if (!decryptionPtr)
{
LOGE("## createNewPkDecryptionJni(): failure - init decryption OOM");
errorMessage = "init decryption OOM";
}
else
{
LOGD("## createNewPkDecryptionJni(): success - OLM decryption created");
LOGD(
"## createNewPkDecryptionJni(): decryptionPtr=%p (jlong)(intptr_t)decryptionPtr=%lld",
decryptionPtr, (jlong)(intptr_t)decryptionPtr
);
}
if (errorMessage)
{
// release the allocated data
if (decryptionPtr)
{
olm_clear_pk_decryption(decryptionPtr);
free(decryptionPtr);
}
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return (jlong)(intptr_t)decryptionPtr;
}
JNIEXPORT void OLM_PK_DECRYPTION_FUNC_DEF(releasePkDecryptionJni)(JNIEnv *env, jobject thiz)
{
LOGD("## releasePkDecryptionJni(): IN");
OlmPkDecryption* decryptionPtr = getPkDecryptionInstanceId(env, thiz);
if (!decryptionPtr)
{
LOGE(" ## releasePkDecryptionJni(): failure - invalid Decryption ptr=NULL");
}
else
{
LOGD(" ## releasePkDecryptionJni(): decryptionPtr=%p", encryptionPtr);
olm_clear_pk_decryption(decryptionPtr);
LOGD(" ## releasePkDecryptionJni(): IN");
// even if free(NULL) does not crash, logs are performed for debug
// purpose
free(decryptionPtr);
LOGD(" ## releasePkDecryptionJni(): OUT");
}
}
JNIEXPORT jint OLM_PK_DECRYPTION_FUNC_DEF(privateKeyLength)(JNIEnv *env, jobject thiz)
{
return (jint) olm_pk_private_key_length();
}
JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(setPrivateKeyJni)(JNIEnv *env, jobject thiz, jbyteArray key)
{
jbyteArray publicKeyRet = 0;
jbyte *keyPtr = NULL;
jboolean keyWasCopied = JNI_FALSE;
const char* errorMessage = NULL;
OlmPkDecryption* decryptionPtr = getPkDecryptionInstanceId(env, thiz);
if (!decryptionPtr)
{
LOGE(" ## pkSetPrivateKeyJni(): failure - invalid Decryption ptr=NULL");
}
else if (!key)
{
LOGE(" ## pkSetPrivateKeyJni(): failure - invalid key");
errorMessage = "invalid key";
}
else if (!(keyPtr = env->GetByteArrayElements(key, &keyWasCopied)))
{
LOGE(" ## pkSetPrivateKeyJni(): failure - key JNI allocation OOM");
errorMessage = "key JNI allocation OOM";
}
else
{
size_t publicKeyLength = olm_pk_key_length();
uint8_t *publicKeyPtr = NULL;
size_t keyLength = (size_t)env->GetArrayLength(key);
if (!(publicKeyPtr = (uint8_t*)malloc(publicKeyLength)))
{
LOGE("## pkSetPrivateKeyJni(): failure - public key JNI allocation OOM");
errorMessage = "public key JNI allocation OOM";
}
else
{
size_t returnValue = olm_pk_key_from_private(
decryptionPtr,
publicKeyPtr, publicKeyLength,
keyPtr, keyLength
);
if (returnValue == olm_error())
{
errorMessage = olm_pk_decryption_last_error(decryptionPtr);
LOGE(" ## pkSetPrivateKeyJni(): failure - olm_pk_key_from_private Msg=%s", errorMessage);
}
else
{
publicKeyRet = env->NewByteArray(publicKeyLength);
env->SetByteArrayRegion(
publicKeyRet, 0, publicKeyLength, (jbyte*)publicKeyPtr
);
}
}
}
if (keyPtr)
{
if (keyWasCopied)
{
memset(keyPtr, 0, (size_t)env->GetArrayLength(key));
}
env->ReleaseByteArrayElements(key, keyPtr, JNI_ABORT);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return publicKeyRet;
}
JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(generateKeyJni)(JNIEnv *env, jobject thiz)
{
size_t randomLength = olm_pk_private_key_length();
uint8_t *randomBuffPtr = NULL;
jbyteArray publicKeyRet = 0;
uint8_t *publicKeyPtr = NULL;
size_t publicKeyLength = olm_pk_key_length();
const char* errorMessage = NULL;
OlmPkDecryption *decryptionPtr = getPkDecryptionInstanceId(env, thiz);
if (!decryptionPtr)
{
LOGE(" ## pkGenerateKeyJni(): failure - invalid Decryption ptr=NULL");
errorMessage = "invalid Decryption ptr=NULL";
}
else if (!setRandomInBuffer(env, &randomBuffPtr, randomLength))
{
LOGE("## pkGenerateKeyJni(): failure - random buffer init");
errorMessage = "random buffer init";
}
else if (!(publicKeyPtr = static_cast<uint8_t*>(malloc(publicKeyLength))))
{
LOGE("## pkGenerateKeyJni(): failure - public key allocation OOM");
errorMessage = "public key allocation OOM";
}
else
{
if (olm_pk_key_from_private(decryptionPtr, publicKeyPtr, publicKeyLength, randomBuffPtr, randomLength) == olm_error())
{
errorMessage = olm_pk_decryption_last_error(decryptionPtr);
LOGE("## pkGenerateKeyJni(): failure - olm_pk_generate_key Msg=%s", errorMessage);
}
else
{
publicKeyRet = env->NewByteArray(publicKeyLength);
env->SetByteArrayRegion(publicKeyRet, 0, publicKeyLength, (jbyte*)publicKeyPtr);
LOGD("## pkGenerateKeyJni(): public key generated");
}
}
if (randomBuffPtr)
{
memset(randomBuffPtr, 0, randomLength);
free(randomBuffPtr);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return publicKeyRet;
}
JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(privateKeyJni)(JNIEnv *env, jobject thiz)
{
jbyteArray privateKeyRet = 0;
const char* errorMessage = NULL;
OlmPkDecryption* decryptionPtr = getPkDecryptionInstanceId(env, thiz);
if (!decryptionPtr)
{
LOGE(" ## pkPrivateKeyJni(): failure - invalid Decryption ptr=NULL");
}
else
{
size_t privateKeyLength = olm_pk_private_key_length();
uint8_t *privateKeyPtr = NULL;
if (!(privateKeyPtr = (uint8_t*)malloc(privateKeyLength)))
{
LOGE("## pkPrivateKeyJni(): failure - private key JNI allocation OOM");
errorMessage = "private key JNI allocation OOM";
}
else
{
size_t returnValue = olm_pk_get_private_key(
decryptionPtr,
privateKeyPtr, privateKeyLength
);
if (returnValue == olm_error())
{
errorMessage = olm_pk_decryption_last_error(decryptionPtr);
LOGE(" ## pkPrivateKeyJni(): failure - olm_pk_get_private_key Msg=%s", errorMessage);
}
else
{
privateKeyRet = env->NewByteArray(privateKeyLength);
env->SetByteArrayRegion(
privateKeyRet, 0, privateKeyLength, (jbyte*)privateKeyPtr
);
memset(privateKeyPtr, 0, privateKeyLength);
}
}
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return privateKeyRet;
}
JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(decryptJni)(
JNIEnv *env, jobject thiz, jobject aEncryptedMsg
) {
const char* errorMessage = NULL;
OlmPkDecryption *decryptionPtr = getPkDecryptionInstanceId(env, thiz);
jclass encryptedMsgJClass = 0;
jstring ciphertextJstring = 0;
jstring macJstring = 0;
jstring ephemeralKeyJstring = 0;
jfieldID ciphertextFieldId;
jfieldID macFieldId;
jfieldID ephemeralKeyFieldId;
const char *ciphertextPtr = NULL;
const char *macPtr = NULL;
const char *ephemeralKeyPtr = NULL;
jbyteArray decryptedMsgRet = 0;
if (!decryptionPtr)
{
LOGE(" ## pkDecryptJni(): failure - invalid Decryption ptr=NULL");
errorMessage = "invalid Decryption ptr=NULL";
}
else if (!aEncryptedMsg)
{
LOGE(" ## pkDecryptJni(): failure - invalid encrypted message");
errorMessage = "invalid encrypted message";
}
else if (!(encryptedMsgJClass = env->GetObjectClass(aEncryptedMsg)))
{
LOGE("## pkDecryptJni(): failure - unable to get encrypted message class");
errorMessage = "unable to get encrypted message class";
}
else if (!(ciphertextFieldId = env->GetFieldID(encryptedMsgJClass,"mCipherText","Ljava/lang/String;")))
{
LOGE("## pkDecryptJni(): failure - unable to get message field");
errorMessage = "unable to get message field";
}
else if (!(ciphertextJstring = (jstring)env->GetObjectField(aEncryptedMsg, ciphertextFieldId)))
{
LOGE("## pkDecryptJni(): failure - no ciphertext");
errorMessage = "no ciphertext";
}
else if (!(ciphertextPtr = env->GetStringUTFChars(ciphertextJstring, 0)))
{
LOGE("## pkDecryptJni(): failure - ciphertext JNI allocation OOM");
errorMessage = "ciphertext JNI allocation OOM";
}
else if (!(ciphertextJstring = (jstring)env->GetObjectField(aEncryptedMsg, ciphertextFieldId)))
{
LOGE("## pkDecryptJni(): failure - no ciphertext");
errorMessage = "no ciphertext";
}
else if (!(ciphertextPtr = env->GetStringUTFChars(ciphertextJstring, 0)))
{
LOGE("## decryptMessageJni(): failure - ciphertext JNI allocation OOM");
errorMessage = "ciphertext JNI allocation OOM";
}
else if (!(macFieldId = env->GetFieldID(encryptedMsgJClass,"mMac","Ljava/lang/String;")))
{
LOGE("## pkDecryptJni(): failure - unable to get MAC field");
errorMessage = "unable to get MAC field";
}
else if (!(macJstring = (jstring)env->GetObjectField(aEncryptedMsg, macFieldId)))
{
LOGE("## pkDecryptJni(): failure - no MAC");
errorMessage = "no MAC";
}
else if (!(macPtr = env->GetStringUTFChars(macJstring, 0)))
{
LOGE("## pkDecryptJni(): failure - MAC JNI allocation OOM");
errorMessage = "ciphertext JNI allocation OOM";
}
else if (!(ephemeralKeyFieldId = env->GetFieldID(encryptedMsgJClass,"mEphemeralKey","Ljava/lang/String;")))
{
LOGE("## pkDecryptJni(): failure - unable to get ephemeral key field");
errorMessage = "unable to get ephemeral key field";
}
else if (!(ephemeralKeyJstring = (jstring)env->GetObjectField(aEncryptedMsg, ephemeralKeyFieldId)))
{
LOGE("## pkDecryptJni(): failure - no ephemeral key");
errorMessage = "no ephemeral key";
}
else if (!(ephemeralKeyPtr = env->GetStringUTFChars(ephemeralKeyJstring, 0)))
{
LOGE("## pkDecryptJni(): failure - ephemeral key JNI allocation OOM");
errorMessage = "ephemeral key JNI allocation OOM";
}
else
{
size_t maxPlaintextLength = olm_pk_max_plaintext_length(
decryptionPtr,
(size_t)env->GetStringUTFLength(ciphertextJstring)
);
uint8_t *plaintextPtr = NULL;
uint8_t *tempCiphertextPtr = NULL;
size_t ciphertextLength = (size_t)env->GetStringUTFLength(ciphertextJstring);
if (!(plaintextPtr = (uint8_t*)malloc(maxPlaintextLength)))
{
LOGE("## pkDecryptJni(): failure - plaintext JNI allocation OOM");
errorMessage = "plaintext JNI allocation OOM";
}
else if (!(tempCiphertextPtr = (uint8_t*)malloc(ciphertextLength)))
{
LOGE("## pkDecryptJni(): failure - temp ciphertext JNI allocation OOM");
}
else
{
memcpy(tempCiphertextPtr, ciphertextPtr, ciphertextLength);
size_t plaintextLength = olm_pk_decrypt(
decryptionPtr,
ephemeralKeyPtr, (size_t)env->GetStringUTFLength(ephemeralKeyJstring),
macPtr, (size_t)env->GetStringUTFLength(macJstring),
tempCiphertextPtr, ciphertextLength,
plaintextPtr, maxPlaintextLength
);
if (plaintextLength == olm_error())
{
errorMessage = olm_pk_decryption_last_error(decryptionPtr);
LOGE("## pkDecryptJni(): failure - olm_pk_decrypt Msg=%s", errorMessage);
}
else
{
decryptedMsgRet = env->NewByteArray(plaintextLength);
env->SetByteArrayRegion(decryptedMsgRet, 0, plaintextLength, (jbyte*)plaintextPtr);
LOGD(
"## pkDecryptJni(): success returnedLg=%lu OK",
static_cast<long unsigned int>(plaintextLength)
);
}
}
if (tempCiphertextPtr)
{
free(tempCiphertextPtr);
}
if (plaintextPtr)
{
memset(plaintextPtr, 0, maxPlaintextLength);
free(plaintextPtr);
}
}
if (ciphertextPtr)
{
env->ReleaseStringUTFChars(ciphertextJstring, ciphertextPtr);
}
if (macPtr)
{
env->ReleaseStringUTFChars(macJstring, macPtr);
}
if (ephemeralKeyPtr)
{
env->ReleaseStringUTFChars(ephemeralKeyJstring, ephemeralKeyPtr);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return decryptedMsgRet;
}
OlmPkSigning * initializePkSigningMemory()
{
size_t signingSize = olm_pk_signing_size();
OlmPkSigning *signingPtr = (OlmPkSigning *)malloc(signingSize);
if (signingPtr)
{
// init encryption object
signingPtr = olm_pk_signing(signingPtr);
LOGD(
"## initializePkSigningMemory(): success - OLM signing size=%lu",
static_cast<long unsigned int>(signingSize)
);
}
else
{
LOGE("## initializePkSigningMemory(): failure - OOM");
}
return signingPtr;
}
JNIEXPORT jlong OLM_PK_SIGNING_FUNC_DEF(createNewPkSigningJni)(JNIEnv *env, jobject thiz)
{
const char* errorMessage = NULL;
OlmPkSigning *signingPtr = initializePkSigningMemory();
// init signing memory allocation
if (!signingPtr)
{
LOGE("## createNewPkSigningJni(): failure - init signing OOM");
errorMessage = "init signing OOM";
}
else
{
LOGD("## createNewPkSigningJni(): success - OLM signing created");
LOGD(
"## createNewPkSigningJni(): signingPtr=%p (jlong)(intptr_t)signingPtr=%lld",
signingPtr, (jlong)(intptr_t)signingPtr
);
}
if (errorMessage)
{
// release the allocated data
if (signingPtr)
{
olm_clear_pk_signing(signingPtr);
free(signingPtr);
}
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return (jlong)(intptr_t)signingPtr;
}
JNIEXPORT void OLM_PK_SIGNING_FUNC_DEF(releasePkSigningJni)(JNIEnv *env, jobject thiz)
{
LOGD("## releasePkSigningJni(): IN");
OlmPkSigning* signingPtr = getPkSigningInstanceId(env, thiz);
if (!signingPtr)
{
LOGE(" ## releasePkSigningJni(): failure - invalid Signing ptr=NULL");
}
else
{
LOGD(" ## releasePkSigningJni(): signingPtr=%p", signingPtr);
olm_clear_pk_signing(signingPtr);
LOGD(" ## releasePkSigningJni(): IN");
// even if free(NULL) does not crash, logs are performed for debug
// purpose
free(signingPtr);
LOGD(" ## releasePkSigningJni(): OUT");
}
}
JNIEXPORT jbyteArray OLM_PK_SIGNING_FUNC_DEF(generateSeedJni)(JNIEnv *env, jobject thiz)
{
size_t randomLength = olm_pk_signing_seed_length();
uint8_t *randomBuffPtr = NULL;
jbyteArray randomRet = 0;
const char* errorMessage = NULL;
if (!setRandomInBuffer(env, &randomBuffPtr, randomLength))
{
errorMessage = "random buffer init";
LOGE("## pkSigningGenerateSeedJni(): failure - %s", errorMessage);
}
else if (!(randomRet = env->NewByteArray(randomLength)))
{
errorMessage = "randomRet JNI allocation OOM";
LOGE(" ## pkSigningGenerateSeedJni(): falure - %s", errorMessage);
}
else
{
env->SetByteArrayRegion(
randomRet, 0, randomLength, (jbyte*)randomBuffPtr
);
}
if (randomBuffPtr)
{
memset(randomBuffPtr, 0, randomLength);
free(randomBuffPtr);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return randomRet;
}
JNIEXPORT jint OLM_PK_SIGNING_FUNC_DEF(seedLength)(JNIEnv *env, jobject thiz)
{
return (jint) olm_pk_signing_seed_length();
}
JNIEXPORT jbyteArray OLM_PK_SIGNING_FUNC_DEF(setKeyFromSeedJni)(JNIEnv *env, jobject thiz, jbyteArray seed)
{
const char* errorMessage = NULL;
OlmPkSigning *signingPtr = getPkSigningInstanceId(env, thiz);
jbyteArray publicKeyRet = 0;
jbyte *seedPtr = NULL;
jboolean seedWasCopied = JNI_FALSE;
if (!signingPtr)
{
errorMessage = "invalid Siging ptr=NULL";
LOGE(" ## setPkSigningKeyFromSeedJni(): failure - %s", errorMessage);
}
else if (!seed)
{
errorMessage = "invalid seed";
LOGE(" ## setPkSigningKeyFromSeedJni: failure - %s", errorMessage);
}
else if (!(seedPtr = env->GetByteArrayElements(seed, &seedWasCopied)))
{
errorMessage = "seed JNI allocation OOM";
LOGE(" ## setPkSigningKeyFromSeedJni(): failure - %s", errorMessage);
}
else
{
size_t publicKeyLength = olm_pk_signing_public_key_length();
uint8_t *publicKeyPtr = NULL;
size_t seedLength = (size_t)env->GetArrayLength(seed);
if (!(publicKeyPtr = (uint8_t*)malloc(publicKeyLength)))
{
errorMessage = "public key JNI allocation OOM";
LOGE(" ## setPkSigningKeyFromSeedJni(): falure - %s", errorMessage);
}
else
{
size_t returnValue = olm_pk_signing_key_from_seed(
signingPtr,
publicKeyPtr, publicKeyLength,
seedPtr, seedLength
);
if (returnValue == olm_error())
{
errorMessage = olm_pk_signing_last_error(signingPtr);
LOGE(" ## setPkSigningKeyFromSeedJni: failure - olm_pk_signing_key_from_seed Msg=%s", errorMessage);
}
else
{
if (!(publicKeyRet = env->NewByteArray(publicKeyLength))) {
errorMessage = "publicKeyRet JNI allocation OOM";
LOGE(" ## setPkSigningKeyFromSeedJni(): falure - %s", errorMessage);
} else {
env->SetByteArrayRegion(
publicKeyRet, 0, publicKeyLength, (jbyte*)publicKeyPtr
);
}
}
}
}
if (seedPtr)
{
if (seedWasCopied)
{
memset(seedPtr, 0, (size_t)env->GetArrayLength(seed));
}
env->ReleaseByteArrayElements(seed, seedPtr, JNI_ABORT);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return publicKeyRet;
}
JNIEXPORT jbyteArray OLM_PK_SIGNING_FUNC_DEF(pkSignJni)(JNIEnv *env, jobject thiz, jbyteArray aMessage)
{
const char* errorMessage = NULL;
OlmPkSigning *signingPtr = getPkSigningInstanceId(env, thiz);
jbyteArray signatureRet = 0;
jbyte *messagePtr = NULL;
jboolean messageWasCopied = JNI_FALSE;
if (!signingPtr)
{
errorMessage = "invalid Siging ptr=NULL";
LOGE(" ## setPkSignJni(): failure - %s", errorMessage);
}
else if (!aMessage)
{
errorMessage = "message seed";
LOGE(" ## setPkSignJni: failure - %s", errorMessage);
}
else if (!(messagePtr = env->GetByteArrayElements(aMessage, &messageWasCopied)))
{
errorMessage = "message JNI allocation OOM";
LOGE(" ## setPkSignJni(): failure - %s", errorMessage);
}
else
{
size_t signatureLength = olm_pk_signature_length();
uint8_t *signaturePtr = NULL;
size_t messageLength = (size_t)env->GetArrayLength(aMessage);
if (!(signaturePtr = (uint8_t*)malloc(signatureLength)))
{
errorMessage = "signature JNI allocation OOM";
LOGE(" ## setPkSignJni(): falure - %s", errorMessage);
}
else
{
size_t returnValue = olm_pk_sign(
signingPtr,
(uint8_t *)messagePtr, messageLength,
signaturePtr, signatureLength
);
if (returnValue == olm_error())
{
errorMessage = olm_pk_signing_last_error(signingPtr);
LOGE(" ## setPkSignJni: failure - olm_pk_sign Msg=%s", errorMessage);
}
else
{
if (!(signatureRet = env->NewByteArray(signatureLength))) {
errorMessage = "signatureRet JNI allocation OOM";
LOGE(" ## setPkSignJni(): falure - %s", errorMessage);
} else {
env->SetByteArrayRegion(
signatureRet, 0, signatureLength, (jbyte*)signaturePtr
);
}
}
}
}
if (messagePtr)
{
if (messageWasCopied)
{
memset(messagePtr, 0, (size_t)env->GetArrayLength(aMessage));
}
env->ReleaseByteArrayElements(aMessage, messagePtr, JNI_ABORT);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return signatureRet;
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2018,2019 New Vector 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 _OLMPK_H
#define _OLMPK_H
#include "olm_jni.h"
#include "olm/pk.h"
#define OLM_PK_ENCRYPTION_FUNC_DEF(func_name) FUNC_DEF(OlmPkEncryption,func_name)
#define OLM_PK_DECRYPTION_FUNC_DEF(func_name) FUNC_DEF(OlmPkDecryption,func_name)
#define OLM_PK_SIGNING_FUNC_DEF(func_name) FUNC_DEF(OlmPkSigning,func_name)
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jlong OLM_PK_ENCRYPTION_FUNC_DEF(createNewPkEncryptionJni)(JNIEnv *env, jobject thiz);
JNIEXPORT void OLM_PK_ENCRYPTION_FUNC_DEF(releasePkEncryptionJni)(JNIEnv *env, jobject thiz);
JNIEXPORT void OLM_PK_ENCRYPTION_FUNC_DEF(setRecipientKeyJni)(JNIEnv *env, jobject thiz, jbyteArray aKeyBuffer);
JNIEXPORT jbyteArray OLM_PK_ENCRYPTION_FUNC_DEF(encryptJni)(JNIEnv *env, jobject thiz, jbyteArray aPlaintextBuffer, jobject aEncryptedMsg);
JNIEXPORT jlong OLM_PK_DECRYPTION_FUNC_DEF(createNewPkDecryptionJni)(JNIEnv *env, jobject thiz);
JNIEXPORT void OLM_PK_DECRYPTION_FUNC_DEF(releasePkDecryptionJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jint OLM_PK_DECRYPTION_FUNC_DEF(privateKeyLength)(JNIEnv *env, jobject thiz);
JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(setPrivateKeyJni)(JNIEnv *env, jobject thiz, jbyteArray key);
JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(generateKeyJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(privateKeyJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(decryptJni)(JNIEnv *env, jobject thiz, jobject aEncryptedMsg);
JNIEXPORT jlong OLM_PK_SIGNING_FUNC_DEF(createNewPkSigningJni)(JNIEnv *env, jobject thiz);
JNIEXPORT void OLM_PK_SIGNING_FUNC_DEF(releasePkSigningJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jint OLM_PK_SIGNING_FUNC_DEF(seedLength)(JNIEnv *env, jobject thiz);
JNIEXPORT jbyteArray OLM_PK_SIGNING_FUNC_DEF(generateSeedJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jbyteArray OLM_PK_SIGNING_FUNC_DEF(setKeyFromSeedJni)(JNIEnv *env, jobject thiz, jbyteArray seed);
JNIEXPORT jbyteArray OLM_PK_SIGNING_FUNC_DEF(pkSignJni)(JNIEnv *env, jobject thiz, jbyteArray aMessage);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,470 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "olm_sas.h"
#include "olm/olm.h"
using namespace AndroidOlmSdk;
JNIEXPORT jlong OLM_SAS_FUNC_DEF(createNewSASJni)(JNIEnv *env, jobject thiz)
{
size_t sasSize = olm_sas_size();
OlmSAS *sasPtr = (OlmSAS *) malloc(sasSize);
const char* errorMessage = NULL;
if (!sasPtr)
{
LOGE("## createNewSASJni(): failure - init SAS OOM");
env->ThrowNew(env->FindClass("java/lang/Exception"), "init sas OOM");
}
else
{
sasPtr = olm_sas(sasPtr)
LOGD(" ## createNewSASJni(): success - sasPtr=%p (jlong)(intptr_t)accountPtr=%lld",sasPtr,(jlong)(intptr_t)sasPtr);
}
size_t randomSize = olm_create_sas_random_length(sasPtr);
uint8_t *randomBuffPtr = NULL;
LOGD("## createNewSASJni(): randomSize=%lu",static_cast<long unsigned int>(randomSize));
if ( (0 != randomSize) && !setRandomInBuffer(env, &randomBuffPtr, randomSize))
{
LOGE("## createNewSASJni(): failure - random buffer init");
errorMessage = "Failed to init private key";
}
else
{
size_t result = olm_create_sas(sasPtr, randomBuffPtr, randomSize);
if (result == olm_error())
{
errorMessage = (const char *)olm_sas_last_error(sasPtr);
LOGE("## createNewSASJni(): failure - error creating SAS Msg=%s", errorMessage);
}
}
if (randomBuffPtr)
{
memset(randomBuffPtr, 0, randomSize);
free(randomBuffPtr);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return (jlong)(intptr_t)sasPtr;
}
JNIEXPORT void OLM_SAS_FUNC_DEF(releaseSASJni)(JNIEnv *env, jobject thiz)
{
LOGD("## releaseSASJni(): IN");
OlmSAS* sasPtr = getOlmSasInstanceId(env, thiz);
if (!sasPtr)
{
LOGE("## releaseSessionJni(): failure - invalid Session ptr=NULL");
}
else
{
olm_clear_sas(sasPtr);
// even if free(NULL) does not crash, logs are performed for debug purpose
free(sasPtr);
}
}
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(getPubKeyJni)(JNIEnv *env, jobject thiz)
{
LOGD("## getPubKeyJni(): IN");
const char* errorMessage = NULL;
jbyteArray returnValue = 0;
OlmSAS* sasPtr = getOlmSasInstanceId(env, thiz);
if (!sasPtr)
{
LOGE("## getPubKeyJni(): failure - invalid SAS ptr=NULL");
errorMessage = "invalid SAS ptr=NULL";
}
else
{
size_t pubKeyLength = olm_sas_pubkey_length(sasPtr);
void *pubkey = malloc(pubKeyLength*sizeof(uint8_t));
size_t result = olm_sas_get_pubkey(sasPtr, pubkey, pubKeyLength);
if (result == olm_error())
{
errorMessage = (const char *)olm_sas_last_error(sasPtr);
LOGE("## getPubKeyJni(): failure - error getting pub key Msg=%s", errorMessage);
}
else
{
returnValue = env->NewByteArray(pubKeyLength);
env->SetByteArrayRegion(returnValue, 0 , pubKeyLength, (jbyte*)pubkey);
}
if (pubkey) {
free(pubkey);
}
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return returnValue;
}
JNIEXPORT void OLM_SAS_FUNC_DEF(setTheirPubKey)(JNIEnv *env, jobject thiz,jbyteArray pubKeyBuffer) {
OlmSAS* sasPtr = getOlmSasInstanceId(env, thiz);
const char* errorMessage = NULL;
jbyte *pubKeyPtr = NULL;
jboolean pubKeyWasCopied = JNI_FALSE;
if (!sasPtr)
{
LOGE("## setTheirPubKey(): failure - invalid SAS ptr=NULL");
errorMessage = "invalid SAS ptr=NULL";
} else if(!pubKeyBuffer) {
LOGE("## setTheirPubKey(): failure - invalid info");
errorMessage = "invalid pubKey";
}
else if (!(pubKeyPtr = env->GetByteArrayElements(pubKeyBuffer, &pubKeyWasCopied)))
{
LOGE(" ## setTheirPubKey(): failure - info JNI allocation OOM");
errorMessage = "info JNI allocation OOM";
}
else
{
size_t pubKeyLength = (size_t)env->GetArrayLength(pubKeyBuffer);
size_t result = olm_sas_set_their_key(sasPtr,pubKeyPtr,pubKeyLength);
if (result == olm_error())
{
errorMessage = (const char *)olm_sas_last_error(sasPtr);
LOGE("## setTheirPubKey(): failure - error setting their key Msg=%s", errorMessage);
}
}
// free alloc
if (pubKeyPtr)
{
if (pubKeyWasCopied)
{
memset(pubKeyPtr, 0, (size_t)env->GetArrayLength(pubKeyBuffer));
}
env->ReleaseByteArrayElements(pubKeyBuffer, pubKeyPtr, JNI_ABORT);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
}
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(generateShortCodeJni)(JNIEnv *env, jobject thiz, jbyteArray infoStringBytes, jint byteNb) {
LOGD("## generateShortCodeJni(): IN");
const char* errorMessage = NULL;
jbyteArray returnValue = 0;
OlmSAS* sasPtr = getOlmSasInstanceId(env, thiz);
jbyte *infoPtr = NULL;
jboolean infoWasCopied = JNI_FALSE;
if (!sasPtr)
{
LOGE("## generateShortCodeJni(): failure - invalid SAS ptr=NULL");
errorMessage = "invalid SAS ptr=NULL";
} else if(!infoStringBytes) {
LOGE("## generateShortCodeJni(): failure - invalid info");
errorMessage = "invalid info";
}
else if (!(infoPtr = env->GetByteArrayElements(infoStringBytes, &infoWasCopied)))
{
LOGE(" ## generateShortCodeJni(): failure - info JNI allocation OOM");
errorMessage = "info JNI allocation OOM";
}
else {
size_t shortBytesCodeLength = (size_t) byteNb;
void *shortBytesCode = malloc(shortBytesCodeLength * sizeof(uint8_t));
size_t infoLength = (size_t)env->GetArrayLength(infoStringBytes);
olm_sas_generate_bytes(sasPtr, infoPtr, infoLength, shortBytesCode, shortBytesCodeLength);
returnValue = env->NewByteArray(shortBytesCodeLength);
env->SetByteArrayRegion(returnValue, 0 , shortBytesCodeLength, (jbyte*)shortBytesCode);
free(shortBytesCode);
}
// free alloc
if (infoPtr)
{
if (infoWasCopied)
{
memset(infoPtr, 0, (size_t)env->GetArrayLength(infoStringBytes));
}
env->ReleaseByteArrayElements(infoStringBytes, infoPtr, JNI_ABORT);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return returnValue;
}
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(calculateMacJni)(JNIEnv *env, jobject thiz,jbyteArray messageBuffer,jbyteArray infoBuffer) {
LOGD("## calculateMacJni(): IN");
const char* errorMessage = NULL;
jbyteArray returnValue = 0;
OlmSAS* sasPtr = getOlmSasInstanceId(env, thiz);
jbyte *messagePtr = NULL;
jboolean messageWasCopied = JNI_FALSE;
jbyte *infoPtr = NULL;
jboolean infoWasCopied = JNI_FALSE;
if (!sasPtr)
{
LOGE("## calculateMacJni(): failure - invalid SAS ptr=NULL");
errorMessage = "invalid SAS ptr=NULL";
} else if(!messageBuffer) {
LOGE("## calculateMacJni(): failure - invalid message");
errorMessage = "invalid info";
}
else if (!(messagePtr = env->GetByteArrayElements(messageBuffer, &messageWasCopied)))
{
LOGE(" ## calculateMacJni(): failure - message JNI allocation OOM");
errorMessage = "message JNI allocation OOM";
}
else if (!(infoPtr = env->GetByteArrayElements(infoBuffer, &infoWasCopied)))
{
LOGE(" ## calculateMacJni(): failure - info JNI allocation OOM");
errorMessage = "info JNI allocation OOM";
} else {
size_t infoLength = (size_t)env->GetArrayLength(infoBuffer);
size_t messageLength = (size_t)env->GetArrayLength(messageBuffer);
size_t macLength = olm_sas_mac_length(sasPtr);
void *macPtr = malloc(macLength*sizeof(uint8_t));
size_t result = olm_sas_calculate_mac(sasPtr,messagePtr,messageLength,infoPtr,infoLength,macPtr,macLength);
if (result == olm_error())
{
errorMessage = (const char *)olm_sas_last_error(sasPtr);
LOGE("## calculateMacJni(): failure - error calculating SAS mac Msg=%s", errorMessage);
}
else
{
returnValue = env->NewByteArray(macLength);
env->SetByteArrayRegion(returnValue, 0 , macLength, (jbyte*)macPtr);
}
if (macPtr) {
free(macPtr);
}
}
// free alloc
if (infoPtr)
{
if (infoWasCopied)
{
memset(infoPtr, 0, (size_t)env->GetArrayLength(infoBuffer));
}
env->ReleaseByteArrayElements(infoBuffer, infoPtr, JNI_ABORT);
}
if (messagePtr)
{
if (messageWasCopied)
{
memset(messagePtr, 0, (size_t)env->GetArrayLength(messageBuffer));
}
env->ReleaseByteArrayElements(messageBuffer, messagePtr, JNI_ABORT);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return returnValue;
}
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(calculateMacFixedBase64Jni)(JNIEnv *env, jobject thiz,jbyteArray messageBuffer,jbyteArray infoBuffer) {
LOGD("## calculateMacFixedBase64Jni(): IN");
const char* errorMessage = NULL;
jbyteArray returnValue = 0;
OlmSAS* sasPtr = getOlmSasInstanceId(env, thiz);
jbyte *messagePtr = NULL;
jboolean messageWasCopied = JNI_FALSE;
jbyte *infoPtr = NULL;
jboolean infoWasCopied = JNI_FALSE;
if (!sasPtr)
{
LOGE("## calculateMacFixedBase64Jni(): failure - invalid SAS ptr=NULL");
errorMessage = "invalid SAS ptr=NULL";
} else if(!messageBuffer) {
LOGE("## calculateMacFixedBase64Jni(): failure - invalid message");
errorMessage = "invalid info";
}
else if (!(messagePtr = env->GetByteArrayElements(messageBuffer, &messageWasCopied)))
{
LOGE(" ## calculateMacFixedBase64Jni(): failure - message JNI allocation OOM");
errorMessage = "message JNI allocation OOM";
}
else if (!(infoPtr = env->GetByteArrayElements(infoBuffer, &infoWasCopied)))
{
LOGE(" ## calculateMacFixedBase64Jni(): failure - info JNI allocation OOM");
errorMessage = "info JNI allocation OOM";
} else {
size_t infoLength = (size_t)env->GetArrayLength(infoBuffer);
size_t messageLength = (size_t)env->GetArrayLength(messageBuffer);
size_t macLength = olm_sas_mac_length(sasPtr);
void *macPtr = malloc(macLength*sizeof(uint8_t));
size_t result = olm_sas_calculate_mac_fixed_base64(sasPtr,messagePtr,messageLength,infoPtr,infoLength,macPtr,macLength);
if (result == olm_error())
{
errorMessage = (const char *)olm_sas_last_error(sasPtr);
LOGE("## calculateMacFixedBase64Jni(): failure - error calculating SAS mac Msg=%s", errorMessage);
}
else
{
returnValue = env->NewByteArray(macLength);
env->SetByteArrayRegion(returnValue, 0 , macLength, (jbyte*)macPtr);
}
if (macPtr) {
free(macPtr);
}
}
// free alloc
if (infoPtr)
{
if (infoWasCopied)
{
memset(infoPtr, 0, (size_t)env->GetArrayLength(infoBuffer));
}
env->ReleaseByteArrayElements(infoBuffer, infoPtr, JNI_ABORT);
}
if (messagePtr)
{
if (messageWasCopied)
{
memset(messagePtr, 0, (size_t)env->GetArrayLength(messageBuffer));
}
env->ReleaseByteArrayElements(messageBuffer, messagePtr, JNI_ABORT);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return returnValue;
}
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(calculateMacLongKdfJni)(JNIEnv *env, jobject thiz,jbyteArray messageBuffer,jbyteArray infoBuffer) {
LOGD("## calculateMacLongKdfJni(): IN");
const char* errorMessage = NULL;
jbyteArray returnValue = 0;
OlmSAS* sasPtr = getOlmSasInstanceId(env, thiz);
jbyte *messagePtr = NULL;
jboolean messageWasCopied = JNI_FALSE;
jbyte *infoPtr = NULL;
jboolean infoWasCopied = JNI_FALSE;
if (!sasPtr)
{
LOGE("## calculateMacLongKdfJni(): failure - invalid SAS ptr=NULL");
errorMessage = "invalid SAS ptr=NULL";
} else if(!messageBuffer) {
LOGE("## calculateMacLongKdfJni(): failure - invalid message");
errorMessage = "invalid info";
}
else if (!(messagePtr = env->GetByteArrayElements(messageBuffer, &messageWasCopied)))
{
LOGE(" ## calculateMacLongKdfJni(): failure - message JNI allocation OOM");
errorMessage = "message JNI allocation OOM";
}
else if (!(infoPtr = env->GetByteArrayElements(infoBuffer, &infoWasCopied)))
{
LOGE(" ## calculateMacLongKdfJni(): failure - info JNI allocation OOM");
errorMessage = "info JNI allocation OOM";
} else {
size_t infoLength = (size_t)env->GetArrayLength(infoBuffer);
size_t messageLength = (size_t)env->GetArrayLength(messageBuffer);
size_t macLength = olm_sas_mac_length(sasPtr);
void *macPtr = malloc(macLength*sizeof(uint8_t));
size_t result = olm_sas_calculate_mac_long_kdf(sasPtr,messagePtr,messageLength,infoPtr,infoLength,macPtr,macLength);
if (result == olm_error())
{
errorMessage = (const char *)olm_sas_last_error(sasPtr);
LOGE("## calculateMacLongKdfJni(): failure - error calculating SAS mac Msg=%s", errorMessage);
}
else
{
returnValue = env->NewByteArray(macLength);
env->SetByteArrayRegion(returnValue, 0 , macLength, (jbyte*)macPtr);
}
if (macPtr) {
free(macPtr);
}
}
// free alloc
if (infoPtr)
{
if (infoWasCopied)
{
memset(infoPtr, 0, (size_t)env->GetArrayLength(infoBuffer));
}
env->ReleaseByteArrayElements(infoBuffer, infoPtr, JNI_ABORT);
}
if (messagePtr)
{
if (messageWasCopied)
{
memset(messagePtr, 0, (size_t)env->GetArrayLength(messageBuffer));
}
env->ReleaseByteArrayElements(messageBuffer, messagePtr, JNI_ABORT);
}
if (errorMessage)
{
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
}
return returnValue;
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2019 New Vector 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 _OMLSAS_H
#define _OMLSAS_H
#include "olm_jni.h"
#include "olm/sas.h"
#define OLM_SAS_FUNC_DEF(func_name) FUNC_DEF(OlmSAS,func_name)
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jlong OLM_SAS_FUNC_DEF(createNewSASJni)(JNIEnv *env, jobject thiz);
JNIEXPORT void OLM_SAS_FUNC_DEF(releaseSASJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(getPubKeyJni)(JNIEnv *env, jobject thiz);
JNIEXPORT void OLM_SAS_FUNC_DEF(setTheirPubKey)(JNIEnv *env, jobject thiz,jbyteArray pubKey);
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(generateShortCodeJni)(JNIEnv *env, jobject thiz, jbyteArray infoStringBytes, jint byteNb);
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(calculateMacJni)(JNIEnv *env, jobject thiz, jbyteArray messageBuffer, jbyteArray infoBuffer);
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(calculateMacFixedBase64Jni)(JNIEnv *env, jobject thiz, jbyteArray messageBuffer, jbyteArray infoBuffer);
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(calculateMacLongKdfJni)(JNIEnv *env, jobject thiz, jbyteArray messageBuffer, jbyteArray infoBuffer);
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,60 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016 Vector Creations 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 _OLMSESSION_H
#define _OLMSESSION_H
#include "olm_jni.h"
#include "olm/olm.h"
#define OLM_SESSION_FUNC_DEF(func_name) FUNC_DEF(OlmSession,func_name)
#ifdef __cplusplus
extern "C" {
#endif
// session creation/destruction
JNIEXPORT void OLM_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jlong OLM_SESSION_FUNC_DEF(createNewSessionJni)(JNIEnv *env, jobject thiz);
// outbound session
JNIEXPORT void OLM_SESSION_FUNC_DEF(initOutboundSessionJni)(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jbyteArray aTheirIdentityKey, jbyteArray aTheirOneTimeKey);
// inbound sessions: establishment based on PRE KEY message
JNIEXPORT void OLM_SESSION_FUNC_DEF(initInboundSessionJni)(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jbyteArray aOneTimeKeyMsg);
JNIEXPORT void OLM_SESSION_FUNC_DEF(initInboundSessionFromIdKeyJni)(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jbyteArray aTheirIdentityKey, jbyteArray aOneTimeKeyMsg);
// match inbound sessions: based on PRE KEY message
JNIEXPORT jboolean OLM_SESSION_FUNC_DEF(matchesInboundSessionJni)(JNIEnv *env, jobject thiz, jbyteArray aOneTimeKeyMsg);
JNIEXPORT jboolean OLM_SESSION_FUNC_DEF(matchesInboundSessionFromIdKeyJni)(JNIEnv *env, jobject thiz, jbyteArray aTheirIdentityKey, jbyteArray aOneTimeKeyMsg);
// encrypt/decrypt
JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobject thiz, jbyteArray aClearMsg, jobject aEncryptedMsg);
JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(decryptMessageJni)(JNIEnv *env, jobject thiz, jobject aEncryptedMsg);
JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(getSessionIdentifierJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(olmSessionDescribeJni)(JNIEnv *env, jobject thiz);
// serialization
JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(serializeJni)(JNIEnv *env, jobject thiz, jbyteArray aKey);
JNIEXPORT jlong OLM_SESSION_FUNC_DEF(deserializeJni)(JNIEnv *env, jobject thiz, jbyteArray aSerializedData, jbyteArray aKey);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,236 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016 Vector Creations Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "olm_utility.h"
using namespace AndroidOlmSdk;
OlmUtility* initializeUtilityMemory()
{
size_t utilitySize = olm_utility_size();
OlmUtility* utilityPtr = (OlmUtility*)malloc(utilitySize);
if (utilityPtr)
{
utilityPtr = olm_utility(utilityPtr);
LOGD("## initializeUtilityMemory(): success - OLM utility size=%lu",static_cast<long unsigned int>(utilitySize));
}
else
{
LOGE("## initializeUtilityMemory(): failure - OOM");
}
return utilityPtr;
}
JNIEXPORT jlong OLM_UTILITY_FUNC_DEF(createUtilityJni)(JNIEnv *env, jobject thiz)
{
OlmUtility* utilityPtr = initializeUtilityMemory();
LOGD("## createUtilityJni(): IN");
// init account memory allocation
if (!utilityPtr)
{
LOGE(" ## createUtilityJni(): failure - init OOM");
env->ThrowNew(env->FindClass("java/lang/Exception"), "init OOM");
}
else
{
LOGD(" ## createUtilityJni(): success");
}
return (jlong)(intptr_t)utilityPtr;
}
JNIEXPORT void OLM_UTILITY_FUNC_DEF(releaseUtilityJni)(JNIEnv *env, jobject thiz)
{
OlmUtility* utilityPtr = getUtilityInstanceId(env, thiz);
LOGD("## releaseUtilityJni(): IN");
if (!utilityPtr)
{
LOGE("## releaseUtilityJni(): failure - utility ptr=NULL");
}
else
{
olm_clear_utility(utilityPtr);
free(utilityPtr);
}
}
/**
* Verify an ed25519 signature.
* @param aSignature the base64-encoded message signature to be checked.
* @param aKey the ed25519 key (fingerprint key)
* @param aMessage the message which was signed
* @return 0 if validation succeed, an error message string if operation failed
*/
JNIEXPORT jstring OLM_UTILITY_FUNC_DEF(verifyEd25519SignatureJni)(JNIEnv *env, jobject thiz, jbyteArray aSignatureBuffer, jbyteArray aKeyBuffer, jbyteArray aMessageBuffer)
{
jstring errorMessageRetValue = 0;
OlmUtility* utilityPtr = getUtilityInstanceId(env, thiz);
jbyte* signaturePtr = NULL;
jbyte* keyPtr = NULL;
jbyte* messagePtr = NULL;
jboolean messageWasCopied = JNI_FALSE;
LOGD("## verifyEd25519SignatureJni(): IN");
if (!utilityPtr)
{
LOGE(" ## verifyEd25519SignatureJni(): failure - invalid utility ptr=NULL");
}
else if (!aSignatureBuffer || !aKeyBuffer || !aMessageBuffer)
{
LOGE(" ## verifyEd25519SignatureJni(): failure - invalid input parameters ");
}
else if (!(signaturePtr = env->GetByteArrayElements(aSignatureBuffer, 0)))
{
LOGE(" ## verifyEd25519SignatureJni(): failure - signature JNI allocation OOM");
}
else if (!(keyPtr = env->GetByteArrayElements(aKeyBuffer, 0)))
{
LOGE(" ## verifyEd25519SignatureJni(): failure - key JNI allocation OOM");
}
else if (!(messagePtr = env->GetByteArrayElements(aMessageBuffer, &messageWasCopied)))
{
LOGE(" ## verifyEd25519SignatureJni(): failure - message JNI allocation OOM");
}
else
{
size_t signatureLength = (size_t)env->GetArrayLength(aSignatureBuffer);
size_t keyLength = (size_t)env->GetArrayLength(aKeyBuffer);
size_t messageLength = (size_t)env->GetArrayLength(aMessageBuffer);
LOGD(" ## verifyEd25519SignatureJni(): signatureLength=%lu keyLength=%lu messageLength=%lu",static_cast<long unsigned int>(signatureLength),static_cast<long unsigned int>(keyLength),static_cast<long unsigned int>(messageLength));
LOGD(" ## verifyEd25519SignatureJni(): key=%.*s", static_cast<int>(keyLength), keyPtr);
size_t result = olm_ed25519_verify(utilityPtr,
(void const *)keyPtr,
keyLength,
(void const *)messagePtr,
messageLength,
(void*)signaturePtr,
signatureLength);
if (result == olm_error()) {
const char *errorMsgPtr = olm_utility_last_error(utilityPtr);
errorMessageRetValue = env->NewStringUTF(errorMsgPtr);
LOGE("## verifyEd25519SignatureJni(): failure - olm_ed25519_verify Msg=%s",errorMsgPtr);
}
else
{
LOGD("## verifyEd25519SignatureJni(): success - result=%lu", static_cast<long unsigned int>(result));
}
}
// free alloc
if (signaturePtr)
{
env->ReleaseByteArrayElements(aSignatureBuffer, signaturePtr, JNI_ABORT);
}
if (keyPtr)
{
env->ReleaseByteArrayElements(aKeyBuffer, keyPtr, JNI_ABORT);
}
if (messagePtr)
{
if (messageWasCopied) {
memset(messagePtr, 0, (size_t)env->GetArrayLength(aMessageBuffer));
}
env->ReleaseByteArrayElements(aMessageBuffer, messagePtr, JNI_ABORT);
}
return errorMessageRetValue;
}
/**
* Compute the digest (SHA 256) for the message passed in parameter.<br>
* The digest value is the function return value.
* An exception is thrown if the operation fails.
* @param aMessage the message
* @return digest of the message.
**/
JNIEXPORT jbyteArray OLM_UTILITY_FUNC_DEF(sha256Jni)(JNIEnv *env, jobject thiz, jbyteArray aMessageToHashBuffer)
{
jbyteArray sha256Ret = 0;
OlmUtility* utilityPtr = getUtilityInstanceId(env, thiz);
jbyte* messagePtr = NULL;
jboolean messageWasCopied = JNI_FALSE;
LOGD("## sha256Jni(): IN");
if (!utilityPtr)
{
LOGE(" ## sha256Jni(): failure - invalid utility ptr=NULL");
}
else if(!aMessageToHashBuffer)
{
LOGE(" ## sha256Jni(): failure - invalid message parameters ");
}
else if(!(messagePtr = env->GetByteArrayElements(aMessageToHashBuffer, &messageWasCopied)))
{
LOGE(" ## sha256Jni(): failure - message JNI allocation OOM");
}
else
{
// get lengths
size_t messageLength = (size_t)env->GetArrayLength(aMessageToHashBuffer);
size_t hashLength = olm_sha256_length(utilityPtr);
void* hashValuePtr = malloc((hashLength)*sizeof(uint8_t));
if (!hashValuePtr)
{
LOGE("## sha256Jni(): failure - hash value allocation OOM");
}
else
{
size_t result = olm_sha256(utilityPtr,
(void const *)messagePtr,
messageLength,
(void *)hashValuePtr,
hashLength);
if (result == olm_error())
{
LOGE("## sha256Jni(): failure - hash creation Msg=%s",(const char *)olm_utility_last_error(utilityPtr));
}
else
{
LOGD("## sha256Jni(): success - result=%lu hashValue=%.*s",static_cast<long unsigned int>(result), static_cast<int>(result), (char*)hashValuePtr);
sha256Ret = env->NewByteArray(result);
env->SetByteArrayRegion(sha256Ret, 0 , result, (jbyte*)hashValuePtr);
}
free(hashValuePtr);
}
}
if (messagePtr)
{
if (messageWasCopied) {
memset(messagePtr, 0, (size_t)env->GetArrayLength(aMessageToHashBuffer));
}
env->ReleaseByteArrayElements(aMessageToHashBuffer, messagePtr, JNI_ABORT);
}
return sha256Ret;
}

View File

@ -0,0 +1,40 @@
/*
* Copyright 2016 OpenMarket Ltd
* Copyright 2016 Vector Creations 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 _OLMUTILITY_H
#define _OLMUTILITY_H
#include "olm_jni.h"
#include "olm/olm.h"
#define OLM_UTILITY_FUNC_DEF(func_name) FUNC_DEF(OlmUtility,func_name)
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jlong OLM_UTILITY_FUNC_DEF(createUtilityJni)(JNIEnv *env, jobject thiz);
JNIEXPORT void OLM_UTILITY_FUNC_DEF(releaseUtilityJni)(JNIEnv *env, jobject thiz);
JNIEXPORT jstring OLM_UTILITY_FUNC_DEF(verifyEd25519SignatureJni)(JNIEnv *env, jobject thiz, jbyteArray aSignature, jbyteArray aKey, jbyteArray aMessage);
JNIEXPORT jbyteArray OLM_UTILITY_FUNC_DEF(sha256Jni)(JNIEnv *env, jobject thiz, jbyteArray aMessageToHash);
#ifdef __cplusplus
}
#endif
#endif

1
android/settings.gradle Normal file
View File

@ -0,0 +1 @@
include ':olm-sdk'

11
cmake/OlmConfig.cmake.in Normal file
View File

@ -0,0 +1,11 @@
get_filename_component(Olm_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
include(CMakeFindDependencyMacro)
list(APPEND CMAKE_MODULE_PATH ${Olm_CMAKE_DIR})
list(REMOVE_AT CMAKE_MODULE_PATH -1)
if(NOT TARGET Olm::olm)
include("${Olm_CMAKE_DIR}/OlmTargets.cmake")
endif()
set(Olm_LIBRARIES Olm::olm)

4
common.mk Normal file
View File

@ -0,0 +1,4 @@
MAJOR := 3
MINOR := 2
PATCH := 16

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 34 KiB

1
docs/DH ratchet.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 48 KiB

39
docs/DH ratchet.txt Normal file
View File

@ -0,0 +1,39 @@
lifelinestyle ::solid
participantspacing 10
participant :0 " " as p0
lifelinestyle p0 #white
actor "Alice" as A
participant :0 " " as p1
lifelinestyle p1 #white
actor "Bob" as B
participant :0 " " as p2
lifelinestyle p2 #white
parallel
box over A: ""//R[0]//"" = ""HKDF(0, //S//)""
box over B: ""//R[0]//"" = ""HKDF(0, //S//)""
parallel off
box over A: generate new ratchet keypair ""//T[0]//""
A->B: ""//R[0]//(//msg0//)"", ""//T[0]//""
A->B: ""//R[0]//(//msg1//)"", ""//T[0]//""
A->B: ""//R[0]//(//msg2//)"", ""//T[0]//""
box over B: generate new ratchet keypair ""//T[1]//""
box over B: ""//R[1]//"" = ""HKDF(//R[0]//, DH(//T[1]//, //T[0]//))""
B->A: ""//R[1]//(//msg3//)"", ""//T[1]//""
box over A: ""//R[1]//"" = ""HKDF(//R[0]//, DH(//T[0]//, //T[1]//))""
B->A: ""//R[1]//(//msg4//)"", ""//T[1]//""
B->(13)A: ""//R[1]//(//msg5//)"", ""//T[1]//""
space -14
box over A: generate new ratchet keypair ""//T[2]//""
box over A: ""//R[2]//"" = ""HKDF(//R[1]//, DH(//T[2]//, //T[1]//))""
A->(3)B: ""//R[2]//(//msg6//)"", ""//T[2]//""
box over B: ""//R[2]//"" = ""HKDF(//R[1]//, DH(//T[1]//, //T[2]//))""
box over p0,p2 #EDF2AE:<size:10>where:\n ""//S//"" is the shared secret derived from the 3ECDH exchange\n ""//R[n]//"" is a root key\n ""//T[n]//"" is a ratchet keypair\n ""HKDF(//salt//, //key//)"" means performing an HMAC-based key derivation with a salt value of ""//salt//"" and input key material of ""//key//""\n ""DH(//k1//, //k2//)"" means performing Diffie-Hellman with the private half of ""//k1//"" and the public half of ""//k2//""\n ""//R[n]//(//msg//)"" means a message encrypted with a key derived from root key ""//R[n]//""

BIN
docs/double_ratchet.dia Normal file

Binary file not shown.

433
docs/double_ratchet.svg Normal file
View File

@ -0,0 +1,433 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd">
<svg width="35cm" height="41cm" viewBox="-11 -2 691 809" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Background">
<g>
<ellipse style="fill: #90ee90; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" cx="150" cy="154" rx="32" ry="12"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="150" y="157.9">
<tspan x="150" y="157.9">HKDF</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #000000" x1="150" y1="21" x2="150" y2="137.276"/>
<polygon style="fill: #000000; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #000000" fill-rule="evenodd" points="150,141.776 147,135.776 150,137.276 153,135.776 "/>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="130" y="-1" width="40" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #000000; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="150" y="13.9">
<tspan x="150" y="13.9">S</tspan>
</text>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="90" y="117" width="20" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="100" y="131.9">
<tspan x="100" y="131.9">0</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="110" y1="133.5" x2="123.488" y2="142.828"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="127.189,145.388 120.547,144.442 123.488,142.828 123.96,139.507 "/>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="130" y="189" width="40" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="150" y="203.9">
<tspan x="150" y="203.9">R[0]</tspan>
</text>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="190" y="189" width="48" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="214" y="203.9">
<tspan x="214" y="203.9">C[0,0]</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="150" y1="166" x2="150" y2="184.276"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="150,188.776 147,182.776 150,184.276 153,182.776 "/>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="165.653" y1="164.536" x2="198.081" y2="186.362"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="201.814,188.875 195.162,188.014 198.081,186.362 198.512,183.036 "/>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="214" y1="211" x2="214" y2="217.276"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="214,221.776 211,215.776 214,217.276 217,215.776 "/>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="190" y="257" width="48" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="214" y="271.9">
<tspan x="214" y="271.9">M[0,0]</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="214" y1="242" x2="214" y2="252.276"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="214,256.776 211,250.776 214,252.276 217,250.776 "/>
</g>
<g>
<ellipse style="fill: #90ee90; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" cx="286" cy="200" rx="28" ry="10"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="286" y="203.9">
<tspan x="286" y="203.9">HMAC</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="238" y1="200" x2="253.276" y2="200"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="257.776,200 251.776,203 253.276,200 251.776,197 "/>
</g>
<g>
<ellipse style="fill: #90ee90; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" cx="214" cy="232" rx="28" ry="10"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="214" y="235.9">
<tspan x="214" y="235.9">HMAC</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="314" y1="200" x2="329.276" y2="200"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="333.776,200 327.776,203 329.276,200 327.776,197 "/>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="334" y="189" width="48" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="358" y="203.9">
<tspan x="358" y="203.9">C[0,1]</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="358" y1="211" x2="358" y2="217.276"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="358,221.776 355,215.776 358,217.276 361,215.776 "/>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="334" y="257" width="48" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="358" y="271.9">
<tspan x="358" y="271.9">M[0,1]</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="358" y1="242" x2="358" y2="252.276"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="358,256.776 355,250.776 358,252.276 361,250.776 "/>
</g>
<g>
<ellipse style="fill: #90ee90; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" cx="358" cy="232" rx="28" ry="10"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="358" y="235.9">
<tspan x="358" y="235.9">HMAC</tspan>
</text>
</g>
<g>
<ellipse style="fill: #90ee90; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" cx="430" cy="200" rx="28" ry="10"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="430" y="203.9">
<tspan x="430" y="203.9">HMAC</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="382" y1="200" x2="397.276" y2="200"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="401.776,200 395.776,203 397.276,200 395.776,197 "/>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="458" y1="200" x2="473.276" y2="200"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="477.776,200 471.776,203 473.276,200 471.776,197 "/>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="478" y="189" width="48" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="502" y="203.9">
<tspan x="502" y="203.9">C[0,2]</tspan>
</text>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="29.025" y="79" width="31.95" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="45" y="93.9">
<tspan x="45" y="93.9">T[0]</tspan>
</text>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="29" y="329" width="31.95" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005f5f; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="44.975" y="343.9">
<tspan x="44.975" y="343.9">T[1]</tspan>
</text>
</g>
<g>
<ellipse style="fill: #d8e5e5; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" cx="100" cy="362" rx="20" ry="12"/>
<text font-size="12.8" style="fill: #005f5f; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="100" y="365.9">
<tspan x="100" y="365.9">DH</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" x1="60.95" y1="345.5" x2="81.3612" y2="352.067"/>
<polygon style="fill: #005f5f; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" fill-rule="evenodd" points="85.6449,353.446 79.0144,354.464 81.3612,352.067 80.8521,348.752 "/>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="45" y1="101" x2="98.9812" y2="345.388"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="99.9518,349.782 95.7283,344.57 98.9812,345.388 101.587,343.276 "/>
</g>
<g>
<ellipse style="fill: #d8e5e5; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" cx="150" cy="394" rx="32" ry="12"/>
<text font-size="12.8" style="fill: #005f5f; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="150" y="397.9">
<tspan x="150" y="397.9">HKDF</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" x1="114.142" y1="370.486" x2="124.251" y2="381.969"/>
<polygon style="fill: #005f5f; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" fill-rule="evenodd" points="127.225,385.346 121.008,382.825 124.251,381.969 125.512,378.86 "/>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="130" y="429" width="40" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005f5f; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="150" y="443.9">
<tspan x="150" y="443.9">R[1]</tspan>
</text>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="190" y="429" width="48" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005f5f; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="214" y="443.9">
<tspan x="214" y="443.9">C[1,0]</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" x1="150" y1="406" x2="150" y2="424.276"/>
<polygon style="fill: #005f5f; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" fill-rule="evenodd" points="150,428.776 147,422.776 150,424.276 153,422.776 "/>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" x1="165.653" y1="404.536" x2="198.081" y2="426.362"/>
<polygon style="fill: #005f5f; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" fill-rule="evenodd" points="201.814,428.875 195.162,428.014 198.081,426.362 198.512,423.036 "/>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" x1="214" y1="451" x2="214" y2="457.276"/>
<polygon style="fill: #005f5f; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" fill-rule="evenodd" points="214,461.776 211,455.776 214,457.276 217,455.776 "/>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="190" y="497" width="48" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005f5f; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="214" y="511.9">
<tspan x="214" y="511.9">M[1,0]</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" x1="214" y1="482" x2="214" y2="492.276"/>
<polygon style="fill: #005f5f; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" fill-rule="evenodd" points="214,496.776 211,490.776 214,492.276 217,490.776 "/>
</g>
<g>
<ellipse style="fill: #d8e5e5; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" cx="286" cy="440" rx="28" ry="10"/>
<text font-size="12.8" style="fill: #005f5f; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="286" y="443.9">
<tspan x="286" y="443.9">HMAC</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" x1="238" y1="440" x2="253.276" y2="440"/>
<polygon style="fill: #005f5f; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" fill-rule="evenodd" points="257.776,440 251.776,443 253.276,440 251.776,437 "/>
</g>
<g>
<ellipse style="fill: #d8e5e5; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" cx="214" cy="472" rx="28" ry="10"/>
<text font-size="12.8" style="fill: #005f5f; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="214" y="475.9">
<tspan x="214" y="475.9">HMAC</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" x1="314" y1="440" x2="329.276" y2="440"/>
<polygon style="fill: #005f5f; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" fill-rule="evenodd" points="333.776,440 327.776,443 329.276,440 327.776,437 "/>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="150" y1="211" x2="150" y2="377.276"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="150,381.776 147,375.776 150,377.276 153,375.776 "/>
</g>
<g>
<ellipse style="fill: #90ee90; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" cx="100" cy="602" rx="20" ry="12"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="100" y="605.9">
<tspan x="100" y="605.9">DH</tspan>
</text>
</g>
<g>
<ellipse style="fill: #90ee90; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" cx="150" cy="634" rx="32" ry="12"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="150" y="637.9">
<tspan x="150" y="637.9">HKDF</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="114.142" y1="610.486" x2="124.251" y2="621.969"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="127.225,625.346 121.008,622.825 124.251,621.969 125.512,618.86 "/>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="130" y="669" width="40" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="150" y="683.9">
<tspan x="150" y="683.9">R[2]</tspan>
</text>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="190" y="669" width="48" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="214" y="683.9">
<tspan x="214" y="683.9">C[2,0]</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="150" y1="646" x2="150" y2="664.276"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="150,668.776 147,662.776 150,664.276 153,662.776 "/>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="165.653" y1="644.536" x2="198.081" y2="666.362"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="201.814,668.875 195.162,668.014 198.081,666.362 198.512,663.036 "/>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="214" y1="691" x2="214" y2="697.276"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="214,701.776 211,695.776 214,697.276 217,695.776 "/>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="190" y="737" width="48" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="214" y="751.9">
<tspan x="214" y="751.9">M[2,0]</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="214" y1="722" x2="214" y2="732.276"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="214,736.776 211,730.776 214,732.276 217,730.776 "/>
</g>
<g>
<ellipse style="fill: #90ee90; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" cx="286" cy="680" rx="28" ry="10"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="286" y="683.9">
<tspan x="286" y="683.9">HMAC</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="238" y1="680" x2="253.276" y2="680"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="257.776,680 251.776,683 253.276,680 251.776,677 "/>
</g>
<g>
<ellipse style="fill: #90ee90; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" cx="214" cy="712" rx="28" ry="10"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="214" y="715.9">
<tspan x="214" y="715.9">HMAC</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="314" y1="680" x2="329.276" y2="680"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="333.776,680 327.776,683 329.276,680 327.776,677 "/>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="526" y1="200" x2="541.276" y2="200"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="545.776,200 539.776,203 541.276,200 539.776,197 "/>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="502" y1="211" x2="502" y2="217.276"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="502,221.776 499,215.776 502,217.276 505,215.776 "/>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" x1="150" y1="451" x2="150" y2="617.276"/>
<polygon style="fill: #005f5f; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" fill-rule="evenodd" points="150,621.776 147,615.776 150,617.276 153,615.776 "/>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="334" y="429" width="48" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005f5f; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="358" y="443.9">
<tspan x="358" y="443.9">C[1,1]</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" x1="358" y1="451" x2="358" y2="457.276"/>
<polygon style="fill: #005f5f; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" fill-rule="evenodd" points="358,461.776 355,455.776 358,457.276 361,455.776 "/>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="334" y="499" width="48" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005f5f; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="358" y="513.9">
<tspan x="358" y="513.9">M[1,1]</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" x1="358" y1="482" x2="358" y2="494.276"/>
<polygon style="fill: #005f5f; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" fill-rule="evenodd" points="358,498.776 355,492.776 358,494.276 361,492.776 "/>
</g>
<g>
<ellipse style="fill: #d8e5e5; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" cx="358" cy="472" rx="28" ry="10"/>
<text font-size="12.8" style="fill: #005f5f; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="358" y="475.9">
<tspan x="358" y="475.9">HMAC</tspan>
</text>
</g>
<g>
<ellipse style="fill: #d8e5e5; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" cx="430" cy="440" rx="28" ry="10"/>
<text font-size="12.8" style="fill: #005f5f; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="430" y="443.9">
<tspan x="430" y="443.9">HMAC</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" x1="382" y1="440" x2="397.276" y2="440"/>
<polygon style="fill: #005f5f; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" fill-rule="evenodd" points="401.776,440 395.776,443 397.276,440 395.776,437 "/>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" x1="458" y1="440" x2="473.276" y2="440"/>
<polygon style="fill: #005f5f; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" fill-rule="evenodd" points="477.776,440 471.776,443 473.276,440 471.776,437 "/>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="478" y="429" width="48" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005f5f; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="502" y="443.9">
<tspan x="502" y="443.9">C[1,2]</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" x1="526" y1="440" x2="541.276" y2="440"/>
<polygon style="fill: #005f5f; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" fill-rule="evenodd" points="545.776,440 539.776,443 541.276,440 539.776,437 "/>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" x1="502" y1="451" x2="502" y2="457.276"/>
<polygon style="fill: #005f5f; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" fill-rule="evenodd" points="502,461.776 499,455.776 502,457.276 505,455.776 "/>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="334" y="669" width="48" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="358" y="683.9">
<tspan x="358" y="683.9">C[2,1]</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="358" y1="691" x2="358" y2="697.276"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="358,701.776 355,695.776 358,697.276 361,695.776 "/>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="334" y="737" width="48" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="358" y="751.9">
<tspan x="358" y="751.9">M[2,1]</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="358" y1="722" x2="358" y2="732.276"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="358,736.776 355,730.776 358,732.276 361,730.776 "/>
</g>
<g>
<ellipse style="fill: #90ee90; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" cx="358" cy="712" rx="28" ry="10"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="358" y="715.9">
<tspan x="358" y="715.9">HMAC</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="382" y1="680" x2="397.276" y2="680"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="401.776,680 395.776,683 397.276,680 395.776,677 "/>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #ffffff" x="29.025" y="569" width="31.95" height="22" rx="0" ry="0"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="45" y="583.9">
<tspan x="45" y="583.9">T[2]</tspan>
</text>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" x1="44.975" y1="351" x2="91.4314" y2="586.28"/>
<polygon style="fill: #005f5f; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005f5f" fill-rule="evenodd" points="92.3031,590.695 88.1976,585.389 91.4314,586.28 94.084,584.227 "/>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="60.975" y1="580" x2="81.7069" y2="591.26"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="85.6613,593.407 78.957,593.18 81.7069,591.26 81.8205,587.907 "/>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="150" y1="691" x2="150" y2="795.276"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="150,799.776 147,793.776 150,795.276 153,793.776 "/>
</g>
<g>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" x1="52.9875" y1="591" x2="59.2318" y2="802.192"/>
<polygon style="fill: #005200; fill-opacity: 1; stroke-opacity: 1; stroke-width: 0.2; stroke: #005200" fill-rule="evenodd" points="59.3648,806.69 56.1888,800.782 59.2318,802.192 62.1862,800.604 "/>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke: none" x="230" y="68.1" width="107.55" height="14.95"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="230" y="80">
<tspan x="230" y="80">Initiated by Alice</tspan>
</text>
</g>
<g>
<rect style="fill: #ffffff; fill-opacity: 1; stroke: none" x="230" y="558.1" width="107.55" height="14.95"/>
<text font-size="12.8" style="fill: #005200; fill-opacity: 1; stroke: none;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="230" y="570">
<tspan x="230" y="570">Initiated by Alice</tspan>
</text>
</g>
<text font-size="12.8" style="fill: #005f5f; fill-opacity: 1; stroke: none;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="230" y="330">
<tspan x="230" y="330">Initiated by Bob</tspan>
</text>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke-dasharray: 20; stroke: #000000" x1="0" y1="40" x2="680" y2="40"/>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke-dasharray: 20; stroke: #000000" x1="0" y1="290" x2="680" y2="290"/>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke-dasharray: 20; stroke: #000000" x1="0" y1="530" x2="680" y2="530"/>
<line style="fill: none; stroke-opacity: 1; stroke-width: 0.2; stroke-dasharray: 20; stroke: #000000" x1="-10" y1="770" x2="670" y2="770"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 32 KiB

374
docs/megolm.md Normal file
View File

@ -0,0 +1,374 @@
# Megolm group ratchet
An AES-based cryptographic ratchet intended for group communications.
## Background
The Megolm ratchet is intended for encrypted messaging applications where there
may be a large number of recipients of each message, thus precluding the use of
peer-to-peer encryption systems such as [Olm][].
It also allows a recipient to decrypt received messages multiple times. For
instance, in client/server applications, a copy of the ciphertext can be stored
on the (untrusted) server, while the client need only store the session keys.
## Overview
Each participant in a conversation uses their own outbound session for
encrypting messages. A session consists of a ratchet and an [Ed25519][] keypair.
Secrecy is provided by the ratchet, which can be wound forwards but not
backwards, and is used to derive a distinct message key for each message.
Authenticity is provided via Ed25519 signatures.
The value of the ratchet, and the public part of the Ed25519 key, are shared
with other participants in the conversation via secure peer-to-peer
channels. Provided that peer-to-peer channel provides authenticity of the
messages to the participants and deniability of the messages to third parties,
the Megolm session will inherit those properties.
## The Megolm ratchet algorithm
The Megolm ratchet $`R_i`$ consists of four parts, $`R_{i,j}`$ for
$`j \in {0,1,2,3}`$. The length of each part depends on the hash function
in use (256 bits for this version of Megolm).
The ratchet is initialised with cryptographically-secure random data, and
advanced as follows:
```math
\begin{aligned}
R_{i,0} &=
\begin{cases}
H_0\left(R_{2^{24}(n-1),0}\right) &\text{if }\exists n | i = 2^{24}n\\
R_{i-1,0} &\text{otherwise}
\end{cases}\\
R_{i,1} &=
\begin{cases}
H_1\left(R_{2^{24}(n-1),0}\right) &\text{if }\exists n | i = 2^{24}n\\
H_1\left(R_{2^{16}(m-1),1}\right) &\text{if }\exists m | i = 2^{16}m\\
R_{i-1,1} &\text{otherwise}
\end{cases}\\
R_{i,2} &=
\begin{cases}
H_2\left(R_{2^{24}(n-1),0}\right) &\text{if }\exists n | i = 2^{24}n\\
H_2\left(R_{2^{16}(m-1),1}\right) &\text{if }\exists m | i = 2^{16}m\\
H_2\left(R_{2^8(p-1),2}\right) &\text{if }\exists p | i = 2^8p\\
R_{i-1,2} &\text{otherwise}
\end{cases}\\
R_{i,3} &=
\begin{cases}
H_3\left(R_{2^{24}(n-1),0}\right) &\text{if }\exists n | i = 2^{24}n\\
H_3\left(R_{2^{16}(m-1),1}\right) &\text{if }\exists m | i = 2^{16}m\\
H_3\left(R_{2^8(p-1),2}\right) &\text{if }\exists p | i = 2^8p\\
H_3\left(R_{i-1,3}\right) &\text{otherwise}
\end{cases}
\end{aligned}
```
where $`H_0`$, $`H_1`$, $`H_2`$, and $`H_3`$ are different hash
functions. In summary: every $`2^8`$ iterations, $`R_{i,3}`$ is
reseeded from $`R_{i,2}`$. Every $`2^{16}`$ iterations, $`R_{i,2}`$
and $`R_{i,3}`$ are reseeded from $`R_{i,1}`$. Every $`2^{24}`$
iterations, $`R_{i,1}`$, $`R_{i,2}`$ and $`R_{i,3}`$ are reseeded
from $`R_{i,0}`$.
The complete ratchet value, $`R_{i}`$, is hashed to generate the keys used
to encrypt each message. This scheme allows the ratchet to be advanced an
arbitrary amount forwards while needing at most 1020 hash computations. A
client can decrypt chat history onwards from the earliest value of the ratchet
it is aware of, but cannot decrypt history from before that point without
reversing the hash function.
This allows a participant to share its ability to decrypt chat history with
another from a point in the conversation onwards by giving a copy of the
ratchet at that point in the conversation.
## The Megolm protocol
### Session setup
Each participant in a conversation generates their own Megolm session. A
session consists of three parts:
* a 32 bit counter, $`i`$.
* an [Ed25519][] keypair, $`K`$.
* a ratchet, $`R_i`$, which consists of four 256-bit values,
$`R_{i,j}`$ for $`j \in {0,1,2,3}`$.
The counter $`i`$ is initialised to $`0`$. A new Ed25519 keypair is
generated for $`K`$. The ratchet is simply initialised with 1024 bits of
cryptographically-secure random data.
A single participant may use multiple sessions over the lifetime of a
conversation. The public part of $`K`$ is used as an identifier to
discriminate between sessions.
### Sharing session data
To allow other participants in the conversation to decrypt messages, the
session data is formatted as described in [Session-sharing format](#session-sharing-format). It is then
shared with other participants in the conversation via a secure peer-to-peer
channel (such as that provided by [Olm][]).
When the session data is received from other participants, the recipient first
checks that the signature matches the public key. They then store their own
copy of the counter, ratchet, and public key.
### Message encryption
This version of Megolm uses [AES-256][] in [CBC][] mode with [PKCS#7][] padding and
[HMAC-SHA-256][] (truncated to 64 bits). The 256 bit AES key, 256 bit HMAC key,
and 128 bit AES IV are derived from the megolm ratchet $`R_i`$:
```math
\begin{aligned}
\mathit{AES\_KEY}_{i}\;\parallel\;\mathit{HMAC\_KEY}_{i}\;\parallel\;\mathit{AES\_IV}_{i}
&= \operatorname{HKDF}\left(0,\,R_{i},\text{"MEGOLM\_KEYS"},\,80\right) \\
\end{aligned}
```
where $`\parallel`$ represents string splitting, and
$`\operatorname{HKDF}\left(\mathit{salt},\,\mathit{IKM},\,\mathit{info},\,L\right)`$
refers to the [HMAC-based key
derivation function][] using using [SHA-256][] as the hash function
([HKDF-SHA-256][]) with a salt value of $`\mathit{salt}`$, input key material of
$`\mathit{IKM}`$, context string $`\mathit{info}`$, and output keying material length of
$`L`$ bytes.
The plain-text is encrypted with AES-256, using the key $`\mathit{AES\_KEY}_{i}`$
and the IV $`\mathit{AES\_IV}_{i}`$ to give the cipher-text, $`X_{i}`$.
The ratchet index $`i`$, and the cipher-text $`X_{i}`$, are then packed
into a message as described in [Message format](#message-format). Then the entire message
(including the version bytes and all payload bytes) are passed through
HMAC-SHA-256. The first 8 bytes of the MAC are appended to the message.
Finally, the authenticated message is signed using the Ed25519 keypair; the 64
byte signature is appended to the message.
The complete signed message, together with the public part of $`K`$ (acting
as a session identifier), can then be sent over an insecure channel. The
message can then be authenticated and decrypted only by recipients who have
received the session data.
### Advancing the ratchet
After each message is encrypted, the ratchet is advanced. This is done as
described in [The Megolm ratchet algorithm](#the-megolm-ratchet-algorithm), using the following definitions:
```math
\begin{aligned}
H_0(A) &\equiv \operatorname{HMAC}(A,\text{``\char`\\x00"}) \\
H_1(A) &\equiv \operatorname{HMAC}(A,\text{``\char`\\x01"}) \\
H_2(A) &\equiv \operatorname{HMAC}(A,\text{``\char`\\x02"}) \\
H_3(A) &\equiv \operatorname{HMAC}(A,\text{``\char`\\x03"}) \\
\end{aligned}
```
where $`\operatorname{HMAC}(A, T)`$ is the HMAC-SHA-256 of ``T``, using ``A`` as the
key.
For outbound sessions, the updated ratchet and counter are stored in the
session.
In order to maintain the ability to decrypt conversation history, inbound
sessions should store a copy of their earliest known ratchet value (unless they
explicitly want to drop the ability to decrypt that history - see [Partial
Forward Secrecy](#partial-forward-secrecy)). They may also choose to cache calculated ratchet values,
but the decision of which ratchet states to cache is left to the application.
## Data exchange formats
### Session sharing format
This format is used for the initial sharing of a Megolm session with other
group participants who need to be able to read messages encrypted by this
session.
The session sharing format is as follows:
```
+---+----+--------+--------+--------+--------+------+-----------+
| V | i | R(i,0) | R(i,1) | R(i,2) | R(i,3) | Kpub | Signature |
+---+----+--------+--------+--------+--------+------+-----------+
0 1 5 37 69 101 133 165 229 bytes
```
The version byte, ``V``, is ``"\x02"``.
This is followed by the ratchet index, $`i`$, which is encoded as a
big-endian 32-bit integer; the ratchet values $`R_{i,j}`$; and the public
part of the Ed25519 keypair $`K`$.
The data is then signed using the Ed25519 keypair, and the 64-byte signature is
appended.
### Session export format
Once the session is initially shared with the group participants, each
participant needs to retain a copy of the session if they want to maintain
their ability to decrypt messages encrypted with that session.
For forward-secrecy purposes, a participant may choose to store a ratcheted
version of the session. But since the ratchet index is covered by the
signature, this would invalidate the signature. So we define a similar format,
called the *session export format*, which is identical to the [session sharing
format](#session-sharing-format) except for dropping the signature.
The Megolm session export format is thus as follows:
```
+---+----+--------+--------+--------+--------+------+
| V | i | R(i,0) | R(i,1) | R(i,2) | R(i,3) | Kpub |
+---+----+--------+--------+--------+--------+------+
0 1 5 37 69 101 133 165 bytes
```
The version byte, ``V``, is ``"\x01"``.
This is followed by the ratchet index, $`i`$, which is encoded as a
big-endian 32-bit integer; the ratchet values $`R_{i,j}`$; and the public
part of the Ed25519 keypair $`K`$.
### Message format
Megolm messages consist of a one byte version, followed by a variable length
payload, a fixed length message authentication code, and a fixed length
signature.
```
+---+------------------------------------+-----------+------------------+
| V | Payload Bytes | MAC Bytes | Signature Bytes |
+---+------------------------------------+-----------+------------------+
0 1 N N+8 N+72 bytes
```
The version byte, ``V``, is ``"\x03"``.
The payload uses a format based on the [Protocol Buffers encoding][]. It
consists of the following key-value pairs:
**Name**|**Tag**|**Type**|**Meaning**
:-----:|:-----:|:-----:|:-----:
Message-Index|0x08|Integer|The index of the ratchet, i
Cipher-Text|0x12|String|The cipher-text, Xi, of the message
Within the payload, integers are encoded using a variable length encoding. Each
integer is encoded as a sequence of bytes with the high bit set followed by a
byte with the high bit clear. The seven low bits of each byte store the bits of
the integer. The least significant bits are stored in the first byte.
Strings are encoded as a variable-length integer followed by the string itself.
Each key-value pair is encoded as a variable-length integer giving the tag,
followed by a string or variable-length integer giving the value.
The payload is followed by the MAC. The length of the MAC is determined by the
authenticated encryption algorithm being used (8 bytes in this version of the
protocol). The MAC protects all of the bytes preceding the MAC.
The length of the signature is determined by the signing algorithm being used
(64 bytes in this version of the protocol). The signature covers all of the
bytes preceding the signature.
## Limitations
### Message Replays
A message can be decrypted successfully multiple times. This means that an
attacker can re-send a copy of an old message, and the recipient will treat it
as a new message.
To mitigate this it is recommended that applications track the ratchet indices
they have received and that they reject messages with a ratchet index that
they have already decrypted.
### Lack of Transcript Consistency
In a group conversation, there is no guarantee that all recipients have
received the same messages. For example, if Alice is in a conversation with Bob
and Charlie, she could send different messages to Bob and Charlie, or could
send some messages to Bob but not Charlie, or vice versa.
Solving this is, in general, a hard problem, particularly in a protocol which
does not guarantee in-order message delivery. For now it remains the subject of
future research.
### Lack of Backward Secrecy
[Backward secrecy](https://intensecrypto.org/public/lec_08_hash_functions_part2.html#sec-forward-and-backward-secrecy)
(also called 'future secrecy' or 'post-compromise security') is the property
that if current private keys are compromised, an attacker cannot decrypt
future messages in a given session. In other words, when looking
**backwards** in time at a compromise which has already happened, **current**
messages are still secret.
By itself, Megolm does not possess this property: once the key to a Megolm
session is compromised, the attacker can decrypt any message that was
encrypted using a key derived from the compromised or subsequent ratchet
values.
In order to mitigate this, the application should ensure that Megolm sessions
are not used indefinitely. Instead it should periodically start a new session,
with new keys shared over a secure channel.
<!-- TODO: Can we recommend sensible lifetimes for Megolm sessions? Probably
depends how paranoid we're feeling, but some guidelines might be useful. -->
### Partial Forward Secrecy
[Forward secrecy](https://intensecrypto.org/public/lec_08_hash_functions_part2.html#sec-forward-and-backward-secrecy)
(also called 'perfect forward secrecy') is the property that if the current
private keys are compromised, an attacker cannot decrypt *past* messages in
a given session. In other words, when looking **forwards** in time towards a
potential future compromise, **current** messages will be secret.
In Megolm, each recipient maintains a record of the ratchet value which allows
them to decrypt any messages sent in the session after the corresponding point
in the conversation. If this value is compromised, an attacker can similarly
decrypt past messages which were encrypted by a key derived from the
compromised or subsequent ratchet values. This gives 'partial' forward
secrecy.
To mitigate this issue, the application should offer the user the option to
discard historical conversations, by winding forward any stored ratchet values,
or discarding sessions altogether.
### Dependency on secure channel for key exchange
The design of the Megolm ratchet relies on the availability of a secure
peer-to-peer channel for the exchange of session keys. Any vulnerabilities in
the underlying channel are likely to be amplified when applied to Megolm
session setup.
For example, if the peer-to-peer channel is vulnerable to an unknown key-share
attack, the entire Megolm session become similarly vulnerable. For example:
Alice starts a group chat with Eve, and shares the session keys with Eve. Eve
uses the unknown key-share attack to forward the session keys to Bob, who
believes Alice is starting the session with him. Eve then forwards messages
from the Megolm session to Bob, who again believes they are coming from
Alice. Provided the peer-to-peer channel is not vulnerable to this attack, Bob
will realise that the key-sharing message was forwarded by Eve, and can treat
the Megolm session as a forgery.
A second example: if the peer-to-peer channel is vulnerable to a replay
attack, this can be extended to entire Megolm sessions.
## License
The Megolm specification (this document) is licensed under the Apache License,
Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.
[Ed25519]: http://ed25519.cr.yp.to/
[HMAC-based key derivation function]: https://tools.ietf.org/html/rfc5869
[HKDF-SHA-256]: https://tools.ietf.org/html/rfc5869
[HMAC-SHA-256]: https://tools.ietf.org/html/rfc2104
[SHA-256]: https://tools.ietf.org/html/rfc6234
[AES-256]: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
[CBC]: http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
[PKCS#7]: https://tools.ietf.org/html/rfc2315
[Olm]: https://gitlab.matrix.org/matrix-org/olm/blob/master/docs/olm.md
[Protocol Buffers encoding]: https://developers.google.com/protocol-buffers/docs/encoding

331
docs/olm.md Normal file
View File

@ -0,0 +1,331 @@
# Olm: A Cryptographic Ratchet
An implementation of the double cryptographic ratchet described by
https://whispersystems.org/docs/specifications/doubleratchet/.
## Notation
This document uses $`\parallel`$ to represent string concatenation. When
$`\parallel`$ appears on the right hand side of an $`=`$ it means that
the inputs are concatenated. When $`\parallel`$ appears on the left hand
side of an $`=`$ it means that the output is split.
When this document uses $`\operatorname{ECDH}\left(K_A,K_B\right)`$ it means
that each party computes a Diffie-Hellman agreement using their private key
and the remote party's public key.
So party $`A`$ computes $`\operatorname{ECDH}\left(K_B^{public},K_A^{private}\right)`$
and party $`B`$ computes $`\operatorname{ECDH}\left(K_A^{public},K_B^{private}\right)`$.
Where this document uses $`\operatorname{HKDF}\left(salt,IKM,info,L\right)`$ it
refers to the [HMAC-based key derivation function][] with a salt value of
$`salt`$, input key material of $`IKM`$, context string $`info`$,
and output keying material length of $`L`$ bytes.
## The Olm Algorithm
### Initial setup
The setup takes four [Curve25519][] inputs: Identity keys for Alice and Bob,
$`I_A`$ and $`I_B`$, and one-time keys for Alice and Bob,
$`E_A`$ and $`E_B`$. A shared secret, $`S`$, is generated using
[Triple Diffie-Hellman][]. The initial 256 bit root key, $`R_0`$, and 256
bit chain key, $`C_{0,0}`$, are derived from the shared secret using an
HMAC-based Key Derivation Function using [SHA-256][] as the hash function
([HKDF-SHA-256][]) with default salt and ``"OLM_ROOT"`` as the info.
```math
\begin{aligned}
S&=\operatorname{ECDH}\left(I_A,E_B\right)\;\parallel\;
\operatorname{ECDH}\left(E_A,I_B\right)\;\parallel\;
\operatorname{ECDH}\left(E_A,E_B\right)\\
R_0\;\parallel\;C_{0,0}&=
\operatorname{HKDF}\left(0,S,\text{``OLM\_ROOT"},64\right)
\end{aligned}
```
### Advancing the root key
Advancing a root key takes the previous root key, $`R_{i-1}`$, and two
Curve25519 inputs: the previous ratchet key, $`T_{i-1}`$, and the current
ratchet key $`T_i`$. The even ratchet keys are generated by Alice.
The odd ratchet keys are generated by Bob. A shared secret is generated
using Diffie-Hellman on the ratchet keys. The next root key, $`R_i`$, and
chain key, $`C_{i,0}`$, are derived from the shared secret using
[HKDF-SHA-256][] using $`R_{i-1}`$ as the salt and ``"OLM_RATCHET"`` as the
info.
```math
\begin{aligned}
R_i\;\parallel\;C_{i,0}&=
\operatorname{HKDF}\left(
R_{i-1},
\operatorname{ECDH}\left(T_{i-1},T_i\right),
\text{``OLM\_RATCHET"},
64
\right)
\end{aligned}
```
### Advancing the chain key
Advancing a chain key takes the previous chain key, $`C_{i,j-1}`$. The next
chain key, $`C_{i,j}`$, is the [HMAC-SHA-256][] of ``"\x02"`` using the
previous chain key as the key.
```math
\begin{aligned}
C_{i,j}&=\operatorname{HMAC}\left(C_{i,j-1},\text{``\char`\\x02"}\right)
\end{aligned}
```
### Creating a message key
Creating a message key takes the current chain key, $`C_{i,j}`$. The
message key, $`M_{i,j}`$, is the [HMAC-SHA-256][] of ``"\x01"`` using the
current chain key as the key. The message keys where $`i`$ is even are used
by Alice to encrypt messages. The message keys where $`i`$ is odd are used
by Bob to encrypt messages.
```math
\begin{aligned}
M_{i,j}&=\operatorname{HMAC}\left(C_{i,j},\text{``\char`\\x01"}\right)
\end{aligned}
```
## The Olm Protocol
### Creating an outbound session
Bob publishes the public parts of his identity key, $`I_B`$, and some
single-use one-time keys $`E_B`$.
Alice downloads Bob's identity key, $`I_B`$, and a one-time key,
$`E_B`$. She generates a new single-use key, $`E_A`$, and computes a
root key, $`R_0`$, and a chain key $`C_{0,0}`$. She also generates a
new ratchet key $`T_0`$.
### Sending the first pre-key messages
Alice computes a message key, $`M_{0,j}`$, and a new chain key,
$`C_{0,j+1}`$, using the current chain key. She replaces the current chain
key with the new one.
Alice encrypts her plain-text with the message key, $`M_{0,j}`$, using an
authenticated encryption scheme (see below) to get a cipher-text,
$`X_{0,j}`$.
She then sends the following to Bob:
* The public part of her identity key, $`I_A`$
* The public part of her single-use key, $`E_A`$
* The public part of Bob's single-use key, $`E_B`$
* The current chain index, $`j`$
* The public part of her ratchet key, $`T_0`$
* The cipher-text, $`X_{0,j}`$
Alice will continue to send pre-key messages until she receives a message from
Bob.
### Creating an inbound session from a pre-key message
Bob receives a pre-key message as above.
Bob looks up the private part of his single-use key, $`E_B`$. He can now
compute the root key, $`R_0`$, and the chain key, $`C_{0,0}`$, from
$`I_A`$, $`E_A`$, $`I_B`$, and $`E_B`$.
Bob then advances the chain key $`j`$ times, to compute the chain key used
by the message, $`C_{0,j}`$. He now creates the
message key, $`M_{0,j}`$, and attempts to decrypt the cipher-text,
$`X_{0,j}`$. If the cipher-text's authentication is correct then Bob can
discard the private part of his single-use one-time key, $`E_B`$.
Bob stores Alice's initial ratchet key, $`T_0`$, until he wants to
send a message.
### Sending normal messages
Once a message has been received from the other side, a session is considered
established, and a more compact form is used.
To send a message, the user checks if they have a sender chain key,
$`C_{i,j}`$. Alice uses chain keys where $`i`$ is even. Bob uses chain
keys where $`i`$ is odd. If the chain key doesn't exist then a new ratchet
key $`T_i`$ is generated and a new root key $`R_i`$ and chain key
$`C_{i,0}`$ are computed using $`R_{i-1}`$, $`T_{i-1}`$ and
$`T_i`$.
A message key,
$`M_{i,j}`$ is computed from the current chain key, $`C_{i,j}`$, and
the chain key is replaced with the next chain key, $`C_{i,j+1}`$. The
plain-text is encrypted with $`M_{i,j}`$, using an authenticated encryption
scheme (see below) to get a cipher-text, $`X_{i,j}`$.
The user then sends the following to the recipient:
* The current chain index, $`j`$
* The public part of the current ratchet key, $`T_i`$
* The cipher-text, $`X_{i,j}`$
### Receiving messages
The user receives a message as above with the sender's current chain index, $`j`$,
the sender's ratchet key, $`T_i`$, and the cipher-text, $`X_{i,j}`$.
The user checks if they have a receiver chain with the correct
$`i`$ by comparing the ratchet key, $`T_i`$. If the chain doesn't exist
then they compute a new root key, $`R_i`$, and a new receiver chain, with
chain key $`C_{i,0}`$, using $`R_{i-1}`$, $`T_{i-1}`$ and
$`T_i`$.
If the $`j`$ of the message is less than
the current chain index on the receiver then the message may only be decrypted
if the receiver has stored a copy of the message key $`M_{i,j}`$. Otherwise
the receiver computes the chain key, $`C_{i,j}`$. The receiver computes the
message key, $`M_{i,j}`$, from the chain key and attempts to decrypt the
cipher-text, $`X_{i,j}`$.
If the decryption succeeds the receiver updates the chain key for $`T_i`$
with $`C_{i,j+1}`$ and stores the message keys that were skipped in the
process so that they can decode out of order messages. If the receiver created
a new receiver chain then they discard their current sender chain so that
they will create a new chain when they next send a message.
## The Olm Message Format
Olm uses two types of messages. The underlying transport protocol must provide
a means for recipients to distinguish between them.
### Normal Messages
Olm messages start with a one byte version followed by a variable length
payload followed by a fixed length message authentication code.
```
+--------------+------------------------------------+-----------+
| Version Byte | Payload Bytes | MAC Bytes |
+--------------+------------------------------------+-----------+
```
The version byte is ``"\x03"``.
The payload consists of key-value pairs where the keys are integers and the
values are integers and strings. The keys are encoded as a variable length
integer tag where the 3 lowest bits indicates the type of the value:
0 for integers, 2 for strings. If the value is an integer then the tag is
followed by the value encoded as a variable length integer. If the value is
a string then the tag is followed by the length of the string encoded as
a variable length integer followed by the string itself.
Olm uses a variable length encoding for integers. Each integer is encoded as a
sequence of bytes with the high bit set followed by a byte with the high bit
clear. The seven low bits of each byte store the bits of the integer. The least
significant bits are stored in the first byte.
**Name**|**Tag**|**Type**|**Meaning**
:-----:|:-----:|:-----:|:-----:
Ratchet-Key|0x0A|String|The public part of the ratchet key, Ti, of the message
Chain-Index|0x10|Integer|The chain index, j, of the message
Cipher-Text|0x22|String|The cipher-text, Xi,j, of the message
The length of the MAC is determined by the authenticated encryption algorithm
being used. (Olm version 1 uses [HMAC-SHA-256][], truncated to 8 bytes). The
MAC protects all of the bytes preceding the MAC.
### Pre-Key Messages
Olm pre-key messages start with a one byte version followed by a variable
length payload.
```
+--------------+------------------------------------+
| Version Byte | Payload Bytes |
+--------------+------------------------------------+
```
The version byte is ``"\x03"``.
The payload uses the same key-value format as for normal messages.
**Name**|**Tag**|**Type**|**Meaning**
:-----:|:-----:|:-----:|:-----:
One-Time-Key|0x0A|String|The public part of Bob's single-use key, Eb.
Base-Key|0x12|String|The public part of Alice's single-use key, Ea.
Identity-Key|0x1A|String|The public part of Alice's identity key, Ia.
Message|0x22|String|An embedded Olm message with its own version and MAC.
## Olm Authenticated Encryption
### Version 1
Version 1 of Olm uses [AES-256][] in [CBC][] mode with [PKCS#7][] padding for
encryption and [HMAC-SHA-256][] (truncated to 64 bits) for authentication. The
256 bit AES key, 256 bit HMAC key, and 128 bit AES IV are derived from the
message key using [HKDF-SHA-256][] using the default salt and an info of
``"OLM_KEYS"``.
```math
\begin{aligned}
AES\_KEY_{i,j}\;\parallel\;HMAC\_KEY_{i,j}\;\parallel\;AES\_IV_{i,j}
&= \operatorname{HKDF}\left(0,M_{i,j},\text{``OLM\_KEYS"},80\right)
\end{aligned}
```
The plain-text is encrypted with AES-256, using the key $`AES\_KEY_{i,j}`$
and the IV $`AES\_IV_{i,j}`$ to give the cipher-text, $`X_{i,j}`$.
Then the entire message (including the Version Byte and all Payload Bytes) are
passed through [HMAC-SHA-256][]. The first 8 bytes of the MAC are appended to the message.
## Message authentication concerns
To avoid unknown key-share attacks, the application must include identifying
data for the sending and receiving user in the plain-text of (at least) the
pre-key messages. Such data could be a user ID, a telephone number;
alternatively it could be the public part of a keypair which the relevant user
has proven ownership of.
### Example attacks
1. Alice publishes her public [Curve25519][] identity key, $`I_A`$. Eve
publishes the same identity key, claiming it as her own. Bob downloads
Eve's keys, and associates $`I_A`$ with Eve. Alice sends a message to
Bob; Eve intercepts it before forwarding it to Bob. Bob believes the
message came from Eve rather than Alice.
This is prevented if Alice includes her user ID in the plain-text of the
pre-key message, so that Bob can see that the message was sent by Alice
originally.
2. Bob publishes his public [Curve25519][] identity key, $`I_B`$. Eve
publishes the same identity key, claiming it as her own. Alice downloads
Eve's keys, and associates $`I_B`$ with Eve. Alice sends a message to
Eve; Eve cannot decrypt it, but forwards it to Bob. Bob believes the
Alice sent the message to him, wheras Alice intended it to go to Eve.
This is prevented by Alice including the user ID of the intended recpient
(Eve) in the plain-text of the pre-key message. Bob can now tell that the
message was meant for Eve rather than him.
## IPR
The Olm specification (this document) is hereby placed in the public domain.
## Feedback
Can be sent to olm at matrix.org.
## Acknowledgements
The ratchet that Olm implements was designed by Trevor Perrin and Moxie
Marlinspike - details at https://whispersystems.org/docs/specifications/doubleratchet/. Olm is
an entirely new implementation written by the Matrix.org team.
[Curve25519]: http://cr.yp.to/ecdh.html
[Triple Diffie-Hellman]: https://whispersystems.org/blog/simplifying-otr-deniability/
[HMAC-based key derivation function]: https://tools.ietf.org/html/rfc5869
[HKDF-SHA-256]: https://tools.ietf.org/html/rfc5869
[HMAC-SHA-256]: https://tools.ietf.org/html/rfc2104
[SHA-256]: https://tools.ietf.org/html/rfc6234
[AES-256]: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
[CBC]: http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
[PKCS#7]: https://tools.ietf.org/html/rfc2315

View File

@ -1,326 +0,0 @@
Olm: A Cryptographic Ratchet
============================
An implementation of the double cryptographic ratchet described by
https://github.com/trevp/double_ratchet/wiki.
Notation
--------
This document uses :math:`\parallel` to represent string concatenation. When
:math:`\parallel` appears on the right hand side of an :math:`=` it means that
the inputs are concatenated. When :math:`\parallel` appears on the left hand
side of an :math:`=` it means that the output is split.
When this document uses :math:`ECDH\left(K_A,\,K_B\right)` it means that each
party computes a Diffie-Hellman agreement using their private key and the
remote party's public key.
So party :math:`A` computes :math:`ECDH\left(K_B_public,\,K_A_private\right)`
and party :math:`B` computes :math:`ECDH\left(K_A_public,\,K_B_private\right)`.
Where this document uses :math:`HKDF\left(salt,\,IKM,\,info,\,L\right)` it
refers to the `HMAC-based key derivation function`_ with a salt value of
:math:`salt`, input key material of :math:`IKM`, context string :math:`info`,
and output keying material length of :math:`L` bytes.
The Olm Algorithm
-----------------
Initial setup
~~~~~~~~~~~~~
The setup takes four Curve25519_ inputs: Identity keys for Alice and Bob,
:math:`I_A` and :math:`I_B`, and ephemeral keys for Alice and Bob,
:math:`E_A` and :math:`E_B`. A shared secret, :math:`S`, is generated using
`Triple Diffie-Hellman`_. The initial 256 bit root key, :math:`R_0`, and 256
bit chain key, :math:`C_{0,0}`, are derived from the shared secret using an
HMAC-based Key Derivation Function using SHA-256_ as the hash function
(HKDF-SHA-256_) with default salt and ``"OLM_ROOT"`` as the info.
.. math::
\begin{align}
S&=ECDH\left(I_A,\,E_B\right)\;\parallel\;ECDH\left(E_A,\,I_B\right)\;
\parallel\;ECDH\left(E_A,\,E_B\right)\\
R_0\;\parallel\;C_{0,0}&=
HKDF\left(0,\,S,\,\text{"OLM\_ROOT"},\,64\right)
\end{align}
Advancing the root key
~~~~~~~~~~~~~~~~~~~~~~
Advancing a root key takes the previous root key, :math:`R_{i-1}`, and two
Curve25519 inputs: the previous ratchet key, :math:`T_{i-1}`, and the current
ratchet key :math:`T_i`. The even ratchet keys are generated by Alice.
The odd ratchet keys are generated by Bob. A shared secret is generated
using Diffie-Hellman on the ratchet keys. The next root key, :math:`R_i`, and
chain key, :math:`C_{i,0}`, are derived from the shared secret using
HKDF-SHA-256_ using :math:`R_{i-1}` as the salt and ``"OLM_RATCHET"`` as the
info.
.. math::
\begin{align}
R_i\;\parallel\;C_{i,0}&=HKDF\left(
R_{i-1},\,
ECDH\left(T_{i-1},\,T_i\right),\,
\text{"OLM\_RATCHET"},\,
64
\right)
\end{align}
Advancing the chain key
~~~~~~~~~~~~~~~~~~~~~~~
Advancing a chain key takes the previous chain key, :math:`C_{i,j-i}`. The next
chain key, :math:`C_{i,j}`, is the HMAC-SHA-256_ of ``"\x02"`` using the
previous chain key as the key.
.. math::
\begin{align}
C_{i,j}&=HMAC\left(C_{i,j-1},\,\text{"\textbackslash x02"}\right)
\end{align}
Creating a message key
~~~~~~~~~~~~~~~~~~~~~~
Creating a message key takes the current chain key, :math:`C_{i,j}`. The
message key, :math:`M_{i,j}`, is the HMAC-SHA-256_ of ``"\x01"`` using the
current chain key as the key. The message keys where :math:`i` is even are used
by Alice to encrypt messages. The message keys where :math:`i` is odd are used
by Bob to encrypt messages.
.. math::
\begin{align}
M_{i,j}&=HMAC\left(C_{i,j},\,\text{"\textbackslash x01"}\right)
\end{align}
The Olm Protocol
----------------
Creating an outbound session
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Bob publishes the public parts of his identity key, :math:`I_B`, and some
single-use one-time keys :math:`E_B`.
Alice downloads Bob's identity key, :math:`I_B`, and a one-time key,
:math:`E_B`. She generates a new single-use key, :math:`E_A`, and computes a
root key, :math:`R_0`, and a chain key :math:`C_{0,0}`. She also generates a
new ratchet key :math:`T_0`.
Sending the first pre-key messages
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Alice computes a message key, :math:`M_{0,j}`, and a new chain key,
:math:`C_{0,j+1}`, using the current chain key. She replaces the current chain
key with the new one.
Alice encrypts her plain-text with the message key, :math:`M_{0,j}`, using an
authenticated encryption scheme (see below) to get a cipher-text,
:math:`X_{0,j}`.
She then sends the following to Bob:
* The public part of her identity key, :math:`I_A`
* The public part of her single-use key, :math:`E_A`
* The public part of Bob's single-use key, :math:`E_B`
* The current chain index, :math:`j`
* The public part of her ratchet key, :math:`T_0`
* The cipher-text, :math:`X_{0,j}`
Alice will continue to send pre-key messages until she receives a message from
Bob.
Creating an inbound session from a pre-key message
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Bob receives a pre-key message as above.
Bob looks up the private part of his single-use key, :math:`E_B`. He can now
compute the root key, :math:`R_0`, and the chain key, :math:`C_{0,0}`, from
:math:`I_A`, :math:`E_A`, :math:`I_B`, and :math:`E_B`.
Bob then advances the chain key :math:`j` times, to compute the chain key used
by the message, :math:`C_{0,j}`. He now creates the
message key, :math:`M_{0,j}`, and attempts to decrypt the cipher-text,
:math:`X_{0,j}`. If the cipher-text's authentication is correct then Bob can
discard the private part of his single-use one-time key, :math:`E_B`.
Bob stores Alice's initial ratchet key, :math:`T_0`, until he wants to
send a message.
Sending normal messages
~~~~~~~~~~~~~~~~~~~~~~~
Once a message has been received from the other side, a session is considered
established, and a more compact form is used.
To send a message, the user checks if they have a sender chain key,
:math:`C_{i,j}`. Alice uses chain keys where :math:`i` is even. Bob uses chain
keys where :math:`i` is odd. If the chain key doesn't exist then a new ratchet
key :math:`T_i` is generated and a new root key :math:`R_i` and chain key
:math:`C_{i,0}` are computed using :math:`R_{i-1}`, :math:`T_{i-1}` and
:math:`T_i`.
A message key,
:math:`M_{i,j}` is computed from the current chain key, :math:`C_{i,j}`, and
the chain key is replaced with the next chain key, :math:`C_{i,j+1}`. The
plain-text is encrypted with :math:`M_{i,j}`, using an authenticated encryption
scheme (see below) to get a cipher-text, :math:`X_{i,j}`.
The user then sends the following to the recipient:
* The current chain index, :math:`j`
* The public part of the current ratchet key, :math:`T_i`
* The cipher-text, :math:`X_{i,j}`
Receiving messages
~~~~~~~~~~~~~~~~~~
The user receives a message as above with the sender's current chain index, :math:`j`,
the sender's ratchet key, :math:`T_i`, and the cipher-text, :math:`X_{i,j}`.
The user checks if they have a receiver chain with the correct
:math:`i` by comparing the ratchet key, :math:`T_i`. If the chain doesn't exist
then they compute a new root key, :math:`R_i`, and a new receiver chain, with
chain key :math:`C_{i,0}`, using :math:`R_{i-1}`, :math:`T_{i-1}` and
:math:`T_i`.
If the :math:`j` of the message is less than
the current chain index on the receiver then the message may only be decrypted
if the receiver has stored a copy of the message key :math:`M_{i,j}`. Otherwise
the receiver computes the chain key, :math:`C_{i,j}`. The receiver computes the
message key, :math:`M_{i,j}`, from the chain key and attempts to decrypt the
cipher-text, :math:`X_{i,j}`.
If the decryption succeeds the receiver updates the chain key for :math:`T_i`
with :math:`C_{i,j+1}` and stores the message keys that were skipped in the
process so that they can decode out of order messages. If the receiver created
a new receiver chain then they discard their current sender chain so that
they will create a new chain when they next send a message.
The Olm Message Format
----------------------
Olm uses two types of messages. The underlying transport protocol must provide
a means for recipients to distinguish between them.
Normal Messages
~~~~~~~~~~~~~~~
Olm messages start with a one byte version followed by a variable length
payload followed by a fixed length message authentication code.
.. code::
+--------------+------------------------------------+-----------+
| Version Byte | Payload Bytes | MAC Bytes |
+--------------+------------------------------------+-----------+
The version byte is ``"\x03"``.
The payload consists of key-value pairs where the keys are integers and the
values are integers and strings. The keys are encoded as a variable length
integer tag where the 3 lowest bits indicates the type of the value:
0 for integers, 2 for strings. If the value is an integer then the tag is
followed by the value encoded as a variable length integer. If the value is
a string then the tag is followed by the length of the string encoded as
a variable length integer followed by the string itself.
Olm uses a variable length encoding for integers. Each integer is encoded as a
sequence of bytes with the high bit set followed by a byte with the high bit
clear. The seven low bits of each byte store the bits of the integer. The least
significant bits are stored in the first byte.
=========== ===== ======== ================================================
Name Tag Type Meaning
=========== ===== ======== ================================================
Ratchet-Key 0x0A String The public part of the ratchet key, :math:`T_{i}`,
of the message
Chain-Index 0x10 Integer The chain index, :math:`j`, of the message
Cipher-Text 0x22 String The cipher-text, :math:`X_{i,j}`, of the message
=========== ===== ======== ================================================
The length of the MAC is determined by the authenticated encryption algorithm
being used. (Olm version 1 uses HMAC-SHA-256, truncated to 8 bytes). The
MAC protects all of the bytes preceding the MAC.
Pre-Key Messages
~~~~~~~~~~~~~~~~
Olm pre-key messages start with a one byte version followed by a variable
length payload.
.. code::
+--------------+------------------------------------+
| Version Byte | Payload Bytes |
+--------------+------------------------------------+
The version byte is ``"\x03"``.
The payload uses the same key-value format as for normal messages.
============ ===== ======== ================================================
Name Tag Type Meaning
============ ===== ======== ================================================
One-Time-Key 0x0A String The public part of Bob's single-use key,
:math:`E_b`.
Base-Key 0x12 String The public part of Alice's single-use key,
:math:`E_a`.
Identity-Key 0x1A String The public part of Alice's identity key,
:math:`I_a`.
Message 0x22 String An embedded Olm message with its own version and
MAC.
============ ===== ======== ================================================
Olm Authenticated Encryption
----------------------------
Version 1
~~~~~~~~~
Version 1 of Olm uses AES-256_ in CBC_ mode with `PCKS#7`_ padding for
encryption and HMAC-SHA-256_ (truncated to 64 bits) for authentication. The
256 bit AES key, 256 bit HMAC key, and 128 bit AES IV are derived from the
message key using HKDF-SHA-256_ using the default salt and an info of
``"OLM_KEYS"``.
.. math::
\begin{align}
AES\_KEY_{i,j}\;\parallel\;HMAC\_KEY_{i,j}\;\parallel\;AES\_IV_{i,j}
&= HKDF\left(0,\,M_{i,j},\text{"OLM\_KEYS"},\,80\right) \\
\end{align}
The plain-text is encrypted with AES-256, using the key :math:`AES\_KEY_{i,j}`
and the IV :math:`AES\_IV_{i,j}` to give the cipher-text, :math:`X_{i,j}`.
Then the entire message (including the Version Byte and all Payload Bytes) are
passed through HMAC-SHA-256. The first 8 bytes of the MAC are appended to the message.
IPR
---
The Olm specification (this document) is hereby placed in the public domain.
Feedback
--------
Can be sent to mark at matrix.org.
Acknowledgements
----------------
The ratchet that Olm implements was designed by Trevor Perrin and Moxie
Marlinspike - details at https://github.com/trevp/double_ratchet/wiki. Olm is
an entirely new implementation written by the Matrix.org team.
.. _`Curve25519`: http://cr.yp.to/ecdh.html
.. _`Triple Diffie-Hellman`: https://whispersystems.org/blog/simplifying-otr-deniability/
.. _`HMAC-based key derivation function`: https://tools.ietf.org/html/rfc5869
.. _`HKDF-SHA-256`: https://tools.ietf.org/html/rfc5869
.. _`HMAC-SHA-256`: https://tools.ietf.org/html/rfc2104
.. _`SHA-256`: https://tools.ietf.org/html/rfc6234
.. _`AES-256`: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
.. _`CBC`: http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
.. _`PCKS#7`: https://tools.ietf.org/html/rfc2315

104
docs/signing.md Normal file
View File

@ -0,0 +1,104 @@
# Signature keys and user identity in libolm
The use of any public-key based cryptography system such as Olm presents the
need for our users Alice and Bob to verify that they are in fact communicating
with each other, rather than a man-in-the-middle. Typically this requires an
out-of-band process in which Alice and Bob verify that they have the correct
public keys for each other. For example, this might be done via physical
presence or via a voice call.
In the basic [Olm][] protocol, it is sufficient to compare the public
Curve25519 identity keys. As a naive example, Alice would meet Bob and ensure
that the identity key she downloaded from the key server matched that shown by
his device. This prevents the eavesdropper Eve from decrypting any messages
sent from Alice to Bob, or from masquerading as Bob to send messages to Alice:
she has neither Alice's nor Bob's private identity key, so cannot successfully
complete the triple-DH calculation to compute the shared secret, $`S`$,
which in turn prevents her decrypting intercepted messages, or from creating
new messages with valid MACs. Obviously, for protection to be complete, Bob
must similarly verify Alice's key.
However, the use of the Curve25519 key as the "fingerprint" in this way makes
it difficult to carry out signing operations. For instance, it may be useful to
cross-sign identity keys for different devices, or, as discussed below, to sign
one-time keys. Curve25519 keys are intended for use in DH calculations, and
their use to calculate signatures is non-trivial.
The solution adopted in this library is to generate a signing key for each
user. This is an [Ed25519][] keypair, which is used to calculate a signature on
an object including both the public Ed25519 signing key and the public
Curve25519 identity key. It is then the **public Ed25519 signing key** which is
used as the device fingerprint which Alice and Bob verify with each other.
By verifying the signatures on the key object, Alice and Bob then get the same
level of assurance about the ownership of the Curve25519 identity keys as if
they had compared those directly.
## Signing one-time keys
The Olm protocol requires users to publish a set of one-time keys to a key
server. To establish an Olm session, the originator downloads a key for the
recipient from this server. The decision of whether to sign these one-time keys
is left to the application. There are both advantages and disadvantages to
doing so.
Consider the scenario where one-time keys are unsigned. Alice wants to initiate
an Olm session with Bob. Bob uploads his one-time keys, $`E_B`$, but Eve
replaces them with ones she controls, $`E_E`$. Alice downloads one of the
compromised keys, and sends a pre-key message using a shared secret $`S`$,
where:
```math
S = \operatorname{ECDH}\left(I_A,E_E\right)\;\parallel\;
\operatorname{ECDH}\left(E_A,I_B\right)\;\parallel\;
\operatorname{ECDH}\left(E_A,E_E\right)
```
Eve cannot decrypt the message because she does not have the private parts of
either $`E_A`$ nor $`I_B`$, so cannot calculate
$`ECDH\left(E_A,I_B\right)`$. However, suppose she later compromises
Bob's identity key $`I_B`$. This would give her the ability to decrypt any
pre-key messages sent to Bob using the compromised one-time keys, and is thus a
problematic loss of forward secrecy. If Bob signs his keys with his Ed25519
signing key (and Alice verifies the signature before using them), this problem
is avoided.
On the other hand, signing the one-time keys leads to a reduction in
deniability. Recall that the shared secret is calculated as follows:
```math
S = \operatorname{ECDH}\left(I_A,E_B\right)\;\parallel\;
\operatorname{ECDH}\left(E_A,I_B\right)\;\parallel\;
\operatorname{ECDH}\left(E_A,E_B\right)
```
If keys are unsigned, a forger can make up values of $`E_A`$ and
$`E_B`$, and construct a transcript of a conversation which looks like it
was between Alice and Bob. Alice and Bob can therefore plausibly deny their
participation in any conversation even if they are both forced to divulge their
private identity keys, since it is impossible to prove that the transcript was
a conversation between the two of them, rather than constructed by a forger.
If $`E_B`$ is signed, it is no longer possible to construct arbitrary
transcripts. Given a transcript and Alice and Bob's identity keys, we can now
show that at least one of Alice or Bob was involved in the conversation,
because the ability to calculate $`\operatorname{ECDH}\left(I_A,E_B\right)`$ requires
knowledge of the private parts of either $`I_A`$ (proving Alice's
involvement) or $`E_B`$ (proving Bob's involvement, via the
signature). Note that it remains impossible to show that *both* Alice and Bob
were involved.
In conclusion, applications should consider whether to sign one-time keys based
on the trade-off between forward secrecy and deniability.
## License
This document is licensed under the Apache License, Version 2.0
http://www.apache.org/licenses/LICENSE-2.0.
## Feedback
Questions and feedback can be sent to olm at matrix.org.
[Ed25519]: http://ed25519.cr.yp.to/
[Olm]: https://gitlab.matrix.org/matrix-org/olm/blob/master/docs/olm.md

18
exports.py Executable file
View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
import sys
import re
import json
expr = re.compile(r"(_*olm_[^( ]*)\(")
exports = {'_free', '_malloc'}
for f in sys.argv[1:]:
with open(f) as fp:
for line in fp:
matches = expr.search(line)
if matches is not None:
exports.add('_%s' % (matches.group(1),))
json.dump(sorted(exports), sys.stdout)

60
flake.lock Normal file
View File

@ -0,0 +1,60 @@
{
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1664871473,
"narHash": "sha256-1LzbW6G6Uz8akWiOdlIi435GAm1ct5jF5tovw/9to0o=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b7a6fde153d9470afdb6aa1da51c4117f03b84ed",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"npmlock2nix": {
"flake": false,
"locked": {
"lastModified": 1654775747,
"narHash": "sha256-9pXHDpIjmsK5390wmpGHu9aA4QOPpegPBvThHeBlef4=",
"owner": "nix-community",
"repo": "npmlock2nix",
"rev": "5c4f247688fc91d665df65f71c81e0726621aaa8",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "npmlock2nix",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"npmlock2nix": "npmlock2nix"
}
}
},
"root": "root",
"version": 7
}

40
flake.nix Normal file
View File

@ -0,0 +1,40 @@
{
description = "An implementation of the Double Ratchet cryptographic ratchet";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
# We can't use the current stable release because of
# https://github.com/emscripten-core/emscripten/issues/16913
inputs.flake-utils.url = "github:numtide/flake-utils";
inputs.npmlock2nix = {
url = "github:nix-community/npmlock2nix";
flake = false;
};
outputs = { self, nixpkgs, flake-utils, npmlock2nix }:
let
localOverlay = import ./nix/overlay.nix;
pkgsForSystem = system: import nixpkgs {
inherit system;
overlays = [
(final: prev: {
npmlock2nix = final.callPackage npmlock2nix {};
node_modules = final.npmlock2nix.node_modules { src = ./javascript; };
})
localOverlay
];
};
in (
# some systems cause issues, e.g. i686-linux is unsupported by gradle,
# which causes "nix flake check" to fail. Investigate more later, but for
# now, we will just allow x86_64-linux
flake-utils.lib.eachSystem [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" ] (system: rec {
legacyPackages = pkgsForSystem system;
checks = {
inherit (legacyPackages) olm-gcc-cmake olm-clang-cmake olm-gcc-make;
};
packages = {
javascript = legacyPackages.olm-javascript;
};
}
));
}

View File

@ -1,71 +0,0 @@
#include "olm/olm.hh"
#include "fuzzing.hh"
int main(int argc, const char *argv[]) {
size_t ignored;
if (argc <= 2) {
const char * message = "Usage: decrypt <pickle_key> <group_session>\n";
ignored = write(STDERR_FILENO, message, strlen(message));
exit(3);
}
const char * key = argv[1];
size_t key_length = strlen(key);
int session_fd = check_errno(
"Error opening session file", open(argv[2], O_RDONLY)
);
uint8_t *session_buffer;
ssize_t session_length = check_errno(
"Error reading session file", read_file(session_fd, &session_buffer)
);
int message_fd = STDIN_FILENO;
uint8_t * message_buffer;
ssize_t message_length = check_errno(
"Error reading message file", read_file(message_fd, &message_buffer)
);
uint8_t * tmp_buffer = (uint8_t *) malloc(message_length);
memcpy(tmp_buffer, message_buffer, message_length);
uint8_t session_memory[olm_inbound_group_session_size()];
OlmInboundGroupSession * session = olm_inbound_group_session(session_memory);
check_error(
olm_inbound_group_session_last_error,
session,
"Error unpickling session",
olm_unpickle_inbound_group_session(
session, key, key_length, session_buffer, session_length
)
);
size_t max_length = check_error(
olm_inbound_group_session_last_error,
session,
"Error getting plaintext length",
olm_group_decrypt_max_plaintext_length(
session, tmp_buffer, message_length
)
);
uint8_t plaintext[max_length];
size_t length = check_error(
olm_inbound_group_session_last_error,
session,
"Error decrypting message",
olm_group_decrypt(
session,
message_buffer, message_length,
plaintext, max_length
)
);
ignored = write(STDOUT_FILENO, plaintext, length);
ignored = write(STDOUT_FILENO, "\n", 1);
return ignored;
}

View File

@ -1,14 +0,0 @@
#include "olm/account.hh"
#include "fuzzing.hh"
int main(int argc, const char *argv[]) {
int pickle_fd = STDIN_FILENO;
uint8_t * pickle_buffer;
ssize_t pickle_length = check_errno(
"Error reading pickle file", read_file(pickle_fd, &pickle_buffer)
);
olm::Account * account = new olm::Account;
unpickle(pickle_buffer, pickle_buffer + pickle_length, *account);
free(pickle_buffer);
delete account;
}

10
fuzzing/README.md Normal file
View File

@ -0,0 +1,10 @@
# Directory structure
- `fuzzers/`: Sources for the fuzzing harnesses.
- `corpora/`: Contains the fuzzing corpora and assorted tools. The corpora are
filed under a directory with the same name as the fuzzing harness. Each of
those directories also contains the following:
- `in/`: Contains the actual corpus test cases.
- `tools/`: Any tools useful for that particular harness. A good example
would be a binary which generates seed test cases.

View File

@ -0,0 +1,50 @@
Fuzzers
=======
This directory contains a collection of fuzzing tools. Each tests a different
entry point to the code.
Usage notes:
1. Install AFL:
.. code::
apt-get install afl
2. Build the fuzzers:
.. code::
make fuzzers
3. Some of the tests (eg ``fuzz_decrypt`` and ``fuzz_group_decrypt``) require a
session file. You can create one by pickling an Olm session.
4. Make some work directories:
.. code::
mkdir -p fuzzing/in fuzzing/out
5. Generate starting input:
.. code::
echo "Test" > fuzzing/in/test
6. Run the test under ``afl-fuzz``:
.. code::
afl-fuzz -i fuzzing/in -o fuzzing/out -- \
./build/fuzzers/fuzz_<fuzzing_tool> [<test args>]
7. To resume with the data produced by an earlier run:
.. code::
afl-fuzz -i- -o existing_output_dir [...etc...]
8. If it shows failures, pipe the failure case into
``./build/fuzzers/debug_<fuzzing_tool>``, fix, and repeat.

View File

@ -11,4 +11,6 @@ int main(int argc, const char *argv[]) {
decode_message(*reader, message_buffer, message_length, 8);
free(message_buffer);
delete reader;
return EXIT_SUCCESS;
}

View File

@ -3,11 +3,10 @@
#include "fuzzing.hh"
int main(int argc, const char *argv[]) {
size_t ignored;
if (argc <= 3) {
const char * message = "Usage: decrypt: <session_key> <session_file>"
" <message_type>\n";
ignored = write(STDERR_FILENO, message, strlen(message));
(void)write(STDERR_FILENO, message, strlen(message));
exit(3);
}
@ -59,7 +58,12 @@ int main(int argc, const char *argv[]) {
)
);
ignored = write(STDOUT_FILENO, plaintext, length);
ignored = write(STDOUT_FILENO, "\n", 1);
return ignored;
(void)write(STDOUT_FILENO, plaintext, length);
(void)write(STDOUT_FILENO, "\n", 1);
free(session_buffer);
free(message_buffer);
free(tmp_buffer);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,102 @@
#include "olm/olm.hh"
#include "fuzzing.hh"
#ifndef __AFL_FUZZ_TESTCASE_LEN
ssize_t fuzz_len;
#define __AFL_FUZZ_TESTCASE_LEN fuzz_len
unsigned char fuzz_buf[1024000];
#define __AFL_FUZZ_TESTCASE_BUF fuzz_buf
#define __AFL_FUZZ_INIT() void sync(void);
#define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0)
#define __AFL_INIT() sync()
#endif
__AFL_FUZZ_INIT();
int main(int argc, const char *argv[]) {
if (argc <= 2) {
const char * message = "Usage: decrypt <pickle_key> <group_session>\n";
(void)write(STDERR_FILENO, message, strlen(message));
exit(3);
}
const char * key = argv[1];
size_t key_length = strlen(key);
int session_fd = check_errno(
"Error opening session file", open(argv[2], O_RDONLY)
);
uint8_t *session_buffer;
ssize_t session_length = check_errno(
"Error reading session file", read_file(session_fd, &session_buffer)
);
uint8_t session_memory[olm_inbound_group_session_size()];
OlmInboundGroupSession * session = olm_inbound_group_session(session_memory);
check_error(
olm_inbound_group_session_last_error,
session,
"Error unpickling session",
olm_unpickle_inbound_group_session(
session, key, key_length, session_buffer, session_length
)
);
#ifdef __AFL_HAVE_MANUAL_CONTROL
__AFL_INIT();
#endif
size_t test_case_buf_len = 1024;
uint8_t * message_buffer = (uint8_t *) malloc(test_case_buf_len);
uint8_t * tmp_buffer = (uint8_t *) malloc(test_case_buf_len);
while (__AFL_LOOP(10000)) {
size_t message_length = __AFL_FUZZ_TESTCASE_LEN;
if (message_length > test_case_buf_len) {
message_buffer = (uint8_t *)realloc(message_buffer, message_length);
tmp_buffer = (uint8_t *)realloc(tmp_buffer, message_length);
if (!message_buffer || !tmp_buffer) return 1;
}
memcpy(message_buffer, __AFL_FUZZ_TESTCASE_BUF, message_length);
memcpy(tmp_buffer, message_buffer, message_length);
size_t max_length = check_error(
olm_inbound_group_session_last_error,
session,
"Error getting plaintext length",
olm_group_decrypt_max_plaintext_length(
session, tmp_buffer, message_length
)
);
uint8_t plaintext[max_length];
uint32_t ratchet_index;
size_t length = check_error(
olm_inbound_group_session_last_error,
session,
"Error decrypting message",
olm_group_decrypt(
session,
message_buffer, message_length,
plaintext, max_length, &ratchet_index
)
);
(void)write(STDOUT_FILENO, plaintext, length);
(void)write(STDOUT_FILENO, "\n", 1);
}
free(session_buffer);
free(message_buffer);
free(tmp_buffer);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,41 @@
#include "fuzzing.hh"
#include "olm/account.hh"
#include "olm/olm.h"
size_t fuzz_unpickle_account(
OlmAccount * account, void * pickled, size_t pickled_length
) {
olm::Account & object = *reinterpret_cast<olm::Account *>(account);
std::uint8_t * const pos = reinterpret_cast<std::uint8_t *>(pickled);
std::uint8_t * const end = pos + pickled_length;
if (!unpickle(pos, end, object)) {
if (object.last_error == OlmErrorCode::OLM_SUCCESS) {
object.last_error = OlmErrorCode::OLM_CORRUPTED_PICKLE;
}
return std::size_t(-1);
}
return pickled_length;
}
int main(int argc, const char * argv[]) {
int pickle_fd = STDIN_FILENO;
uint8_t * pickle_buffer;
ssize_t pickle_length = check_errno(
"Error reading pickle file", read_file(pickle_fd, &pickle_buffer));
void * account_buf = malloc(olm_account_size());
if (!account_buf) {
return 3;
}
OlmAccount * account = olm_account(account_buf);
check_error(olm_account_last_error, account, "Error unpickling account",
fuzz_unpickle_account(account, pickle_buffer, pickle_length));
free(pickle_buffer);
free(account);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,28 @@
#include <olm/outbound_group_session.h>
#include "fuzzing.h"
int main(int argc, const char *argv[]) {
if (argc != 1) {
printf("Usage: %s <input_file\n", argv[0]);
exit(3);
}
void *session_buffer = malloc(olm_outbound_group_session_size());
OlmOutboundGroupSession *session = olm_outbound_group_session(session_buffer);
int pickle_fd = STDIN_FILENO;
uint8_t *pickle_buffer;
ssize_t pickle_length = check_errno("Error reading message file",
read_file(pickle_fd, &pickle_buffer));
check_outbound_group_session(
session, "Error unpickling outbound group session",
olm_unpickle_outbound_group_session(session, "", 0, pickle_buffer,
pickle_length));
free(session_buffer);
free(pickle_buffer);
return EXIT_SUCCESS;
}

View File

@ -11,4 +11,6 @@ int main(int argc, const char *argv[]) {
unpickle(pickle_buffer, pickle_buffer + pickle_length, *session);
free(pickle_buffer);
delete session;
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,101 @@
#include "olm/olm.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define OLM_FUZZING 1
ssize_t read_file(
int fd,
uint8_t **buffer
) {
size_t buffer_size = 1;
size_t buffer_pos = 0;
uint8_t * current_buffer = (uint8_t *) malloc(buffer_size);
if (!current_buffer) return -1;
while (1) {
ssize_t count = read(
fd, current_buffer + buffer_pos, buffer_size - buffer_pos
);
if (count < 0) break; // A read error happened, so just fail immediately.
if (count == 0) {
// Nothing more left to read. We downsize the buffer to fit the
// data exactly, unless no data was read at all, in which case we
// skip the downsizing.
if (buffer_pos != 0) {
current_buffer = (uint8_t *) realloc(current_buffer, buffer_pos);
if (!current_buffer) break;
}
// The read was successful so we return the allocated buffer.
*buffer = current_buffer;
return buffer_pos;
}
buffer_pos += count;
// We've reached capacity, so enlarge the buffer.
if (buffer_pos == buffer_size) {
buffer_size *= 2;
uint8_t * new_buffer = (uint8_t *) realloc(current_buffer, buffer_size);
if (!new_buffer) break;
current_buffer = new_buffer;
}
}
free(current_buffer);
return -1;
}
ssize_t check_errno(
const char * message,
ssize_t value
) {
if (value == (ssize_t)-1) {
perror(message);
exit(1);
}
return value;
}
size_t check_error(
const char * message,
const char * olm_message,
size_t value
) {
if (value == olm_error()) {
(void)write(STDERR_FILENO, message, strlen(message));
(void)write(STDERR_FILENO, ": ", 2);
(void)write(STDERR_FILENO, olm_message, strlen(olm_message));
(void)write(STDERR_FILENO, "\n", 1);
exit(2);
}
return value;
}
size_t check_session(
OlmSession * session,
const char * message,
size_t value
) {
return check_error(message, olm_session_last_error(session), value);
}
size_t check_outbound_group_session(
OlmOutboundGroupSession * session,
const char * message,
size_t value
) {
return check_error(message, olm_outbound_group_session_last_error(session), value);
}

View File

@ -15,28 +15,43 @@ ssize_t read_file(
uint8_t **buffer
) {
size_t buffer_size = 4096;
uint8_t * current_buffer = (uint8_t *) malloc(buffer_size);
if (current_buffer == NULL) return -1;
size_t buffer_pos = 0;
uint8_t * current_buffer = (uint8_t *) malloc(buffer_size);
if (!current_buffer) return -1;
while (1) {
ssize_t count = read(
fd, current_buffer + buffer_pos, buffer_size - buffer_pos
);
if (count < 0) break;
if (count < 0) break; // A read error happened, so just fail immediately.
if (count == 0) {
uint8_t * return_buffer = (uint8_t *) realloc(current_buffer, buffer_pos);
if (return_buffer == NULL) break;
*buffer = return_buffer;
// Nothing more left to read. We downsize the buffer to fit the
// data exactly, unless no data was read at all, in which case we
// skip the downsizing.
if (buffer_pos != 0) {
current_buffer = (uint8_t *) realloc(current_buffer, buffer_pos);
if (!current_buffer) break;
}
// The read was successful so we return the allocated buffer.
*buffer = current_buffer;
return buffer_pos;
}
buffer_pos += count;
// We've reached capacity, so enlarge the buffer.
if (buffer_pos == buffer_size) {
buffer_size *= 2;
uint8_t * new_buffer = (uint8_t *) realloc(current_buffer, buffer_size);
if (new_buffer == NULL) break;
if (!new_buffer) break;
current_buffer = new_buffer;
}
}
free(current_buffer);
return -1;
}
@ -62,13 +77,12 @@ size_t check_error(
) {
if (value == olm_error()) {
const char * olm_message = f(object);
ssize_t ignored;
ignored = write(STDERR_FILENO, message, strlen(message));
ignored = write(STDERR_FILENO, ": ", 2);
ignored = write(STDERR_FILENO, olm_message, strlen(olm_message));
ignored = write(STDERR_FILENO, "\n", 1);
(void)write(STDERR_FILENO, message, strlen(message));
(void)write(STDERR_FILENO, ": ", 2);
(void)write(STDERR_FILENO, olm_message, strlen(olm_message));
(void)write(STDERR_FILENO, "\n", 1);
exit(2);
return ignored;
}
return value;
}

118
fuzzing/start_fuzzers.sh Executable file
View File

@ -0,0 +1,118 @@
#!/usr/bin/bash
# Needs to be started in tmux.
script_dir() {
dirname "$(readlink -f "$0")"
}
fuzzer_dir() {
printf '%s/fuzzers\n' "$(script_dir)"
}
fuzzer_list() {
find "$(fuzzer_dir)" -maxdepth 1 -type f \( -name '*.cpp' -or -name '*.c' \) -printf '%P\n' \
| while read -r fuzzer; do
fuzzer="${fuzzer#fuzz_}"
printf '%s\n' "${fuzzer%.c*}"
done
}
usage() {
printf '%s: HARNESS FUZZER\n\n' "$(basename "$0")"
printf ' HARNESS ∈ {\n'
# We want word-splitting here so that each fuzzer ends up as a separate
# argument.
# shellcheck disable=SC2046
printf '%30s,\n' $(fuzzer_list | tr '\n' ' ')
printf ' }\n'
printf ' FUZZER ∈ {afl, afl++}\n'
}
if [[ $# -ne 2 ]]; then
usage
exit 1
fi
case "$2" in
afl++)
export AFL_PATH=/home/dkasak/code/projects/afl/afl++
export AFL_AUTORESUME=1
AFL_ARGS_FUZZER0="-D"
AFL_ARGS_FUZZER1="-L 0"
AFL_ARGS_FUZZER2="-p rare"
AFL_ARGS_FUZZER3="-p fast"
AFL_ARGS_FUZZER4="-p exploit"
AFL_ARGS_FUZZER5="-p explore"
;;
afl)
export AFL_PATH=/usr/bin
;;
*)
printf 'Unknown fuzzer: %s\n' "$2"
exit 1
;;
esac
export AFL=$AFL_PATH/afl-fuzz
export AFL_TMPDIR=/tmp
case "$1" in
group_decrypt)
FUZZER_ARG1="fuzzing/$1/pickled-inbound-group-session.txt"
;;
decrypt)
FUZZER_ARG1="fuzzing/$1/pickled-session.txt"
FUZZER_ARG2="1"
;;
decode_message)
;;
unpickle_session)
;;
unpickle_account)
;;
unpickle_account_test)
;;
unpickle_megolm_outbound)
;;
*)
printf 'Unknown harness: %s\n' "$1"
exit 1
;;
esac
cd "$(script_dir)" || exit 1
# Fuzzer args are deliberately not quoted below so that word-splitting happens.
# This is used so that they expand into nothing in cases where they are missing
# or to expand into multiple arguments from a string definition.
# shellcheck disable=SC2086
tmux new-window -d -n "M" -- \
"$AFL" -i "corpora/$1/in" -o "corpora/$1/out" -M i0 "$AFL_ARGS_FUZZER0" \
-- "../build/fuzzers/fuzz_$1" $FUZZER_ARG1 $FUZZER_ARG2
# shellcheck disable=SC2086
tmux new-window -d -n "S1" -- \
"$AFL" -i "corpora/$1/in" -o "corpora/$1/out" -S i1 "$AFL_ARGS_FUZZER1" \
-- "../build/fuzzers/fuzz_$1" $FUZZER_ARG1 $FUZZER_ARG2
# shellcheck disable=SC2086
tmux new-window -d -n "S2" -- \
"$AFL" -i "corpora/$1/in" -o "corpora/$1/out" -S i2 $AFL_ARGS_FUZZER2 \
-- "../build/fuzzers/fuzz_$1" $FUZZER_ARG1 $FUZZER_ARG2
# shellcheck disable=SC2086
tmux new-window -d -n "S3" -- \
"$AFL" -i "corpora/$1/in" -o "corpora/$1/out" -S i3 $AFL_ARGS_FUZZER3 \
-- "../build/fuzzers/fuzz_$1" $FUZZER_ARG1 $FUZZER_ARG2
# shellcheck disable=SC2086
tmux new-window -d -n "S4" -- \
"$AFL" -i "corpora/$1/in" -o "corpora/$1/out" -S i4 $AFL_ARGS_FUZZER4 \
-- "../build/fuzzers/fuzz_$1_asan" $FUZZER_ARG1 $FUZZER_ARG2
# shellcheck disable=SC2086
tmux new-window -d -n "S5" -- \
"$AFL" -i "corpora/$1/in" -o "corpora/$1/out" -S i5 $AFL_ARGS_FUZZER5 \
-- "../build/fuzzers/fuzz_$1" $FUZZER_ARG1 $FUZZER_ARG2

17
gitlab-math.lua Normal file
View File

@ -0,0 +1,17 @@
function Math(el)
if el.mathtype == "InlineMath" then
if el.text:sub(1,1) == '`' and el.text:sub(#el.text) == '`' then
local text = el.text:sub(2,#el.text-1)
return pandoc.Math(el.mathtype, text)
else
local cont = pandoc.read(el.text)
return { pandoc.Str("$") } .. cont.blocks[1].content .. { pandoc.Str("$") }
end
end
end
function CodeBlock(el)
if el.classes[1] == "math" then
return pandoc.Para({ pandoc.Math("DisplayMath", el.text) })
end
end

4
include/module.modulemap Normal file
View File

@ -0,0 +1,4 @@
module libolm {
header "olm/olm.h"
export *
}

View File

@ -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 <cstdint>
@ -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;
};
@ -43,11 +43,14 @@ struct Account {
Account();
IdentityKeys identity_keys;
List<OneTimeKey, MAX_ONE_TIME_KEYS> one_time_keys;
std::uint8_t num_fallback_keys;
OneTimeKey current_fallback_key;
OneTimeKey prev_fallback_key;
std::uint32_t next_one_time_key_id;
OlmErrorCode last_error;
/** Number of random bytes needed to create a new account */
std::size_t new_account_random_length();
std::size_t new_account_random_length() const;
/** Create a new account. Returns std::size_t(-1) on error. If the number of
* random bytes is too small then last_error will be NOT_ENOUGH_RANDOM */
@ -56,7 +59,7 @@ struct Account {
);
/** Number of bytes needed to output the identity keys for this account */
std::size_t get_identity_json_length();
std::size_t get_identity_json_length() const;
/** Output the identity keys for this account as JSON in the following
* format:
@ -75,7 +78,7 @@ struct Account {
/**
* The length of an ed25519 signature in bytes.
*/
std::size_t signature_length();
std::size_t signature_length() const;
/**
* Signs a message with the ed25519 key for this account.
@ -86,7 +89,7 @@ struct Account {
);
/** Number of bytes needed to output the one time keys for this account */
std::size_t get_one_time_keys_json_length();
std::size_t get_one_time_keys_json_length() const;
/** Output the one time keys that haven't been published yet as JSON:
*
@ -104,18 +107,20 @@ struct Account {
std::uint8_t * one_time_json, std::size_t one_time_json_length
);
/** Mark the current list of one_time_keys as being published. They
* will no longer be returned by get_one_time_keys_json_length(). */
/** Mark the current list of one_time_keys and the current fallback key as
* being published. The current one time keys will no longer be returned by
* get_one_time_keys_json() and the current fallback key will no longer be
* returned by get_unpublished_fallback_key_json(). */
std::size_t mark_keys_as_published();
/** The largest number of one time keys this account can store. */
std::size_t max_number_of_one_time_keys();
std::size_t max_number_of_one_time_keys() const;
/** The number of random bytes needed to generate a given number of new one
* time keys. */
std::size_t generate_one_time_keys_random_length(
std::size_t number_of_keys
);
) const;
/** Generates a number of new one time keys. If the total number of keys
* stored by this account exceeds max_number_of_one_time_keys() then the
@ -126,14 +131,57 @@ struct Account {
std::uint8_t const * random, std::size_t random_length
);
/** The number of random bytes needed to generate a fallback key. */
std::size_t generate_fallback_key_random_length() const;
/** Generates a new fallback key. Returns std::size_t(-1) on error. If the
* number of random bytes is too small then last_error will be
* NOT_ENOUGH_RANDOM */
std::size_t generate_fallback_key(
std::uint8_t const * random, std::size_t random_length
);
/** Number of bytes needed to output the fallback keys for this account */
std::size_t get_fallback_key_json_length() const;
/** Deprecated: use get_unpublished_fallback_key_json instead */
std::size_t get_fallback_key_json(
std::uint8_t * fallback_json, std::size_t fallback_json_length
);
/** Number of bytes needed to output the unpublished fallback keys for this
* account */
std::size_t get_unpublished_fallback_key_json_length() const;
/** Output the fallback key as JSON:
*
* {"curve25519":
* ["<6 byte key id>":"<43 base64 characters>"
* ,"<6 byte key id>":"<43 base64 characters>"
* ...
* ]
* }
*
* if there is a fallback key and it has not been published yet.
*
* Returns the size of the JSON written or std::size_t(-1) on error.
* If the buffer is too small last_error will be OUTPUT_BUFFER_TOO_SMALL.
*/
std::size_t get_unpublished_fallback_key_json(
std::uint8_t * fallback_json, std::size_t fallback_json_length
);
/** Forget about the old fallback key */
void forget_old_fallback_key();
/** 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
);
};

View File

@ -22,6 +22,10 @@
#include <stddef.h>
#include <stdint.h>
// Note: exports in this file are only for unit tests. Nobody else should be
// using this externally
#include "olm/olm_export.h"
#ifdef __cplusplus
extern "C" {
#endif
@ -30,7 +34,7 @@ extern "C" {
/**
* The number of bytes of unpadded base64 needed to encode a length of input.
*/
size_t _olm_encode_base64_length(
OLM_EXPORT size_t _olm_encode_base64_length(
size_t input_length
);
@ -42,7 +46,7 @@ size_t _olm_encode_base64_length(
*
* Returns number of bytes encoded
*/
size_t _olm_encode_base64(
OLM_EXPORT size_t _olm_encode_base64(
uint8_t const * input, size_t input_length,
uint8_t * output
);
@ -51,7 +55,7 @@ size_t _olm_encode_base64(
* The number of bytes of raw data a length of unpadded base64 will encode to.
* Returns size_t(-1) if the length is not a valid length for base64.
*/
size_t _olm_decode_base64_length(
OLM_EXPORT size_t _olm_decode_base64_length(
size_t input_length
);
@ -63,7 +67,7 @@ size_t _olm_decode_base64_length(
*
* Returns number of bytes decoded
*/
size_t _olm_decode_base64(
OLM_EXPORT size_t _olm_decode_base64(
uint8_t const * input, size_t input_length,
uint8_t * output
);

View File

@ -18,12 +18,16 @@
#include <cstddef>
#include <cstdint>
// Note: exports in this file are only for unit tests. Nobody else should be
// using this externally
#include "olm/olm_export.h"
namespace olm {
/**
* The number of bytes of unpadded base64 needed to encode a length of input.
*/
std::size_t encode_base64_length(
OLM_EXPORT std::size_t encode_base64_length(
std::size_t input_length
);
@ -33,7 +37,7 @@ std::size_t encode_base64_length(
* The input can overlap with the last three quarters of the output buffer.
* That is, the input pointer may be output + output_length - input_length.
*/
std::uint8_t * encode_base64(
OLM_EXPORT std::uint8_t * encode_base64(
std::uint8_t const * input, std::size_t input_length,
std::uint8_t * output
);
@ -42,7 +46,7 @@ std::uint8_t * encode_base64(
* The number of bytes of raw data a length of unpadded base64 will encode to.
* Returns std::size_t(-1) if the length is not a valid length for base64.
*/
std::size_t decode_base64_length(
OLM_EXPORT std::size_t decode_base64_length(
std::size_t input_length
);
@ -51,8 +55,12 @@ std::size_t decode_base64_length(
* Writes decode_base64_length(input_length) bytes to the output buffer.
* The output can overlap with the first three quarters of the input buffer.
* That is, the input pointers and output pointer may be the same.
*
* Returns the number of bytes of raw data the base64 input decoded to. If the
* input length supplied is not a valid length for base64, returns
* std::size_t(-1) and does not decode.
*/
std::uint8_t const * decode_base64(
OLM_EXPORT std::size_t decode_base64(
std::uint8_t const * input, std::size_t input_length,
std::uint8_t * output
);

View File

@ -19,6 +19,10 @@
#include <stdint.h>
#include <stdlib.h>
// Note: exports in this file are only for unit tests. Nobody else should be
// using this externally
#include "olm/olm_export.h"
#ifdef __cplusplus
extern "C" {
#endif
@ -111,7 +115,7 @@ struct _olm_cipher_aes_sha_256 {
size_t kdf_info_length;
};
extern const struct _olm_cipher_ops _olm_cipher_aes_sha_256_ops;
OLM_EXPORT extern const struct _olm_cipher_ops _olm_cipher_aes_sha_256_ops;
/**
* get an initializer for an instance of struct _olm_cipher_aes_sha_256.
@ -123,9 +127,9 @@ extern const struct _olm_cipher_ops _olm_cipher_aes_sha_256_ops;
* struct _olm_cipher *cipher = OLM_CIPHER_BASE(&MY_CIPHER);
*/
#define OLM_CIPHER_INIT_AES_SHA_256(KDF_INFO) { \
.base_cipher = { &_olm_cipher_aes_sha_256_ops },\
.kdf_info = (uint8_t *)(KDF_INFO), \
.kdf_info_length = sizeof(KDF_INFO) - 1 \
/*.base_cipher = */{ &_olm_cipher_aes_sha_256_ops },\
/*.kdf_info = */(uint8_t *)(KDF_INFO), \
/*.kdf_info_length = */sizeof(KDF_INFO) - 1 \
}
#define OLM_CIPHER_BASE(CIPHER) \
(&((CIPHER)->base_cipher))

Some files were not shown because too many files have changed in this diff Show More