Compare commits
318 commits
poljar/cma
...
master
Author | SHA1 | Date | |
---|---|---|---|
fd10167985 | |||
cb3fe622ae | |||
|
7e0c827703 | ||
|
972faaadd5 | ||
|
807b252331 | ||
|
bbdac4045d | ||
|
4beb2487ce | ||
|
b54fa37fae | ||
|
66294cf7f6 | ||
|
366520ebfd | ||
|
d27f162316 | ||
|
4b69958c95 | ||
|
5cfe6c3dbd | ||
|
bbdc12c569 | ||
|
afb3d403e1 | ||
|
418656ee9f | ||
|
0d367baa5b | ||
|
8cbb60e476 | ||
|
0880461134 | ||
|
8f4b81b512 | ||
|
ab4cbcd01a | ||
|
704b198f5a | ||
|
0eb4550a8f | ||
|
249acc9e0b | ||
|
5efd38c990 | ||
|
ad76fc1570 | ||
|
b5d68376b5 | ||
|
dbd8a44fa2 | ||
|
722f4df4aa | ||
|
6d767aaf29 | ||
|
21e84095e6 | ||
|
464e193dad | ||
|
df2cfcb6d0 | ||
|
ed94b56d16 | ||
|
f52d179c18 | ||
|
85c0be5fbc | ||
|
203083cdd4 | ||
|
983e78dc53 | ||
|
92769cec71 | ||
|
d18d12d379 | ||
|
14c5ea70d4 | ||
|
ee1b0c8a9a | ||
|
84807125c0 | ||
|
eb21951124 | ||
|
39252b012b | ||
|
86a3d95855 | ||
|
81f5c4a3cd | ||
|
d0b2b8702f | ||
|
e000c33a58 | ||
|
43672251e4 | ||
|
e116efa752 | ||
|
a4a700739e | ||
|
b8990d90f0 | ||
|
99d635779c | ||
|
b65ab350f0 | ||
|
c9e6bf9263 | ||
|
727722d7a8 | ||
|
8510b2f601 | ||
|
7bf6fb553e | ||
|
1c7df35c5f | ||
|
2f23d99424 | ||
|
0a6a5a5caf | ||
|
b5dfa28f3b | ||
|
3c91c66ee2 | ||
|
dcf5582f8a | ||
|
9d66965962 | ||
|
dd1905454b | ||
|
9908862979 | ||
|
7d0a69a099 | ||
|
2c6b9d5e3a | ||
|
0dde38bd4f | ||
|
b3478a526b | ||
|
b11f555b01 | ||
|
23380ca331 | ||
|
1c3af112c8 | ||
|
55c976d4f6 | ||
|
db90ce6b62 | ||
|
96407493d1 | ||
|
9b2c116fbd | ||
|
cbc6886a37 | ||
|
c172ab6236 | ||
|
9946acac23 | ||
|
60122a2c2d | ||
|
8475061136 | ||
|
e197cd76d6 | ||
|
797183f27f | ||
|
21dc11ecbf | ||
|
8519ce0269 | ||
|
c23ce70fc6 | ||
|
2dbeea2f1d | ||
|
e854c0f907 | ||
|
f647747d27 | ||
|
f6309f0281 | ||
|
4b2f68d11e | ||
|
fb162258ab | ||
|
ee76674f03 | ||
|
701f9c765d | ||
|
85a2f47088 | ||
|
8c62046392 | ||
|
845e7cb43b | ||
|
69ca6cd5ca | ||
|
336e1d56a8 | ||
|
6f59e16b58 | ||
|
5e5e32fe83 | ||
|
631f050554 | ||
|
29e0287ef3 | ||
|
c5eff859cb | ||
|
4127a84b3d | ||
|
3b6ff327c0 | ||
|
b989db0117 | ||
|
5039c0cc3a | ||
|
98b8e35a7c | ||
|
2430e9bb9a | ||
|
609e7e8d40 | ||
|
06b723db6e | ||
|
bce4f007b1 | ||
|
0e7c0a5613 | ||
|
201f139523 | ||
|
03c5523aac | ||
|
8656f1463c | ||
|
c81dfd0718 | ||
|
4fb723cad3 | ||
|
72b8bf5334 | ||
|
904e80b75f | ||
|
06407aa08d | ||
|
6a63a5bfa9 | ||
|
e1aa1b3277 | ||
|
91a619b745 | ||
|
8ddb72cfed | ||
|
6c552dd7eb | ||
|
d84c1af882 | ||
|
4d6c3ba8d1 | ||
|
b70e0b06df | ||
|
d704f4bd3c | ||
|
131f7cfd71 | ||
|
bdd73c5c32 | ||
|
34974551ab | ||
|
0a8bbde361 | ||
|
b38e282f3a | ||
|
ceed90922a | ||
|
4d14750c38 | ||
|
e06ac20558 | ||
|
811e56a0f0 | ||
|
583f8b761b | ||
|
84dbba8e1c | ||
|
a44fc368f2 | ||
|
93352b55e7 | ||
|
7dd4c77c19 | ||
|
4901435a0e | ||
|
254a4a5619 | ||
|
abf8f97491 | ||
|
0f7c13334f | ||
|
2aad86ea84 | ||
|
9a8b421903 | ||
|
37c8e14e53 | ||
|
7263c4221b | ||
|
60be1ca55f | ||
|
1b7973626e | ||
|
d47c2a92b8 | ||
|
4803f4192d | ||
|
3612ac7ae7 | ||
|
b90f9ee7d3 | ||
|
6ed8d687e8 | ||
|
3e6592e445 | ||
|
56df2613f3 | ||
|
64afab9364 | ||
|
995def932e | ||
|
d856c441b6 | ||
|
22bc1155ed | ||
|
891a5f22c8 | ||
|
ccc0d122ee | ||
|
2f35e0bc61 | ||
|
e82f2601b0 | ||
|
a5efc08ef3 | ||
|
c325db02fc | ||
|
0a7b6da9a0 | ||
|
8d1cfd207a | ||
|
15f65283c7 | ||
|
0684eb4564 | ||
|
b0a05976ea | ||
|
18ad6cb067 | ||
|
d7c3971f9a | ||
|
c95677611c | ||
|
7f53dedca6 | ||
|
3b3f2c71dc | ||
|
f1d8efd821 | ||
|
1694f15ffb | ||
|
4d2522a65c | ||
|
dbbf467075 | ||
|
26bd2fc35d | ||
|
09fbb9e966 | ||
|
f16377822f | ||
|
09384b4d45 | ||
|
bcb89bcc24 | ||
|
3745ea57bb | ||
|
0bb0f85e18 | ||
|
21ba95ade5 | ||
|
cabefb17dc | ||
|
a07e27cfa5 | ||
|
23e0486007 | ||
|
4be7cc367b | ||
|
b69b56d0bb | ||
|
f46577a06a | ||
|
4e927eb1cf | ||
|
c01164f001 | ||
|
541a2bf6fd | ||
|
b9771dae61 | ||
|
11d34f79af | ||
|
1fd8d2978f | ||
|
64b8bc11cb | ||
|
8efa0ec17d | ||
|
c5ab3ecbf2 | ||
|
7768c3219f | ||
|
c4d737c86c | ||
|
60d451bbbe | ||
|
4d17aa4f05 | ||
|
d4afebc883 | ||
|
030e506c00 | ||
|
22f85d3f3d | ||
|
6611165bff | ||
|
add885c874 | ||
|
be0c31894a | ||
|
73a9ced64e | ||
|
ac61190bb3 | ||
|
0fd315d54c | ||
|
0e6ec3062d | ||
|
ec5ff1e032 | ||
|
78d9cbabb7 | ||
|
2ef1f6f4fc | ||
|
4bae4134eb | ||
|
84841a19e2 | ||
|
3cd6b15853 | ||
|
c47c6ca399 | ||
|
c45f19f12d | ||
|
89050dc0b6 | ||
|
171044f3fc | ||
|
a0284c2ba3 | ||
|
8a958beb48 | ||
|
9349c1b82b | ||
|
6fea6898d4 | ||
|
c9a183a7c5 | ||
|
efd17631b1 | ||
|
ad173bc798 | ||
|
ddd140b23d | ||
|
14c1db02fe | ||
|
fdf25eb3ba | ||
|
83bf351a34 | ||
|
5a9fdd85cb | ||
|
05a7af8db1 | ||
|
281c5aac21 | ||
|
611d3949cb | ||
|
9cc2394672 | ||
|
e6f8a99b34 | ||
|
f409b69e88 | ||
|
954d6f98eb | ||
|
930c467754 | ||
|
0469065855 | ||
|
5bcfeaffe3 | ||
|
a9c7bde457 | ||
|
52098b3af7 | ||
|
baaf002663 | ||
|
6753595300 | ||
|
387deeea8f | ||
|
fc423fad15 | ||
|
b482321213 | ||
|
e73a208fb2 | ||
|
39a1ee0b18 | ||
|
3568060570 | ||
|
44c2e47a3e | ||
|
72df5301e0 | ||
|
b83a0c0992 | ||
|
57b6839c25 | ||
|
32f3a82bf9 | ||
|
e267825bb7 | ||
|
c463d8b55b | ||
|
aa0c9ab6b5 | ||
|
ebd3ba6cc1 | ||
|
ae38f2c5a0 | ||
|
61175c969b | ||
|
28350d612e | ||
|
5d7070d2f3 | ||
|
125c62098c | ||
|
c4d703ac3d | ||
|
7538a1eccf | ||
|
25662564d4 | ||
|
cfd1450b0e | ||
|
fec41f9540 | ||
|
5e24c605d2 | ||
|
ba65551d5f | ||
|
27f5c25fe8 | ||
|
9faa100c6a | ||
|
2f5590bf38 | ||
|
f8abaf9e2f | ||
|
e1a4e6ebf1 | ||
|
4bb039a98e | ||
|
3ed150edf7 | ||
|
a18a4e8eb4 | ||
|
cab1edb6da | ||
|
b6cd1690f2 | ||
|
c368898cef | ||
|
214908ace5 | ||
|
969c8b45e5 | ||
|
5b69a1a5cd | ||
|
b46ac91928 | ||
|
73288e6f2a | ||
|
6a72cfd287 | ||
|
e273189af3 | ||
|
0757e6df40 | ||
|
769d013ef7 | ||
|
69feb86c01 | ||
|
27fcc337a3 | ||
|
6aafd69f8f | ||
|
38649855f7 | ||
|
af47497ace | ||
|
ba1c20d6b4 | ||
|
b79c6d6f69 | ||
|
099b3ce82a | ||
|
327d6ac0eb |
476 changed files with 48160 additions and 4000 deletions
14
.editorconfig
Normal file
14
.editorconfig
Normal 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
|
14
.gitignore
vendored
14
.gitignore
vendored
|
@ -6,9 +6,20 @@
|
||||||
/olm-*.tgz
|
/olm-*.tgz
|
||||||
/README.html
|
/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
|
# Xcode
|
||||||
build/
|
build/
|
||||||
|
.build/
|
||||||
DerivedData/
|
DerivedData/
|
||||||
*.pbxuser
|
*.pbxuser
|
||||||
!default.pbxuser
|
!default.pbxuser
|
||||||
|
@ -27,3 +38,6 @@ xcuserdata/
|
||||||
*.dSYM
|
*.dSYM
|
||||||
Pods/
|
Pods/
|
||||||
*.xcworkspace
|
*.xcworkspace
|
||||||
|
|
||||||
|
# JetBrains tools
|
||||||
|
.idea/
|
||||||
|
|
51
.gitlab-ci.yml
Normal file
51
.gitlab-ci.yml
Normal 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
|
221
CHANGELOG.rst
221
CHANGELOG.rst
|
@ -1,3 +1,224 @@
|
||||||
|
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>`_
|
Changes in `3.1.0 <https://gitlab.matrix.org/matrix-org/olm/tags/3.1.0>`_
|
||||||
=========================================================================
|
=========================================================================
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
cmake_minimum_required(VERSION 3.1)
|
cmake_minimum_required(VERSION 3.4)
|
||||||
|
|
||||||
project(olm VERSION 3.1.0 LANGUAGES CXX C)
|
project(olm VERSION 3.2.16 LANGUAGES CXX C)
|
||||||
|
|
||||||
option(OLM_TESTS "Build tests" ON)
|
option(OLM_TESTS "Build tests" ON)
|
||||||
option(BUILD_SHARED_LIBS "Build as a shared library" ON)
|
option(BUILD_SHARED_LIBS "Build as a shared library" ON)
|
||||||
|
@ -15,11 +15,16 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_C_STANDARD 99)
|
set(CMAKE_C_STANDARD 99)
|
||||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
|
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE)
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
set(CMAKE_BUILD_TYPE Release)
|
set(CMAKE_BUILD_TYPE Release)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_C_VISIBILITY_PRESET hidden)
|
||||||
|
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
||||||
|
set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
|
||||||
|
|
||||||
add_library(olm
|
add_library(olm
|
||||||
src/account.cpp
|
src/account.cpp
|
||||||
src/base64.cpp
|
src/base64.cpp
|
||||||
|
@ -47,10 +52,15 @@ add_library(olm
|
||||||
lib/curve25519-donna/curve25519-donna.c)
|
lib/curve25519-donna/curve25519-donna.c)
|
||||||
add_library(Olm::Olm ALIAS olm)
|
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
|
target_include_directories(olm
|
||||||
PUBLIC
|
PUBLIC
|
||||||
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||||
$<INSTALL_INTERFACE:include>
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||||
PRIVATE
|
PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/lib)
|
${CMAKE_CURRENT_SOURCE_DIR}/lib)
|
||||||
|
|
||||||
|
@ -59,30 +69,44 @@ set_target_properties(olm PROPERTIES
|
||||||
VERSION ${PROJECT_VERSION})
|
VERSION ${PROJECT_VERSION})
|
||||||
|
|
||||||
set_target_properties(olm PROPERTIES
|
set_target_properties(olm PROPERTIES
|
||||||
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}
|
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}
|
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
|
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
|
# Installation
|
||||||
#
|
#
|
||||||
include(GNUInstallDirs)
|
|
||||||
set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/Olm)
|
set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/Olm)
|
||||||
install(TARGETS olm
|
install(TARGETS olm
|
||||||
EXPORT olm-targets
|
EXPORT olm-targets
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||||
|
)
|
||||||
|
|
||||||
# The exported target will be named Olm.
|
# The exported target will be named Olm.
|
||||||
set_target_properties(olm PROPERTIES EXPORT_NAME Olm)
|
set_target_properties(olm PROPERTIES EXPORT_NAME Olm)
|
||||||
install(FILES
|
install(FILES
|
||||||
${CMAKE_SOURCE_DIR}/include/olm/olm.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/olm/olm.h
|
||||||
${CMAKE_SOURCE_DIR}/include/olm/outbound_group_session.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/olm/olm_export.h
|
||||||
${CMAKE_SOURCE_DIR}/include/olm/inbound_group_session.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/olm/outbound_group_session.h
|
||||||
${CMAKE_SOURCE_DIR}/include/olm/pk.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/olm/inbound_group_session.h
|
||||||
${CMAKE_SOURCE_DIR}/include/olm/sas.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)
|
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.
|
# Export the targets to a script.
|
||||||
install(EXPORT olm-targets
|
install(EXPORT olm-targets
|
||||||
FILE OlmTargets.cmake
|
FILE OlmTargets.cmake
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
Contributing code to libolm
|
# Contributing code to libolm
|
||||||
===========================
|
|
||||||
|
|
||||||
To contribute code to this library, the preferred way is to clone the git
|
To contribute code to this library, the preferred way is to clone the git
|
||||||
repository, create a git patch series (for example via ``git
|
repository, create a git patch series (for example via ``git
|
||||||
|
@ -8,18 +7,16 @@ format-patch --stdout origin/master``), and send this by email to
|
||||||
|
|
||||||
Naturally, you must be willing to license your contributions under the same
|
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 as the project itself - in this case, Apache Software License v2 (see
|
||||||
`<LICENSE>`_).
|
[LICENSE](LICENSE)).
|
||||||
|
|
||||||
Sign off
|
## Sign off
|
||||||
--------
|
|
||||||
|
|
||||||
In order to have a concrete record that your contribution is intentional and
|
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
|
you agree to license it under the same terms as the project's license, we've
|
||||||
adopted the same lightweight approach that the
|
adopted the same lightweight approach that the
|
||||||
`Linux Kernel <https://www.kernel.org/doc/Documentation/SubmittingPatches>`_,
|
[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>`_,
|
[Docker](https://github.com/docker/docker/blob/master/CONTRIBUTING.md),
|
||||||
and many other projects use: the DCO
|
and many other projects use: the DCO ([Developer Certificate of Origin](http://developercertificate.org/)).
|
||||||
(`Developer Certificate of Origin <http://developercertificate.org/>`_).
|
|
||||||
This is a simple declaration that you wrote the contribution or otherwise have
|
This is a simple declaration that you wrote the contribution or otherwise have
|
||||||
the right to contribute it to Matrix::
|
the right to contribute it to Matrix::
|
||||||
|
|
214
Makefile
214
Makefile
|
@ -4,14 +4,12 @@ include common.mk
|
||||||
VERSION := $(MAJOR).$(MINOR).$(PATCH)
|
VERSION := $(MAJOR).$(MINOR).$(PATCH)
|
||||||
PREFIX ?= /usr/local
|
PREFIX ?= /usr/local
|
||||||
BUILD_DIR := build
|
BUILD_DIR := build
|
||||||
RELEASE_OPTIMIZE_FLAGS ?= -g -O3
|
RELEASE_OPTIMIZE_FLAGS ?= -O3
|
||||||
DEBUG_OPTIMIZE_FLAGS ?= -g -O0
|
DEBUG_OPTIMIZE_FLAGS ?= -g -O0 -U_FORTIFY_SOURCE
|
||||||
JS_OPTIMIZE_FLAGS ?= -O3
|
JS_OPTIMIZE_FLAGS ?= -O3
|
||||||
FUZZING_OPTIMIZE_FLAGS ?= -O3
|
FUZZER_OPTIMIZE_FLAGS ?= -O3
|
||||||
CC = gcc
|
|
||||||
EMCC = emcc
|
EMCC = emcc
|
||||||
AFL_CC = afl-gcc
|
EMAR = emar
|
||||||
AFL_CXX = afl-g++
|
|
||||||
AR = ar
|
AR = ar
|
||||||
|
|
||||||
UNAME := $(shell uname)
|
UNAME := $(shell uname)
|
||||||
|
@ -29,29 +27,36 @@ STATIC_RELEASE_TARGET := $(BUILD_DIR)/libolm.a
|
||||||
DEBUG_TARGET := $(BUILD_DIR)/libolm_debug.$(SO).$(VERSION)
|
DEBUG_TARGET := $(BUILD_DIR)/libolm_debug.$(SO).$(VERSION)
|
||||||
JS_WASM_TARGET := javascript/olm.js
|
JS_WASM_TARGET := javascript/olm.js
|
||||||
JS_ASMJS_TARGET := javascript/olm_legacy.js
|
JS_ASMJS_TARGET := javascript/olm_legacy.js
|
||||||
|
WASM_TARGET := $(BUILD_DIR)/wasm/libolm.a
|
||||||
|
|
||||||
JS_EXPORTED_FUNCTIONS := javascript/exported_functions.json
|
JS_EXPORTED_FUNCTIONS := javascript/exported_functions.json
|
||||||
JS_EXTRA_EXPORTED_RUNTIME_METHODS := ALLOC_STACK
|
JS_EXPORTED_RUNTIME_METHODS := [ALLOC_STACK,writeAsciiToMemory,intArrayFromString,UTF8ToString,stringToUTF8]
|
||||||
JS_EXTERNS := javascript/externs.js
|
JS_EXTERNS := javascript/externs.js
|
||||||
|
|
||||||
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
|
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) \
|
SOURCES := $(wildcard src/*.cpp) $(wildcard src/*.c) \
|
||||||
lib/crypto-algorithms/sha256.c \
|
lib/crypto-algorithms/sha256.c \
|
||||||
lib/crypto-algorithms/aes.c \
|
lib/crypto-algorithms/aes.c \
|
||||||
lib/curve25519-donna/curve25519-donna.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)
|
TEST_SOURCES := $(wildcard tests/test_*.cpp) $(wildcard tests/test_*.c)
|
||||||
|
|
||||||
OBJECTS := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCES)))
|
OBJECTS := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCES)))
|
||||||
RELEASE_OBJECTS := $(addprefix $(BUILD_DIR)/release/,$(OBJECTS))
|
RELEASE_OBJECTS := $(addprefix $(BUILD_DIR)/release/,$(OBJECTS))
|
||||||
DEBUG_OBJECTS := $(addprefix $(BUILD_DIR)/debug/,$(OBJECTS))
|
DEBUG_OBJECTS := $(addprefix $(BUILD_DIR)/debug/,$(OBJECTS))
|
||||||
FUZZER_OBJECTS := $(addprefix $(BUILD_DIR)/fuzzers/objects/,$(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))
|
FUZZER_DEBUG_BINARIES := $(patsubst $(BUILD_DIR)/fuzzers/fuzz_%,$(BUILD_DIR)/fuzzers/debug_%,$(FUZZER_BINARIES))
|
||||||
TEST_BINARIES := $(patsubst tests/%,$(BUILD_DIR)/tests/%,$(basename $(TEST_SOURCES)))
|
TEST_BINARIES := $(patsubst tests/%,$(BUILD_DIR)/tests/%,$(basename $(TEST_SOURCES)))
|
||||||
JS_OBJECTS := $(addprefix $(BUILD_DIR)/javascript/,$(OBJECTS))
|
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.
|
# pre & post are the js-pre/js-post options to emcc.
|
||||||
# They are injected inside the modularised code and
|
# They are injected inside the modularised code and
|
||||||
|
@ -81,11 +86,14 @@ CPPFLAGS += -Iinclude -Ilib \
|
||||||
-DOLMLIB_VERSION_PATCH=$(PATCH)
|
-DOLMLIB_VERSION_PATCH=$(PATCH)
|
||||||
|
|
||||||
# we rely on <stdint.h>, which was introduced in C99
|
# we rely on <stdint.h>, which was introduced in C99
|
||||||
CFLAGS += -Wall -Werror -std=c99 -fPIC
|
CFLAGS += -Wall -Werror -std=c99
|
||||||
CXXFLAGS += -Wall -Werror -std=c++11 -fPIC
|
CXXFLAGS += -Wall -Werror -std=c++11
|
||||||
LDFLAGS += -Wall -Werror
|
LDFLAGS += -Wall -Werror
|
||||||
|
|
||||||
EMCCFLAGS = --closure 1 --memory-init-file 0 -s NO_FILESYSTEM=1 -s INVOKE_RUN=0 -s MODULARIZE=1
|
CFLAGS_NATIVE = -fPIC
|
||||||
|
CXXFLAGS_NATIVE = -fPIC
|
||||||
|
|
||||||
|
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
|
# 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
|
# payloads (ie. Matrix messages), but we do need about 128K of heap to encrypt
|
||||||
|
@ -95,41 +103,63 @@ EMCCFLAGS = --closure 1 --memory-init-file 0 -s NO_FILESYSTEM=1 -s INVOKE_RUN=0
|
||||||
# (This can't be changed by the app with wasm since it's baked into the wasm).
|
# (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
|
# (emscripten also mandates at least 16MB of memory for asm.js now, so
|
||||||
# we don't use this for the legacy build.)
|
# we don't use this for the legacy build.)
|
||||||
EMCCFLAGS_WASM += -s TOTAL_STACK=65536 -s TOTAL_MEMORY=262144
|
EMCCFLAGS_WASM += -s TOTAL_STACK=65536 -s TOTAL_MEMORY=262144 -s ALLOW_MEMORY_GROWTH
|
||||||
|
|
||||||
EMCCFLAGS_ASMJS += -s WASM=0
|
EMCCFLAGS_ASMJS += -s WASM=0
|
||||||
|
|
||||||
EMCC.c = $(EMCC) $(CFLAGS) $(CPPFLAGS) -c
|
EMCC.c = $(EMCC) $(CFLAGS) $(CPPFLAGS) -c -DNDEBUG -DOLM_STATIC_DEFINE=1
|
||||||
EMCC.cc = $(EMCC) $(CXXFLAGS) $(CPPFLAGS) -c
|
EMCC.cc = $(EMCC) $(CXXFLAGS) $(CPPFLAGS) -c -DNDEBUG -DOLM_STATIC_DEFINE=1
|
||||||
EMCC_LINK = $(EMCC) $(LDFLAGS) $(EMCCFLAGS)
|
EMCC_LINK = $(EMCC) $(LDFLAGS) $(EMCCFLAGS)
|
||||||
|
|
||||||
|
AFL_CC = afl-clang-fast
|
||||||
|
AFL_CXX = afl-clang-fast++
|
||||||
|
|
||||||
AFL.c = $(AFL_CC) $(CFLAGS) $(CPPFLAGS) -c
|
AFL.c = $(AFL_CC) $(CFLAGS) $(CPPFLAGS) -c
|
||||||
AFL.cc = $(AFL_CXX) $(CXXFLAGS) $(CPPFLAGS) -c
|
AFL.cc = $(AFL_CXX) $(CXXFLAGS) $(CPPFLAGS) -c
|
||||||
AFL_LINK.c = $(AFL_CC) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS)
|
AFL_LINK.c = $(AFL_CC) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS)
|
||||||
AFL_LINK.cc = $(AFL_CXX) $(LDFLAGS) $(CXXFLAGS) $(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
|
# generate .d files when compiling
|
||||||
CPPFLAGS += -MMD
|
CPPFLAGS += -MMD
|
||||||
|
|
||||||
### per-target variables
|
### per-target variables
|
||||||
|
|
||||||
$(RELEASE_OBJECTS): CFLAGS += $(RELEASE_OPTIMIZE_FLAGS)
|
$(RELEASE_OBJECTS): CFLAGS += $(RELEASE_OPTIMIZE_FLAGS) $(CFLAGS_NATIVE)
|
||||||
$(RELEASE_OBJECTS): CXXFLAGS += $(RELEASE_OPTIMIZE_FLAGS)
|
$(RELEASE_OBJECTS): CXXFLAGS += $(RELEASE_OPTIMIZE_FLAGS) $(CXXFLAGS_NATIVE)
|
||||||
$(RELEASE_TARGET): LDFLAGS += $(RELEASE_OPTIMIZE_FLAGS)
|
$(RELEASE_TARGET): LDFLAGS += $(RELEASE_OPTIMIZE_FLAGS)
|
||||||
|
|
||||||
$(DEBUG_OBJECTS): CFLAGS += $(DEBUG_OPTIMIZE_FLAGS)
|
$(DEBUG_OBJECTS): CFLAGS += $(DEBUG_OPTIMIZE_FLAGS) $(CFLAGS_NATIVE)
|
||||||
$(DEBUG_OBJECTS): CXXFLAGS += $(DEBUG_OPTIMIZE_FLAGS)
|
$(DEBUG_OBJECTS): CXXFLAGS += $(DEBUG_OPTIMIZE_FLAGS) $(CXXFLAGS_NATIVE)
|
||||||
$(DEBUG_TARGET): LDFLAGS += $(DEBUG_OPTIMIZE_FLAGS)
|
$(DEBUG_TARGET): LDFLAGS += $(DEBUG_OPTIMIZE_FLAGS)
|
||||||
|
|
||||||
$(TEST_BINARIES): CPPFLAGS += -Itests/include
|
$(TEST_BINARIES): CPPFLAGS += -Itests/include
|
||||||
$(TEST_BINARIES): LDFLAGS += $(DEBUG_OPTIMIZE_FLAGS) -L$(BUILD_DIR)
|
$(TEST_BINARIES): LDFLAGS += $(DEBUG_OPTIMIZE_FLAGS) -L$(BUILD_DIR)
|
||||||
|
|
||||||
$(FUZZER_OBJECTS): CFLAGS += $(FUZZER_OPTIMIZE_FLAGS)
|
$(FUZZER_OBJECTS): CFLAGS += $(FUZZER_OPTIMIZE_FLAGS) -D OLM_FUZZING=1
|
||||||
$(FUZZER_OBJECTS): CXXFLAGS += $(FUZZER_OPTIMIZE_FLAGS)
|
$(FUZZER_OBJECTS): CXXFLAGS += $(FUZZER_OPTIMIZE_FLAGS) -D OLM_FUZZING=1
|
||||||
$(FUZZER_BINARIES): CPPFLAGS += -Ifuzzers/include
|
$(FUZZER_DEBUG_OBJECTS): CFLAGS += $(DEBUG_OPTIMIZE_FLAGS) $(CFLAGS_NATIVE) -D OLM_FUZZING=1
|
||||||
$(FUZZER_BINARIES): LDFLAGS += $(FUZZER_OPTIMIZE_FLAGS) -L$(BUILD_DIR)
|
$(FUZZER_DEBUG_OBJECTS): CXXFLAGS += $(DEBUG_OPTIMIZE_FLAGS) $(CXXFLAGS_NATIVE) -D OLM_FUZZING=1
|
||||||
$(FUZZER_DEBUG_BINARIES): CPPFLAGS += -Ifuzzers/include
|
$(FUZZER_ASAN_OBJECTS): CFLAGS += $(FUZZER_OPTIMIZE_FLAGS) -D OLM_FUZZING=1
|
||||||
$(FUZZER_DEBUG_BINARIES): LDFLAGS += $(DEBUG_OPTIMIZE_FLAGS)
|
$(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): CFLAGS += $(JS_OPTIMIZE_FLAGS)
|
||||||
$(JS_OBJECTS): CXXFLAGS += $(JS_OPTIMIZE_FLAGS)
|
$(JS_OBJECTS): CXXFLAGS += $(JS_OPTIMIZE_FLAGS)
|
||||||
|
@ -155,6 +185,12 @@ lib: $(RELEASE_TARGET)
|
||||||
.PHONY: lib
|
.PHONY: lib
|
||||||
|
|
||||||
$(RELEASE_TARGET): $(RELEASE_OBJECTS)
|
$(RELEASE_TARGET): $(RELEASE_OBJECTS)
|
||||||
|
@echo
|
||||||
|
@echo '****************************************************************************'
|
||||||
|
@echo '* WARNING: Building olm with make is deprecated. Please use cmake instead. *'
|
||||||
|
@echo '****************************************************************************'
|
||||||
|
@echo
|
||||||
|
|
||||||
$(CXX) $(LDFLAGS) --shared -fPIC \
|
$(CXX) $(LDFLAGS) --shared -fPIC \
|
||||||
$(OLM_LDFLAGS) \
|
$(OLM_LDFLAGS) \
|
||||||
$(OUTPUT_OPTION) $(RELEASE_OBJECTS)
|
$(OUTPUT_OPTION) $(RELEASE_OBJECTS)
|
||||||
|
@ -179,32 +215,39 @@ $(STATIC_RELEASE_TARGET): $(RELEASE_OBJECTS)
|
||||||
js: $(JS_WASM_TARGET) $(JS_ASMJS_TARGET)
|
js: $(JS_WASM_TARGET) $(JS_ASMJS_TARGET)
|
||||||
.PHONY: js
|
.PHONY: js
|
||||||
|
|
||||||
|
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
|
# 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
|
# wasm file baked into the js, hence messing around outputting to olm.js
|
||||||
# and then renaming it.
|
# and then renaming it.
|
||||||
$(JS_WASM_TARGET): $(JS_OBJECTS) $(JS_PRE) $(JS_POST) $(JS_EXPORTED_FUNCTIONS) $(JS_PREFIX) $(JS_SUFFIX)
|
$(JS_WASM_TARGET): $(JS_OBJECTS) $(JS_PRE) $(JS_POST) $(JS_EXPORTED_FUNCTIONS) $(JS_PREFIX) $(JS_SUFFIX)
|
||||||
EMCC_CLOSURE_ARGS="--externs $(JS_EXTERNS)" $(EMCC_LINK) \
|
EMCC_CLOSURE_ARGS="--externs $(CURDIR)/$(JS_EXTERNS)" $(EMCC_LINK) \
|
||||||
$(EMCCFLAGS_WASM) \
|
$(EMCCFLAGS_WASM) \
|
||||||
$(foreach f,$(JS_PRE),--pre-js $(f)) \
|
$(foreach f,$(JS_PRE),--pre-js $(f)) \
|
||||||
$(foreach f,$(JS_POST),--post-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_FUNCTIONS=@$(JS_EXPORTED_FUNCTIONS)" \
|
||||||
-s "EXTRA_EXPORTED_RUNTIME_METHODS=$(JS_EXTRA_EXPORTED_RUNTIME_METHODS)" \
|
-s "EXPORTED_RUNTIME_METHODS=$(JS_EXPORTED_RUNTIME_METHODS)" \
|
||||||
$(JS_OBJECTS) -o $@
|
-o $@ $(JS_OBJECTS)
|
||||||
mv $@ javascript/olmtmp.js
|
|
||||||
cat $(JS_PREFIX) javascript/olmtmp.js $(JS_SUFFIX) > $@
|
|
||||||
rm javascript/olmtmp.js
|
|
||||||
|
|
||||||
$(JS_ASMJS_TARGET): $(JS_OBJECTS) $(JS_PRE) $(JS_POST) $(JS_EXPORTED_FUNCTIONS) $(JS_PREFIX) $(JS_SUFFIX)
|
$(JS_ASMJS_TARGET): $(JS_OBJECTS) $(JS_PRE) $(JS_POST) $(JS_EXPORTED_FUNCTIONS) $(JS_PREFIX) $(JS_SUFFIX)
|
||||||
EMCC_CLOSURE_ARGS="--externs $(JS_EXTERNS)" $(EMCC_LINK) \
|
EMCC_CLOSURE_ARGS="--externs $(CURDIR)/$(JS_EXTERNS)" $(EMCC_LINK) \
|
||||||
$(EMCCFLAGS_ASMJS) \
|
$(EMCCFLAGS_ASMJS) \
|
||||||
$(foreach f,$(JS_PRE),--pre-js $(f)) \
|
$(foreach f,$(JS_PRE),--pre-js $(f)) \
|
||||||
$(foreach f,$(JS_POST),--post-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_FUNCTIONS=@$(JS_EXPORTED_FUNCTIONS)" \
|
||||||
-s "EXTRA_EXPORTED_RUNTIME_METHODS=$(JS_EXTRA_EXPORTED_RUNTIME_METHODS)" \
|
-s "EXPORTED_RUNTIME_METHODS=$(JS_EXPORTED_RUNTIME_METHODS)" \
|
||||||
$(JS_OBJECTS) -o $@
|
-o $@ $(JS_OBJECTS)
|
||||||
mv $@ javascript/olmtmp.js
|
|
||||||
cat $(JS_PREFIX) javascript/olmtmp.js $(JS_SUFFIX) > $@
|
|
||||||
rm javascript/olmtmp.js
|
|
||||||
|
|
||||||
build_tests: $(TEST_BINARIES)
|
build_tests: $(TEST_BINARIES)
|
||||||
|
|
||||||
|
@ -214,7 +257,13 @@ test: build_tests
|
||||||
$$i || exit $$?; \
|
$$i || exit $$?; \
|
||||||
done
|
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
|
.PHONY: fuzzers
|
||||||
|
|
||||||
$(JS_EXPORTED_FUNCTIONS): $(PUBLIC_HEADERS)
|
$(JS_EXPORTED_FUNCTIONS): $(PUBLIC_HEADERS)
|
||||||
|
@ -226,21 +275,21 @@ all: test js lib debug doc
|
||||||
|
|
||||||
install-headers: $(PUBLIC_HEADERS)
|
install-headers: $(PUBLIC_HEADERS)
|
||||||
test -d $(DESTDIR)$(PREFIX)/include/olm || $(call mkdir,$(DESTDIR)$(PREFIX)/include/olm)
|
test -d $(DESTDIR)$(PREFIX)/include/olm || $(call mkdir,$(DESTDIR)$(PREFIX)/include/olm)
|
||||||
install -Dm644 $(PUBLIC_HEADERS) $(DESTDIR)$(PREFIX)/include/olm/
|
install $(PUBLIC_HEADERS) $(DESTDIR)$(PREFIX)/include/olm/
|
||||||
.PHONY: install-headers
|
.PHONY: install-headers
|
||||||
|
|
||||||
install-debug: debug install-headers
|
install-debug: debug install-headers
|
||||||
test -d $(DESTDIR)$(PREFIX)/lib || $(call mkdir,$(DESTDIR)$(PREFIX)/lib)
|
test -d $(DESTDIR)$(PREFIX)/lib || $(call mkdir,$(DESTDIR)$(PREFIX)/lib)
|
||||||
install -Dm755 $(DEBUG_TARGET) $(DESTDIR)$(PREFIX)/lib/libolm_debug.$(SO).$(VERSION)
|
install $(DEBUG_TARGET) $(DESTDIR)$(PREFIX)/lib/libolm_debug.$(SO).$(VERSION)
|
||||||
ln -s libolm_debug.$(SO).$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm_debug.$(SO).$(MAJOR)
|
ln -sf libolm_debug.$(SO).$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm_debug.$(SO).$(MAJOR)
|
||||||
ln -s libolm_debug.$(SO).$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm_debug.$(SO)
|
ln -sf libolm_debug.$(SO).$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm_debug.$(SO)
|
||||||
.PHONY: install-debug
|
.PHONY: install-debug
|
||||||
|
|
||||||
install: lib install-headers
|
install: lib install-headers
|
||||||
test -d $(DESTDIR)$(PREFIX)/lib || $(call mkdir,$(DESTDIR)$(PREFIX)/lib)
|
test -d $(DESTDIR)$(PREFIX)/lib || $(call mkdir,$(DESTDIR)$(PREFIX)/lib)
|
||||||
install -Dm755 $(RELEASE_TARGET) $(DESTDIR)$(PREFIX)/lib/libolm.$(SO).$(VERSION)
|
install $(RELEASE_TARGET) $(DESTDIR)$(PREFIX)/lib/libolm.$(SO).$(VERSION)
|
||||||
ln -s libolm.$(SO).$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm.$(SO).$(MAJOR)
|
ln -sf libolm.$(SO).$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm.$(SO).$(MAJOR)
|
||||||
ln -s libolm.$(SO).$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm.$(SO)
|
ln -sf libolm.$(SO).$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm.$(SO)
|
||||||
.PHONY: install
|
.PHONY: install
|
||||||
|
|
||||||
clean:;
|
clean:;
|
||||||
|
@ -275,13 +324,21 @@ $(BUILD_DIR)/javascript/%.o: %.cpp
|
||||||
$(call mkdir,$(dir $@))
|
$(call mkdir,$(dir $@))
|
||||||
$(EMCC.cc) $(OUTPUT_OPTION) $<
|
$(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)
|
$(BUILD_DIR)/tests/%: tests/%.c $(DEBUG_OBJECTS)
|
||||||
$(call mkdir,$(dir $@))
|
$(call mkdir,$(dir $@))
|
||||||
$(LINK.c) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
|
$(LINK.c) -o $@ $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS)
|
||||||
|
|
||||||
$(BUILD_DIR)/tests/%: tests/%.cpp $(DEBUG_OBJECTS)
|
$(BUILD_DIR)/tests/%: tests/%.cpp $(DEBUG_OBJECTS)
|
||||||
$(call mkdir,$(dir $@))
|
$(call mkdir,$(dir $@))
|
||||||
$(LINK.cc) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
|
$(LINK.cc) -o $@ $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS)
|
||||||
|
|
||||||
$(BUILD_DIR)/fuzzers/objects/%.o: %.c
|
$(BUILD_DIR)/fuzzers/objects/%.o: %.c
|
||||||
$(call mkdir,$(dir $@))
|
$(call mkdir,$(dir $@))
|
||||||
|
@ -291,21 +348,61 @@ $(BUILD_DIR)/fuzzers/objects/%.o: %.cpp
|
||||||
$(call mkdir,$(dir $@))
|
$(call mkdir,$(dir $@))
|
||||||
$(AFL.cc) $(OUTPUT_OPTION) $<
|
$(AFL.cc) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
$(BUILD_DIR)/fuzzers/fuzz_%: fuzzers/fuzz_%.c $(FUZZER_OBJECTS)
|
$(BUILD_DIR)/fuzzers/objects/asan_%.o: %.c
|
||||||
$(AFL_LINK.c) $< $(FUZZER_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
|
$(call mkdir,$(dir $@))
|
||||||
|
$(AFL_ASAN.c) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
$(BUILD_DIR)/fuzzers/fuzz_%: fuzzers/fuzz_%.cpp $(FUZZER_OBJECTS)
|
$(BUILD_DIR)/fuzzers/objects/asan_%.o: %.cpp
|
||||||
$(AFL_LINK.cc) $< $(FUZZER_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
|
$(call mkdir,$(dir $@))
|
||||||
|
$(AFL_ASAN.cc) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
$(BUILD_DIR)/fuzzers/debug_%: fuzzers/fuzz_%.c $(DEBUG_OBJECTS)
|
$(BUILD_DIR)/fuzzers/objects/msan_%.o: %.c
|
||||||
$(LINK.c) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
|
$(call mkdir,$(dir $@))
|
||||||
|
$(AFL_MSAN.c) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
$(BUILD_DIR)/fuzzers/debug_%: fuzzers/fuzz_%.cpp $(DEBUG_OBJECTS)
|
$(BUILD_DIR)/fuzzers/objects/msan_%.o: %.cpp
|
||||||
$(LINK.cc) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
|
$(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
|
%.html: %.rst
|
||||||
rst2html $< $@
|
rst2html $< $@
|
||||||
|
|
||||||
|
%.html: %.md
|
||||||
|
pandoc --from markdown --to html5 --standalone --lua-filter gitlab-math.lua --katex -o $@ $<
|
||||||
|
|
||||||
### dependencies
|
### dependencies
|
||||||
|
|
||||||
-include $(RELEASE_OBJECTS:.o=.d)
|
-include $(RELEASE_OBJECTS:.o=.d)
|
||||||
|
@ -313,5 +410,10 @@ $(BUILD_DIR)/fuzzers/debug_%: fuzzers/fuzz_%.cpp $(DEBUG_OBJECTS)
|
||||||
-include $(JS_OBJECTS:.o=.d)
|
-include $(JS_OBJECTS:.o=.d)
|
||||||
-include $(TEST_BINARIES:=.d)
|
-include $(TEST_BINARIES:=.d)
|
||||||
-include $(FUZZER_OBJECTS:.o=.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_BINARIES:=.d)
|
||||||
|
-include $(FUZZER_ASAN_BINARIES:=.d)
|
||||||
|
-include $(FUZZER_MSAN_BINARIES:=.d)
|
||||||
-include $(FUZZER_DEBUG_BINARIES:=.d)
|
-include $(FUZZER_DEBUG_BINARIES:=.d)
|
||||||
|
|
|
@ -2,8 +2,8 @@ Pod::Spec.new do |s|
|
||||||
|
|
||||||
# The libolm version
|
# The libolm version
|
||||||
MAJOR = 3
|
MAJOR = 3
|
||||||
MINOR = 1
|
MINOR = 2
|
||||||
PATCH = 0
|
PATCH = 16
|
||||||
|
|
||||||
s.name = "OLMKit"
|
s.name = "OLMKit"
|
||||||
s.version = "#{MAJOR}.#{MINOR}.#{PATCH}"
|
s.version = "#{MAJOR}.#{MINOR}.#{PATCH}"
|
||||||
|
|
52
Package.swift
Normal file
52
Package.swift
Normal 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
328
README.md
Normal 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.
|
216
README.rst
216
README.rst
|
@ -1,216 +0,0 @@
|
||||||
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.rst>`_.
|
|
||||||
|
|
||||||
This library also includes an implementation of the Megolm cryptographic
|
|
||||||
ratchet, as specified in `<docs/megolm.rst>`_.
|
|
||||||
|
|
||||||
Building
|
|
||||||
--------
|
|
||||||
|
|
||||||
To build olm as a shared library run either:
|
|
||||||
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
cmake . -Bbuild
|
|
||||||
cmake --build build
|
|
||||||
|
|
||||||
or:
|
|
||||||
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
make
|
|
||||||
|
|
||||||
Using cmake is the preferred method for building the shared library; the
|
|
||||||
Makefile may be removed in the future.
|
|
||||||
|
|
||||||
To run the tests when using cmake, run:
|
|
||||||
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
cd build/tests
|
|
||||||
ctest .
|
|
||||||
|
|
||||||
To run the tests when using make, 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
|
|
||||||
|
|
||||||
Note that if you run emscripten in a docker container, you need to pass through
|
|
||||||
the EMCC_CLOSURE_ARGS environment variable.
|
|
||||||
|
|
||||||
To build the android project for Android bindings, run:
|
|
||||||
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
cd android
|
|
||||||
./gradlew clean assembleRelease
|
|
||||||
|
|
||||||
To build the Xcode workspace for Objective-C bindings, run:
|
|
||||||
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
cd xcode
|
|
||||||
pod install
|
|
||||||
open OLMKit.xcworkspace
|
|
||||||
|
|
||||||
To build the Python bindings, first build olm as a shared library as above, and
|
|
||||||
then run:
|
|
||||||
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
cd python
|
|
||||||
make
|
|
||||||
|
|
||||||
to make both the Python 2 and Python 3 bindings. To make only one version, use
|
|
||||||
``make olm-python2`` or ``make olm-python3`` instead of just ``make``.
|
|
||||||
|
|
||||||
To build olm as a static library (which still needs libstdc++ dynamically) run
|
|
||||||
either:
|
|
||||||
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
cmake . -Bbuild -DBUILD_SHARED_LIBS=NO
|
|
||||||
cmake --build build
|
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
make static
|
|
||||||
|
|
||||||
The library can also be used as a dependency with CMake using:
|
|
||||||
|
|
||||||
.. code:: cmake
|
|
||||||
|
|
||||||
find_package(Olm::Olm REQUIRED)
|
|
||||||
target_link_libraries(my_exe Olm::Olm)
|
|
||||||
|
|
||||||
|
|
||||||
Release process
|
|
||||||
---------------
|
|
||||||
|
|
||||||
First: bump version numbers in ``common.mk``, ``CMakeLists.txt``,
|
|
||||||
``javascript/package.json``, ``python/olm/__version__.py``, ``OLMKit.podspec``,
|
|
||||||
and ``android/olm-sdk/build.gradle`` (``versionCode``, ``versionName`` and
|
|
||||||
``version``).
|
|
||||||
|
|
||||||
Also, ensure the changelog is up to date, and that everyting 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.
|
|
||||||
|
|
||||||
.. code:: bash
|
|
||||||
|
|
||||||
make clean
|
|
||||||
|
|
||||||
# build and test C library
|
|
||||||
make test
|
|
||||||
|
|
||||||
# build and test JS wrapper
|
|
||||||
make js
|
|
||||||
(cd javascript && npm run test)
|
|
||||||
npm pack javascript
|
|
||||||
|
|
||||||
VERSION=x.y.z
|
|
||||||
scp olm-$VERSION.tgz packages@ares.matrix.org:packages/npm/olm/
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
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.rst>`_ 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.trust/us/our-research/matrix-olm-cryptographic-review/
|
|
||||||
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/
|
|
||||||
|
|
||||||
Bug reports
|
|
||||||
-----------
|
|
||||||
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.
|
|
29
Windows64.cmake
Normal file
29
Windows64.cmake
Normal 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++")
|
BIN
android/.DS_Store
vendored
BIN
android/.DS_Store
vendored
Binary file not shown.
46
android/.gitlab-ci.yml
Normal file
46
android/.gitlab-ci.yml
Normal 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
|
|
@ -5,25 +5,21 @@ OlmLibSdk exposes an android wrapper to libolm.
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
------------
|
------------
|
||||||
Create a libs directory in your project directory
|
|
||||||
Copy the olm-sdk.aar into it.
|
|
||||||
|
|
||||||
In your build.gradle file, add in the android section::
|
Android Olm library is released on MavenCentral.
|
||||||
|
|
||||||
repositories {
|
Add this dependency to your project:
|
||||||
flatDir {
|
|
||||||
dir 'libs'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Add in the dependencies category::
|
```groovy
|
||||||
|
implementation "org.matrix.android:olm:3.2.8"
|
||||||
|
```
|
||||||
|
|
||||||
compile(name: 'olm-sdk', ext: 'aar')
|
Latest version: ![Latest version](https://img.shields.io/maven-central/v/org.matrix.android/olm)
|
||||||
|
|
||||||
Development
|
Development
|
||||||
-----------
|
-----------
|
||||||
import the project from the ``android/`` path.
|
import the project from the ``android/`` path.
|
||||||
|
|
||||||
The project contains some JNI files and some Java wraper files.
|
The project contains some JNI files and some Java wrapper files.
|
||||||
|
|
||||||
The project contains some tests under AndroidTests package.
|
The project contains some tests under AndroidTests package.
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
mavenCentral()
|
||||||
google()
|
google()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.1.3'
|
// 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
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
}
|
}
|
||||||
|
@ -15,9 +17,20 @@ buildscript {
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
mavenCentral()
|
||||||
google()
|
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) {
|
task clean(type: Delete) {
|
||||||
|
|
|
@ -19,4 +19,31 @@
|
||||||
#systemProp.https.proxyHost=batproxy
|
#systemProp.https.proxyHost=batproxy
|
||||||
#systemProp.http.proxyPort=8080
|
#systemProp.http.proxyPort=8080
|
||||||
|
|
||||||
|
android.useAndroidX=true
|
||||||
org.gradle.configureondemand=false
|
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
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#Thu Oct 13 09:38:01 CEST 2016
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
|
distributionSha256Sum=c9490e938b221daf0094982288e4038deed954a3f12fb54cbf270ddf4e37d879
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-all.zip
|
|
||||||
|
|
|
@ -1,18 +1,26 @@
|
||||||
import org.apache.tools.ant.taskdefs.condition.Os
|
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.android.library'
|
||||||
|
apply plugin: "com.vanniktech.maven.publish.base"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 27
|
compileSdk 31
|
||||||
buildToolsVersion '27.0.3'
|
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 11
|
minSdk 14
|
||||||
targetSdkVersion 21
|
targetSdk 31
|
||||||
versionCode 310
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
versionName "3.1.0"
|
|
||||||
version "3.1.0"
|
buildConfigField "String", "OLM_VERSION", "\"${project.getProperties().getOrDefault("VERSION_NAME", "0.0.0")}\""
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
|
||||||
|
// 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 {
|
buildTypes {
|
||||||
debug {
|
debug {
|
||||||
|
@ -61,7 +69,8 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(JavaCompile) {
|
tasks.withType(JavaCompile) {
|
||||||
compileTask -> if (compileTask.name.startsWith('compileDebugJava')) {
|
compileTask ->
|
||||||
|
if (compileTask.name.startsWith('compileDebugJava')) {
|
||||||
println 'test compile: Debug'
|
println 'test compile: Debug'
|
||||||
compileTask.dependsOn ndkBuildNativeDebug
|
compileTask.dependsOn ndkBuildNativeDebug
|
||||||
} else if (compileTask.name.startsWith('compileReleaseJava')) {
|
} else if (compileTask.name.startsWith('compileReleaseJava')) {
|
||||||
|
@ -71,6 +80,27 @@ android {
|
||||||
compileTask.dependsOn buildJavaDoc
|
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
|
clean.dependsOn cleanNative
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,6 +112,11 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_11
|
||||||
|
targetCompatibility JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def getNdkFolder() {
|
def getNdkFolder() {
|
||||||
|
@ -118,9 +153,15 @@ def gitRevisionDate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'junit:junit:4.12'
|
androidTestImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'com.android.support:support-annotations:27.1.1'
|
|
||||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
androidTestImplementation 'androidx.test:core:1.4.0'
|
||||||
androidTestImplementation 'com.android.support.test:rules:1.0.2'
|
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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,12 @@
|
||||||
package org.matrix.olm;
|
package org.matrix.olm;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import org.json.JSONObject;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -41,11 +41,13 @@ import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static android.support.test.InstrumentationRegistry.getInstrumentation;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
|
@ -65,7 +67,7 @@ public class OlmAccountTest {
|
||||||
|
|
||||||
String olmLibVersion = mOlmManager.getOlmLibVersion();
|
String olmLibVersion = mOlmManager.getOlmLibVersion();
|
||||||
assertNotNull(olmLibVersion);
|
assertNotNull(olmLibVersion);
|
||||||
String olmSdkVersion = mOlmManager.getDetailedVersion(getInstrumentation().getContext());
|
String olmSdkVersion = mOlmManager.getDetailedVersion(ApplicationProvider.getApplicationContext());
|
||||||
assertNotNull(olmLibVersion);
|
assertNotNull(olmLibVersion);
|
||||||
Log.d(LOG_TAG, "## setUpClass(): Versions - Android Olm SDK = " + olmSdkVersion + " Olm lib =" + olmLibVersion);
|
Log.d(LOG_TAG, "## setUpClass(): Versions - Android Olm SDK = " + olmSdkVersion + " Olm lib =" + olmLibVersion);
|
||||||
}
|
}
|
||||||
|
@ -96,12 +98,12 @@ public class OlmAccountTest {
|
||||||
mOlmAccount = new OlmAccount();
|
mOlmAccount = new OlmAccount();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
assertTrue("OlmAccount failed " + e.getMessage(), false);
|
fail("OlmAccount failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(mOlmAccount);
|
assertNotNull(mOlmAccount);
|
||||||
|
|
||||||
mOlmAccount.releaseAccount();
|
mOlmAccount.releaseAccount();
|
||||||
assertTrue(0 == mOlmAccount.getOlmAccountId());
|
assertEquals(0, mOlmAccount.getOlmAccountId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -110,7 +112,7 @@ public class OlmAccountTest {
|
||||||
mOlmAccount = new OlmAccount();
|
mOlmAccount = new OlmAccount();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
assertTrue("OlmAccount failed " + e.getMessage(), false);
|
fail("OlmAccount failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(mOlmAccount);
|
assertNotNull(mOlmAccount);
|
||||||
mIsAccountCreated = true;
|
mIsAccountCreated = true;
|
||||||
|
@ -134,18 +136,18 @@ public class OlmAccountTest {
|
||||||
try {
|
try {
|
||||||
identityKeys = mOlmAccount.identityKeys();
|
identityKeys = mOlmAccount.identityKeys();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("identityKeys failed " + e.getMessage(), false);
|
fail("identityKeys failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(identityKeys);
|
assertNotNull(identityKeys);
|
||||||
Log.d(LOG_TAG, "## testIdentityKeys Keys=" + identityKeys);
|
Log.d(LOG_TAG, "## testIdentityKeys Keys=" + identityKeys);
|
||||||
|
|
||||||
// is JSON_KEY_FINGER_PRINT_KEY present?
|
// is JSON_KEY_FINGER_PRINT_KEY present?
|
||||||
String fingerPrintKey = TestHelper.getFingerprintKey(identityKeys);
|
String fingerPrintKey = TestHelper.getFingerprintKey(identityKeys);
|
||||||
assertTrue("fingerprint key missing",!TextUtils.isEmpty(fingerPrintKey));
|
assertFalse("fingerprint key missing", TextUtils.isEmpty(fingerPrintKey));
|
||||||
|
|
||||||
// is JSON_KEY_IDENTITY_KEY present?
|
// is JSON_KEY_IDENTITY_KEY present?
|
||||||
String identityKey = TestHelper.getIdentityKey(identityKeys);
|
String identityKey = TestHelper.getIdentityKey(identityKeys);
|
||||||
assertTrue("identity key missing",!TextUtils.isEmpty(identityKey));
|
assertFalse("identity key missing", TextUtils.isEmpty(identityKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
//****************************************************
|
//****************************************************
|
||||||
|
@ -172,7 +174,7 @@ public class OlmAccountTest {
|
||||||
error = e.getMessage();
|
error = e.getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(null == error);
|
assertNull(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -186,21 +188,21 @@ public class OlmAccountTest {
|
||||||
try {
|
try {
|
||||||
oneTimeKeysJson = mOlmAccount.oneTimeKeys();
|
oneTimeKeysJson = mOlmAccount.oneTimeKeys();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(oneTimeKeysJson);
|
assertNotNull(oneTimeKeysJson);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Map<String, String> map = oneTimeKeysJson.get(OlmAccount.JSON_KEY_ONE_TIME_KEY);
|
Map<String, String> map = oneTimeKeysJson.get(OlmAccount.JSON_KEY_ONE_TIME_KEY);
|
||||||
assertTrue(OlmAccount.JSON_KEY_ONE_TIME_KEY +" object is missing", null!=map);
|
assertNotNull(OlmAccount.JSON_KEY_ONE_TIME_KEY + " object is missing", map);
|
||||||
|
|
||||||
// test the count of the generated one time keys:
|
// test the count of the generated one time keys:
|
||||||
oneTimeKeysCount = map.size();
|
oneTimeKeysCount = map.size();
|
||||||
|
|
||||||
assertTrue("Expected count="+GENERATION_ONE_TIME_KEYS_NUMBER+" found="+oneTimeKeysCount,GENERATION_ONE_TIME_KEYS_NUMBER==oneTimeKeysCount);
|
assertEquals("Expected count=" + GENERATION_ONE_TIME_KEYS_NUMBER + " found=" + oneTimeKeysCount, GENERATION_ONE_TIME_KEYS_NUMBER, oneTimeKeysCount);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("Exception MSg="+e.getMessage(), false);
|
fail("Exception MSg=" + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,7 +212,7 @@ public class OlmAccountTest {
|
||||||
try {
|
try {
|
||||||
olmSession = new OlmSession();
|
olmSession = new OlmSession();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception Msg="+e.getMessage(), false);
|
fail("Exception Msg=" + e.getMessage());
|
||||||
}
|
}
|
||||||
long sessionId = olmSession.getOlmSessionId();
|
long sessionId = olmSession.getOlmSessionId();
|
||||||
assertTrue(0 != sessionId);
|
assertTrue(0 != sessionId);
|
||||||
|
@ -222,11 +224,11 @@ public class OlmAccountTest {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
errorMessage = e.getMessage();
|
errorMessage = e.getMessage();
|
||||||
}
|
}
|
||||||
assertTrue(null != errorMessage);
|
assertNotNull(errorMessage);
|
||||||
|
|
||||||
olmSession.releaseSession();
|
olmSession.releaseSession();
|
||||||
sessionId = olmSession.getOlmSessionId();
|
sessionId = olmSession.getOlmSessionId();
|
||||||
assertTrue(0 == sessionId);
|
assertEquals(0, sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -234,7 +236,7 @@ public class OlmAccountTest {
|
||||||
try {
|
try {
|
||||||
mOlmAccount.markOneTimeKeysAsPublished();
|
mOlmAccount.markOneTimeKeysAsPublished();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,7 +248,7 @@ public class OlmAccountTest {
|
||||||
try {
|
try {
|
||||||
signedMsg = mOlmAccount.signMessage(clearMsg);
|
signedMsg = mOlmAccount.signMessage(clearMsg);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertNotNull(signedMsg);
|
assertNotNull(signedMsg);
|
||||||
|
@ -263,18 +265,18 @@ public class OlmAccountTest {
|
||||||
FileOutputStream fileOutput;
|
FileOutputStream fileOutput;
|
||||||
ObjectOutputStream objectOutput;
|
ObjectOutputStream objectOutput;
|
||||||
OlmAccount accountRef = null;
|
OlmAccount accountRef = null;
|
||||||
OlmAccount accountDeserial = null;
|
OlmAccount accountDeserial;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
accountRef = new OlmAccount();
|
accountRef = new OlmAccount();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue(e.getMessage(),false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
accountRef.generateOneTimeKeys(GENERATION_ONE_TIME_KEYS_NUMBER);
|
accountRef.generateOneTimeKeys(GENERATION_ONE_TIME_KEYS_NUMBER);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(),false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// get keys references
|
// get keys references
|
||||||
|
@ -283,7 +285,7 @@ public class OlmAccountTest {
|
||||||
try {
|
try {
|
||||||
identityKeysRef = accountRef.identityKeys();
|
identityKeysRef = accountRef.identityKeys();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("identityKeys failed " + e.getMessage(), false);
|
fail("identityKeys failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Map<String, String>> oneTimeKeysRef = null;
|
Map<String, Map<String, String>> oneTimeKeysRef = null;
|
||||||
|
@ -291,14 +293,14 @@ public class OlmAccountTest {
|
||||||
try {
|
try {
|
||||||
oneTimeKeysRef = accountRef.oneTimeKeys();
|
oneTimeKeysRef = accountRef.oneTimeKeys();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertNotNull(identityKeysRef);
|
assertNotNull(identityKeysRef);
|
||||||
assertNotNull(oneTimeKeysRef);
|
assertNotNull(oneTimeKeysRef);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Context context = getInstrumentation().getContext();
|
Context context = ApplicationProvider.getApplicationContext();
|
||||||
//context.getFilesDir();
|
//context.getFilesDir();
|
||||||
fileOutput = context.openFileOutput(FILE_NAME, Context.MODE_PRIVATE);
|
fileOutput = context.openFileOutput(FILE_NAME, Context.MODE_PRIVATE);
|
||||||
|
|
||||||
|
@ -322,32 +324,28 @@ public class OlmAccountTest {
|
||||||
assertNotNull(oneTimeKeysDeserial);
|
assertNotNull(oneTimeKeysDeserial);
|
||||||
|
|
||||||
// compare identity keys
|
// compare identity keys
|
||||||
assertTrue(identityKeysDeserial.toString().equals(identityKeysRef.toString()));
|
assertEquals(identityKeysDeserial.toString(), identityKeysRef.toString());
|
||||||
|
|
||||||
// compare onetime keys
|
// compare onetime keys
|
||||||
assertTrue(oneTimeKeysDeserial.toString().equals(oneTimeKeysRef.toString()));
|
assertEquals(oneTimeKeysDeserial.toString(), oneTimeKeysRef.toString());
|
||||||
|
|
||||||
accountRef.releaseAccount();
|
accountRef.releaseAccount();
|
||||||
accountDeserial.releaseAccount();
|
accountDeserial.releaseAccount();
|
||||||
}
|
} catch (FileNotFoundException e) {
|
||||||
catch (FileNotFoundException e) {
|
|
||||||
Log.e(LOG_TAG, "## test13Serialization(): Exception FileNotFoundException Msg==" + e.getMessage());
|
Log.e(LOG_TAG, "## test13Serialization(): Exception FileNotFoundException Msg==" + e.getMessage());
|
||||||
assertTrue("test13Serialization failed " + e.getMessage(), false);
|
fail("test13Serialization failed " + e.getMessage());
|
||||||
}
|
} catch (ClassNotFoundException e) {
|
||||||
catch (ClassNotFoundException e) {
|
|
||||||
Log.e(LOG_TAG, "## test13Serialization(): Exception ClassNotFoundException Msg==" + e.getMessage());
|
Log.e(LOG_TAG, "## test13Serialization(): Exception ClassNotFoundException Msg==" + e.getMessage());
|
||||||
assertTrue("test13Serialization failed " + e.getMessage(), false);
|
fail("test13Serialization failed " + e.getMessage());
|
||||||
}
|
} catch (IOException e) {
|
||||||
catch (IOException e) {
|
|
||||||
Log.e(LOG_TAG, "## test13Serialization(): Exception IOException Msg==" + e.getMessage());
|
Log.e(LOG_TAG, "## test13Serialization(): Exception IOException Msg==" + e.getMessage());
|
||||||
assertTrue("test13Serialization failed " + e.getMessage(), false);
|
fail("test13Serialization failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
/*catch (OlmException e) {
|
/*catch (OlmException e) {
|
||||||
Log.e(LOG_TAG, "## test13Serialization(): Exception OlmException Msg==" + e.getMessage());
|
Log.e(LOG_TAG, "## test13Serialization(): Exception OlmException Msg==" + e.getMessage());
|
||||||
}*/
|
}*/ catch (Exception e) {
|
||||||
catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## test13Serialization(): Exception Msg==" + e.getMessage());
|
Log.e(LOG_TAG, "## test13Serialization(): Exception Msg==" + e.getMessage());
|
||||||
assertTrue("test13Serialization failed " + e.getMessage(), false);
|
fail("test13Serialization failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,7 +365,7 @@ public class OlmAccountTest {
|
||||||
errorMessage = e.getMessage();
|
errorMessage = e.getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(null == errorMessage);
|
assertNull(errorMessage);
|
||||||
|
|
||||||
// keys number = negative value
|
// keys number = negative value
|
||||||
errorMessage = null;
|
errorMessage = null;
|
||||||
|
@ -377,7 +375,7 @@ public class OlmAccountTest {
|
||||||
errorMessage = e.getMessage();
|
errorMessage = e.getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(null != errorMessage);
|
assertNotNull(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -386,13 +384,13 @@ public class OlmAccountTest {
|
||||||
try {
|
try {
|
||||||
olmAccount = new OlmAccount();
|
olmAccount = new OlmAccount();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue(e.getMessage(),false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
olmAccount.removeOneTimeKeys(null);
|
olmAccount.removeOneTimeKeys(null);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
olmAccount.releaseAccount();
|
olmAccount.releaseAccount();
|
||||||
|
@ -404,7 +402,7 @@ public class OlmAccountTest {
|
||||||
try {
|
try {
|
||||||
olmAccount = new OlmAccount();
|
olmAccount = new OlmAccount();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue(e.getMessage(),false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
String signedMsg = null;
|
String signedMsg = null;
|
||||||
|
|
||||||
|
@ -449,31 +447,31 @@ public class OlmAccountTest {
|
||||||
|
|
||||||
String identityKey1 = TestHelper.getIdentityKey(identityKeys1);
|
String identityKey1 = TestHelper.getIdentityKey(identityKeys1);
|
||||||
String identityKey2 = TestHelper.getIdentityKey(identityKeys2);
|
String identityKey2 = TestHelper.getIdentityKey(identityKeys2);
|
||||||
assertFalse(identityKey1.equals(identityKey2));
|
assertNotEquals(identityKey1, identityKey2);
|
||||||
|
|
||||||
String identityKey3 = TestHelper.getIdentityKey(identityKeys3);
|
String identityKey3 = TestHelper.getIdentityKey(identityKeys3);
|
||||||
assertFalse(identityKey2.equals(identityKey3));
|
assertNotEquals(identityKey2, identityKey3);
|
||||||
|
|
||||||
String identityKey4 = TestHelper.getIdentityKey(identityKeys4);
|
String identityKey4 = TestHelper.getIdentityKey(identityKeys4);
|
||||||
assertFalse(identityKey3.equals(identityKey4));
|
assertNotEquals(identityKey3, identityKey4);
|
||||||
|
|
||||||
String identityKey5 = TestHelper.getIdentityKey(identityKeys5);
|
String identityKey5 = TestHelper.getIdentityKey(identityKeys5);
|
||||||
assertFalse(identityKey4.equals(identityKey5));
|
assertNotEquals(identityKey4, identityKey5);
|
||||||
|
|
||||||
String identityKey6 = TestHelper.getIdentityKey(identityKeys6);
|
String identityKey6 = TestHelper.getIdentityKey(identityKeys6);
|
||||||
assertFalse(identityKey5.equals(identityKey6));
|
assertNotEquals(identityKey5, identityKey6);
|
||||||
|
|
||||||
String identityKey7 = TestHelper.getIdentityKey(identityKeys7);
|
String identityKey7 = TestHelper.getIdentityKey(identityKeys7);
|
||||||
assertFalse(identityKey6.equals(identityKey7));
|
assertNotEquals(identityKey6, identityKey7);
|
||||||
|
|
||||||
String identityKey8 = TestHelper.getIdentityKey(identityKeys8);
|
String identityKey8 = TestHelper.getIdentityKey(identityKeys8);
|
||||||
assertFalse(identityKey7.equals(identityKey8));
|
assertNotEquals(identityKey7, identityKey8);
|
||||||
|
|
||||||
String identityKey9 = TestHelper.getIdentityKey(identityKeys9);
|
String identityKey9 = TestHelper.getIdentityKey(identityKeys9);
|
||||||
assertFalse(identityKey8.equals(identityKey9));
|
assertNotEquals(identityKey8, identityKey9);
|
||||||
|
|
||||||
String identityKey10 = TestHelper.getIdentityKey(identityKeys10);
|
String identityKey10 = TestHelper.getIdentityKey(identityKeys10);
|
||||||
assertFalse(identityKey9.equals(identityKey10));
|
assertNotEquals(identityKey9, identityKey10);
|
||||||
|
|
||||||
account1.releaseAccount();
|
account1.releaseAccount();
|
||||||
account2.releaseAccount();
|
account2.releaseAccount();
|
||||||
|
@ -487,7 +485,22 @@ public class OlmAccountTest {
|
||||||
account10.releaseAccount();
|
account10.releaseAccount();
|
||||||
|
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue(e.getMessage(),false);
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,12 @@
|
||||||
package org.matrix.olm;
|
package org.matrix.olm;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.FixMethodOrder;
|
import org.junit.FixMethodOrder;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -36,10 +38,12 @@ import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
|
|
||||||
import static android.support.test.InstrumentationRegistry.getInstrumentation;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
|
@ -83,7 +87,7 @@ public class OlmGroupSessionTest {
|
||||||
try {
|
try {
|
||||||
mAliceOutboundGroupSession = new OlmOutboundGroupSession();
|
mAliceOutboundGroupSession = new OlmOutboundGroupSession();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode(), false);
|
fail("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +99,7 @@ public class OlmGroupSessionTest {
|
||||||
try {
|
try {
|
||||||
mAliceSessionIdentifier = mAliceOutboundGroupSession.sessionIdentifier();
|
mAliceSessionIdentifier = mAliceOutboundGroupSession.sessionIdentifier();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertNotNull(mAliceSessionIdentifier);
|
assertNotNull(mAliceSessionIdentifier);
|
||||||
|
@ -110,7 +114,7 @@ public class OlmGroupSessionTest {
|
||||||
try {
|
try {
|
||||||
mAliceOutboundSessionKey = mAliceOutboundGroupSession.sessionKey();
|
mAliceOutboundSessionKey = mAliceOutboundGroupSession.sessionKey();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(mAliceOutboundSessionKey);
|
assertNotNull(mAliceOutboundSessionKey);
|
||||||
assertTrue(mAliceOutboundSessionKey.length() > 0);
|
assertTrue(mAliceOutboundSessionKey.length() > 0);
|
||||||
|
@ -120,7 +124,7 @@ public class OlmGroupSessionTest {
|
||||||
public void test04GetOutboundGroupMessageIndex() {
|
public void test04GetOutboundGroupMessageIndex() {
|
||||||
// test message index before any encryption
|
// test message index before any encryption
|
||||||
mAliceMessageIndex = mAliceOutboundGroupSession.messageIndex();
|
mAliceMessageIndex = mAliceOutboundGroupSession.messageIndex();
|
||||||
assertTrue(0 == mAliceMessageIndex);
|
assertEquals(0, mAliceMessageIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -129,13 +133,13 @@ public class OlmGroupSessionTest {
|
||||||
try {
|
try {
|
||||||
mAliceToBobMessage = mAliceOutboundGroupSession.encryptMessage(CLEAR_MESSAGE1);
|
mAliceToBobMessage = mAliceOutboundGroupSession.encryptMessage(CLEAR_MESSAGE1);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("Exception in bob encryptMessage, Exception code=" + e.getMessage(), false);
|
fail("Exception in bob encryptMessage, Exception code=" + e.getMessage());
|
||||||
}
|
}
|
||||||
assertFalse(TextUtils.isEmpty(mAliceToBobMessage));
|
assertFalse(TextUtils.isEmpty(mAliceToBobMessage));
|
||||||
|
|
||||||
// test message index after encryption is incremented
|
// test message index after encryption is incremented
|
||||||
mAliceMessageIndex = mAliceOutboundGroupSession.messageIndex();
|
mAliceMessageIndex = mAliceOutboundGroupSession.messageIndex();
|
||||||
assertTrue(1 == mAliceMessageIndex);
|
assertEquals(1, mAliceMessageIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -144,7 +148,7 @@ public class OlmGroupSessionTest {
|
||||||
try {
|
try {
|
||||||
mBobInboundGroupSession = new OlmInboundGroupSession(mAliceOutboundSessionKey);
|
mBobInboundGroupSession = new OlmInboundGroupSession(mAliceOutboundSessionKey);
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception in bob OlmInboundGroupSession, Exception code=" + e.getExceptionCode(), false);
|
fail("Exception in bob OlmInboundGroupSession, Exception code=" + e.getExceptionCode());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +160,7 @@ public class OlmGroupSessionTest {
|
||||||
try {
|
try {
|
||||||
mBobSessionIdentifier = mBobInboundGroupSession.sessionIdentifier();
|
mBobSessionIdentifier = mBobInboundGroupSession.sessionIdentifier();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertFalse(TextUtils.isEmpty(mBobSessionIdentifier));
|
assertFalse(TextUtils.isEmpty(mBobSessionIdentifier));
|
||||||
}
|
}
|
||||||
|
@ -164,7 +168,7 @@ public class OlmGroupSessionTest {
|
||||||
@Test
|
@Test
|
||||||
public void test09SessionIdentifiersAreIdentical() {
|
public void test09SessionIdentifiersAreIdentical() {
|
||||||
// check both session identifiers are equals: alice vs bob
|
// check both session identifiers are equals: alice vs bob
|
||||||
assertTrue(mAliceSessionIdentifier.equals(mBobSessionIdentifier));
|
assertEquals(mAliceSessionIdentifier, mBobSessionIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -175,19 +179,19 @@ public class OlmGroupSessionTest {
|
||||||
try {
|
try {
|
||||||
result = mBobInboundGroupSession.decryptMessage(mAliceToBobMessage);
|
result = mBobInboundGroupSession.decryptMessage(mAliceToBobMessage);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// test decrypted message
|
// test decrypted message
|
||||||
mBobDecryptedMessage = result.mDecryptedMessage;
|
mBobDecryptedMessage = result.mDecryptedMessage;
|
||||||
assertFalse(TextUtils.isEmpty(mBobDecryptedMessage));
|
assertFalse(TextUtils.isEmpty(mBobDecryptedMessage));
|
||||||
assertTrue(0 == result.mIndex);
|
assertEquals(0, result.mIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test11InboundDecryptedMessageIdentical() {
|
public void test11InboundDecryptedMessageIdentical() {
|
||||||
// test decrypted message
|
// test decrypted message
|
||||||
assertTrue(mBobDecryptedMessage.equals(CLEAR_MESSAGE1));
|
assertEquals(mBobDecryptedMessage, CLEAR_MESSAGE1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -217,13 +221,13 @@ public class OlmGroupSessionTest {
|
||||||
try {
|
try {
|
||||||
outboundGroupSessionRef = new OlmOutboundGroupSession();
|
outboundGroupSessionRef = new OlmOutboundGroupSession();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode(), false);
|
fail("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode());
|
||||||
}
|
}
|
||||||
assertNotNull(outboundGroupSessionRef);
|
assertNotNull(outboundGroupSessionRef);
|
||||||
|
|
||||||
|
|
||||||
// serialize alice session
|
// serialize alice session
|
||||||
Context context = getInstrumentation().getContext();
|
Context context = ApplicationProvider.getApplicationContext();
|
||||||
try {
|
try {
|
||||||
FileOutputStream fileOutput = context.openFileOutput(FILE_NAME_SERIAL_OUT_SESSION, Context.MODE_PRIVATE);
|
FileOutputStream fileOutput = context.openFileOutput(FILE_NAME_SERIAL_OUT_SESSION, Context.MODE_PRIVATE);
|
||||||
ObjectOutputStream objectOutput = new ObjectOutputStream(fileOutput);
|
ObjectOutputStream objectOutput = new ObjectOutputStream(fileOutput);
|
||||||
|
@ -245,7 +249,7 @@ public class OlmGroupSessionTest {
|
||||||
assertFalse(TextUtils.isEmpty(sessionKeySerial));
|
assertFalse(TextUtils.isEmpty(sessionKeySerial));
|
||||||
|
|
||||||
// session keys comparison
|
// session keys comparison
|
||||||
assertTrue(sessionKeyRef.equals(sessionKeySerial));
|
assertEquals(sessionKeyRef, sessionKeySerial);
|
||||||
|
|
||||||
// get sessions IDs
|
// get sessions IDs
|
||||||
String sessionIdRef = outboundGroupSessionRef.sessionIdentifier();
|
String sessionIdRef = outboundGroupSessionRef.sessionIdentifier();
|
||||||
|
@ -254,7 +258,7 @@ public class OlmGroupSessionTest {
|
||||||
assertFalse(TextUtils.isEmpty(sessionIdSerial));
|
assertFalse(TextUtils.isEmpty(sessionIdSerial));
|
||||||
|
|
||||||
// session IDs comparison
|
// session IDs comparison
|
||||||
assertTrue(sessionIdRef.equals(sessionIdSerial));
|
assertEquals(sessionIdRef, sessionIdSerial);
|
||||||
|
|
||||||
outboundGroupSessionRef.releaseSession();
|
outboundGroupSessionRef.releaseSession();
|
||||||
outboundGroupSessionSerial.releaseSession();
|
outboundGroupSessionSerial.releaseSession();
|
||||||
|
@ -263,19 +267,19 @@ public class OlmGroupSessionTest {
|
||||||
assertTrue(outboundGroupSessionSerial.isReleased());
|
assertTrue(outboundGroupSessionSerial.isReleased());
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception FileNotFoundException Msg=="+e.getMessage());
|
Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception FileNotFoundException Msg=="+e.getMessage());
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception ClassNotFoundException Msg==" + e.getMessage());
|
Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception ClassNotFoundException Msg==" + e.getMessage());
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception OlmException Msg==" + e.getMessage());
|
Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception OlmException Msg==" + e.getMessage());
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception IOException Msg==" + e.getMessage());
|
Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception IOException Msg==" + e.getMessage());
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception Msg==" + e.getMessage());
|
Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception Msg==" + e.getMessage());
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,7 +293,7 @@ public class OlmGroupSessionTest {
|
||||||
try {
|
try {
|
||||||
aliceOutboundGroupSession = new OlmOutboundGroupSession();
|
aliceOutboundGroupSession = new OlmOutboundGroupSession();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode(), false);
|
fail("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode());
|
||||||
}
|
}
|
||||||
assertNotNull(aliceOutboundGroupSession);
|
assertNotNull(aliceOutboundGroupSession);
|
||||||
|
|
||||||
|
@ -299,7 +303,7 @@ public class OlmGroupSessionTest {
|
||||||
try {
|
try {
|
||||||
sessionKeyRef = aliceOutboundGroupSession.sessionKey();
|
sessionKeyRef = aliceOutboundGroupSession.sessionKey();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(sessionKeyRef);
|
assertNotNull(sessionKeyRef);
|
||||||
|
|
||||||
|
@ -307,12 +311,12 @@ public class OlmGroupSessionTest {
|
||||||
try {
|
try {
|
||||||
bobInboundGroupSessionRef = new OlmInboundGroupSession(sessionKeyRef);
|
bobInboundGroupSessionRef = new OlmInboundGroupSession(sessionKeyRef);
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception in OlmInboundGroupSession, Exception code=" + e.getExceptionCode(), false);
|
fail("Exception in OlmInboundGroupSession, Exception code=" + e.getExceptionCode());
|
||||||
}
|
}
|
||||||
assertNotNull(bobInboundGroupSessionRef);
|
assertNotNull(bobInboundGroupSessionRef);
|
||||||
|
|
||||||
// serialize alice session
|
// serialize alice session
|
||||||
Context context = getInstrumentation().getContext();
|
Context context = ApplicationProvider.getApplicationContext();
|
||||||
try {
|
try {
|
||||||
FileOutputStream fileOutput = context.openFileOutput(FILE_NAME_SERIAL_IN_SESSION, Context.MODE_PRIVATE);
|
FileOutputStream fileOutput = context.openFileOutput(FILE_NAME_SERIAL_IN_SESSION, Context.MODE_PRIVATE);
|
||||||
ObjectOutputStream objectOutput = new ObjectOutputStream(fileOutput);
|
ObjectOutputStream objectOutput = new ObjectOutputStream(fileOutput);
|
||||||
|
@ -336,8 +340,8 @@ public class OlmGroupSessionTest {
|
||||||
assertFalse(TextUtils.isEmpty(sessionIdSerial));
|
assertFalse(TextUtils.isEmpty(sessionIdSerial));
|
||||||
|
|
||||||
// session IDs comparison
|
// session IDs comparison
|
||||||
assertTrue(aliceSessionId.equals(sessionIdSerial));
|
assertEquals(aliceSessionId, sessionIdSerial);
|
||||||
assertTrue(sessionIdRef.equals(sessionIdSerial));
|
assertEquals(sessionIdRef, sessionIdSerial);
|
||||||
|
|
||||||
aliceOutboundGroupSession.releaseSession();
|
aliceOutboundGroupSession.releaseSession();
|
||||||
bobInboundGroupSessionRef.releaseSession();
|
bobInboundGroupSessionRef.releaseSession();
|
||||||
|
@ -348,19 +352,19 @@ public class OlmGroupSessionTest {
|
||||||
assertTrue(bobInboundGroupSessionSerial.isReleased());
|
assertTrue(bobInboundGroupSessionSerial.isReleased());
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception FileNotFoundException Msg=="+e.getMessage());
|
Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception FileNotFoundException Msg=="+e.getMessage());
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception ClassNotFoundException Msg==" + e.getMessage());
|
Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception ClassNotFoundException Msg==" + e.getMessage());
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception OlmException Msg==" + e.getMessage());
|
Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception OlmException Msg==" + e.getMessage());
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception IOException Msg==" + e.getMessage());
|
Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception IOException Msg==" + e.getMessage());
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception Msg==" + e.getMessage());
|
Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception Msg==" + e.getMessage());
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,48 +396,48 @@ public class OlmGroupSessionTest {
|
||||||
// get the session key from the outbound group sessions
|
// get the session key from the outbound group sessions
|
||||||
String sessionKey1 = outboundGroupSession1.sessionKey();
|
String sessionKey1 = outboundGroupSession1.sessionKey();
|
||||||
String sessionKey2 = outboundGroupSession2.sessionKey();
|
String sessionKey2 = outboundGroupSession2.sessionKey();
|
||||||
assertFalse(sessionKey1.equals(sessionKey2));
|
assertNotEquals(sessionKey1, sessionKey2);
|
||||||
|
|
||||||
String sessionKey3 = outboundGroupSession3.sessionKey();
|
String sessionKey3 = outboundGroupSession3.sessionKey();
|
||||||
assertFalse(sessionKey2.equals(sessionKey3));
|
assertNotEquals(sessionKey2, sessionKey3);
|
||||||
|
|
||||||
String sessionKey4 = outboundGroupSession4.sessionKey();
|
String sessionKey4 = outboundGroupSession4.sessionKey();
|
||||||
assertFalse(sessionKey3.equals(sessionKey4));
|
assertNotEquals(sessionKey3, sessionKey4);
|
||||||
|
|
||||||
String sessionKey5 = outboundGroupSession5.sessionKey();
|
String sessionKey5 = outboundGroupSession5.sessionKey();
|
||||||
assertFalse(sessionKey4.equals(sessionKey5));
|
assertNotEquals(sessionKey4, sessionKey5);
|
||||||
|
|
||||||
String sessionKey6 = outboundGroupSession6.sessionKey();
|
String sessionKey6 = outboundGroupSession6.sessionKey();
|
||||||
assertFalse(sessionKey5.equals(sessionKey6));
|
assertNotEquals(sessionKey5, sessionKey6);
|
||||||
|
|
||||||
String sessionKey7 = outboundGroupSession7.sessionKey();
|
String sessionKey7 = outboundGroupSession7.sessionKey();
|
||||||
assertFalse(sessionKey6.equals(sessionKey7));
|
assertNotEquals(sessionKey6, sessionKey7);
|
||||||
|
|
||||||
String sessionKey8 = outboundGroupSession8.sessionKey();
|
String sessionKey8 = outboundGroupSession8.sessionKey();
|
||||||
assertFalse(sessionKey7.equals(sessionKey8));
|
assertNotEquals(sessionKey7, sessionKey8);
|
||||||
|
|
||||||
// get the session IDs from the outbound group sessions
|
// get the session IDs from the outbound group sessions
|
||||||
String sessionId1 = outboundGroupSession1.sessionIdentifier();
|
String sessionId1 = outboundGroupSession1.sessionIdentifier();
|
||||||
String sessionId2 = outboundGroupSession2.sessionIdentifier();
|
String sessionId2 = outboundGroupSession2.sessionIdentifier();
|
||||||
assertFalse(sessionId1.equals(sessionId2));
|
assertNotEquals(sessionId1, sessionId2);
|
||||||
|
|
||||||
String sessionId3 = outboundGroupSession3.sessionKey();
|
String sessionId3 = outboundGroupSession3.sessionKey();
|
||||||
assertFalse(sessionId2.equals(sessionId3));
|
assertNotEquals(sessionId2, sessionId3);
|
||||||
|
|
||||||
String sessionId4 = outboundGroupSession4.sessionKey();
|
String sessionId4 = outboundGroupSession4.sessionKey();
|
||||||
assertFalse(sessionId3.equals(sessionId4));
|
assertNotEquals(sessionId3, sessionId4);
|
||||||
|
|
||||||
String sessionId5 = outboundGroupSession5.sessionKey();
|
String sessionId5 = outboundGroupSession5.sessionKey();
|
||||||
assertFalse(sessionId4.equals(sessionId5));
|
assertNotEquals(sessionId4, sessionId5);
|
||||||
|
|
||||||
String sessionId6 = outboundGroupSession6.sessionKey();
|
String sessionId6 = outboundGroupSession6.sessionKey();
|
||||||
assertFalse(sessionId5.equals(sessionId6));
|
assertNotEquals(sessionId5, sessionId6);
|
||||||
|
|
||||||
String sessionId7 = outboundGroupSession7.sessionKey();
|
String sessionId7 = outboundGroupSession7.sessionKey();
|
||||||
assertFalse(sessionId6.equals(sessionId7));
|
assertNotEquals(sessionId6, sessionId7);
|
||||||
|
|
||||||
String sessionId8 = outboundGroupSession8.sessionKey();
|
String sessionId8 = outboundGroupSession8.sessionKey();
|
||||||
assertFalse(sessionId7.equals(sessionId8));
|
assertNotEquals(sessionId7, sessionId8);
|
||||||
|
|
||||||
outboundGroupSession1.releaseSession();
|
outboundGroupSession1.releaseSession();
|
||||||
outboundGroupSession2.releaseSession();
|
outboundGroupSession2.releaseSession();
|
||||||
|
@ -453,7 +457,7 @@ public class OlmGroupSessionTest {
|
||||||
assertTrue(outboundGroupSession7.isReleased());
|
assertTrue(outboundGroupSession7.isReleased());
|
||||||
assertTrue(outboundGroupSession8.isReleased());
|
assertTrue(outboundGroupSession8.isReleased());
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode(), false);
|
fail("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -476,7 +480,7 @@ public class OlmGroupSessionTest {
|
||||||
try {
|
try {
|
||||||
bobInboundGroupSession = new OlmInboundGroupSession(sessionKeyRef);
|
bobInboundGroupSession = new OlmInboundGroupSession(sessionKeyRef);
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception in test18TestBadCharacterCrashInDecrypt, Exception code=" + e.getExceptionCode(), false);
|
fail("Exception in test18TestBadCharacterCrashInDecrypt, Exception code=" + e.getExceptionCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
OlmInboundGroupSession.DecryptMessageResult result = null;
|
OlmInboundGroupSession.DecryptMessageResult result = null;
|
||||||
|
@ -484,11 +488,11 @@ public class OlmGroupSessionTest {
|
||||||
try {
|
try {
|
||||||
result = bobInboundGroupSession.decryptMessage(msgToDecryptWithEmoji);
|
result = bobInboundGroupSession.decryptMessage(msgToDecryptWithEmoji);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("Exception in test18TestBadCharacterCrashInDecrypt, Exception code=" + e.getMessage(), false);
|
fail("Exception in test18TestBadCharacterCrashInDecrypt, Exception code=" + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertNotNull(result.mDecryptedMessage);
|
assertNotNull(result.mDecryptedMessage);
|
||||||
assertTrue(13 == result.mIndex);
|
assertEquals(13, result.mIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -508,7 +512,7 @@ public class OlmGroupSessionTest {
|
||||||
try {
|
try {
|
||||||
bobInboundGroupSession = new OlmInboundGroupSession(sessionKeyRef);
|
bobInboundGroupSession = new OlmInboundGroupSession(sessionKeyRef);
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception in test19TestErrorMessageReturnedInDecrypt, Exception code=" + e.getExceptionCode(), false);
|
fail("Exception in test19TestErrorMessageReturnedInDecrypt, Exception code=" + e.getExceptionCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
String exceptionMessage = null;
|
String exceptionMessage = null;
|
||||||
|
@ -518,8 +522,7 @@ public class OlmGroupSessionTest {
|
||||||
exceptionMessage = e.getMessage();
|
exceptionMessage = e.getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(0!=EXPECTED_ERROR_MESSAGE.length());
|
assertEquals(EXPECTED_ERROR_MESSAGE, exceptionMessage);
|
||||||
assertTrue(EXPECTED_ERROR_MESSAGE.equals(exceptionMessage));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -544,7 +547,7 @@ public class OlmGroupSessionTest {
|
||||||
try {
|
try {
|
||||||
inboundGroupSession = new OlmInboundGroupSession(sessionKey);
|
inboundGroupSession = new OlmInboundGroupSession(sessionKey);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("OlmInboundGroupSession failed " + e.getMessage(), false);
|
fail("OlmInboundGroupSession failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isVerified = false;
|
boolean isVerified = false;
|
||||||
|
@ -552,7 +555,7 @@ public class OlmGroupSessionTest {
|
||||||
try {
|
try {
|
||||||
isVerified = inboundGroupSession.isVerified();
|
isVerified = inboundGroupSession.isVerified();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("isVerified failed " + e.getMessage(), false);
|
fail("isVerified failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(isVerified);
|
assertTrue(isVerified);
|
||||||
|
@ -562,26 +565,26 @@ public class OlmGroupSessionTest {
|
||||||
try {
|
try {
|
||||||
result = inboundGroupSession.decryptMessage(message);
|
result = inboundGroupSession.decryptMessage(message);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("decryptMessage failed " + e.getMessage(), false);
|
fail("decryptMessage failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(TextUtils.equals(result.mDecryptedMessage, "Message"));
|
assertTrue(TextUtils.equals(result.mDecryptedMessage, "Message"));
|
||||||
assertTrue(0 == result.mIndex);
|
assertEquals(0, result.mIndex);
|
||||||
|
|
||||||
String export = null;
|
String export = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
export = inboundGroupSession.export(0);
|
export = inboundGroupSession.export(0);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("export failed " + e.getMessage(), false);
|
fail("export failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
assertTrue(!TextUtils.isEmpty(export));
|
assertFalse(TextUtils.isEmpty(export));
|
||||||
|
|
||||||
long index = -1;
|
long index = -1;
|
||||||
try {
|
try {
|
||||||
index = inboundGroupSession.getFirstKnownIndex();
|
index = inboundGroupSession.getFirstKnownIndex();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("getFirstKnownIndex failed " + e.getMessage(), false);
|
fail("getFirstKnownIndex failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
assertTrue(index >=0);
|
assertTrue(index >=0);
|
||||||
|
|
||||||
|
@ -593,13 +596,13 @@ public class OlmGroupSessionTest {
|
||||||
try {
|
try {
|
||||||
inboundGroupSession2 = inboundGroupSession.importSession(export);
|
inboundGroupSession2 = inboundGroupSession.importSession(export);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("OlmInboundGroupSession failed " + e.getMessage(), false);
|
fail("OlmInboundGroupSession failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
isVerified = inboundGroupSession2.isVerified();
|
isVerified = inboundGroupSession2.isVerified();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("isVerified failed " + e.getMessage(), false);
|
fail("isVerified failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertFalse(isVerified);
|
assertFalse(isVerified);
|
||||||
|
@ -608,16 +611,16 @@ public class OlmGroupSessionTest {
|
||||||
try {
|
try {
|
||||||
result = inboundGroupSession2.decryptMessage(message);
|
result = inboundGroupSession2.decryptMessage(message);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("decryptMessage failed " + e.getMessage(), false);
|
fail("decryptMessage failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(TextUtils.equals(result.mDecryptedMessage, "Message"));
|
assertTrue(TextUtils.equals(result.mDecryptedMessage, "Message"));
|
||||||
assertTrue(0 == result.mIndex);
|
assertEquals(0, result.mIndex);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
isVerified = inboundGroupSession2.isVerified();
|
isVerified = inboundGroupSession2.isVerified();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("isVerified failed " + e.getMessage(), false);
|
fail("isVerified failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(isVerified);
|
assertTrue(isVerified);
|
||||||
|
|
|
@ -16,21 +16,22 @@
|
||||||
|
|
||||||
package org.matrix.olm;
|
package org.matrix.olm;
|
||||||
|
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
import org.junit.FixMethodOrder;
|
import org.junit.FixMethodOrder;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.MethodSorters;
|
import org.junit.runners.MethodSorters;
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
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.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
|
@ -47,13 +48,13 @@ public class OlmPkTest {
|
||||||
mOlmPkEncryption = new OlmPkEncryption();
|
mOlmPkEncryption = new OlmPkEncryption();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
assertTrue("OlmPkEncryption failed " + e.getMessage(), false);
|
fail("OlmPkEncryption failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
mOlmPkDecryption = new OlmPkDecryption();
|
mOlmPkDecryption = new OlmPkDecryption();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
assertTrue("OlmPkEncryption failed " + e.getMessage(), false);
|
fail("OlmPkEncryption failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertNotNull(mOlmPkEncryption);
|
assertNotNull(mOlmPkEncryption);
|
||||||
|
@ -63,13 +64,13 @@ public class OlmPkTest {
|
||||||
try {
|
try {
|
||||||
key = mOlmPkDecryption.generateKey();
|
key = mOlmPkDecryption.generateKey();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception in generateKey, Exception code=" + e.getExceptionCode(), false);
|
fail("Exception in generateKey, Exception code=" + e.getExceptionCode());
|
||||||
}
|
}
|
||||||
Log.d(LOG_TAG, "Ephemeral Key: " + key);
|
Log.d(LOG_TAG, "Ephemeral Key: " + key);
|
||||||
try {
|
try {
|
||||||
mOlmPkEncryption.setRecipientKey(key);
|
mOlmPkEncryption.setRecipientKey(key);
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception in setRecipientKey, Exception code=" + e.getExceptionCode(), false);
|
fail("Exception in setRecipientKey, Exception code=" + e.getExceptionCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
String clearMessage = "Public key test";
|
String clearMessage = "Public key test";
|
||||||
|
@ -77,7 +78,7 @@ public class OlmPkTest {
|
||||||
try {
|
try {
|
||||||
message = mOlmPkEncryption.encrypt(clearMessage);
|
message = mOlmPkEncryption.encrypt(clearMessage);
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception in encrypt, Exception code=" + e.getExceptionCode(), false);
|
fail("Exception in encrypt, Exception code=" + e.getExceptionCode());
|
||||||
}
|
}
|
||||||
Log.d(LOG_TAG, "message: " + message.mCipherText + " " + message.mMac + " " + message.mEphemeralKey);
|
Log.d(LOG_TAG, "message: " + message.mCipherText + " " + message.mMac + " " + message.mEphemeralKey);
|
||||||
|
|
||||||
|
@ -85,9 +86,9 @@ public class OlmPkTest {
|
||||||
try {
|
try {
|
||||||
decryptedMessage = mOlmPkDecryption.decrypt(message);
|
decryptedMessage = mOlmPkDecryption.decrypt(message);
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception in decrypt, Exception code=" + e.getExceptionCode(), false);
|
fail("Exception in decrypt, Exception code=" + e.getExceptionCode());
|
||||||
}
|
}
|
||||||
assertTrue(clearMessage.equals(decryptedMessage));
|
assertEquals(clearMessage, decryptedMessage);
|
||||||
|
|
||||||
mOlmPkEncryption.releaseEncryption();
|
mOlmPkEncryption.releaseEncryption();
|
||||||
mOlmPkDecryption.releaseDecryption();
|
mOlmPkDecryption.releaseDecryption();
|
||||||
|
@ -101,7 +102,7 @@ public class OlmPkTest {
|
||||||
mOlmPkDecryption = new OlmPkDecryption();
|
mOlmPkDecryption = new OlmPkDecryption();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
assertTrue("OlmPkEncryption failed " + e.getMessage(), false);
|
fail("OlmPkEncryption failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertNotNull(mOlmPkDecryption);
|
assertNotNull(mOlmPkDecryption);
|
||||||
|
@ -117,12 +118,12 @@ public class OlmPkTest {
|
||||||
(byte) 0x1D, (byte) 0xB9, (byte) 0x2C, (byte) 0x2A
|
(byte) 0x1D, (byte) 0xB9, (byte) 0x2C, (byte) 0x2A
|
||||||
};
|
};
|
||||||
|
|
||||||
assertTrue(privateKey.length == OlmPkDecryption.privateKeyLength());
|
assertEquals(privateKey.length, OlmPkDecryption.privateKeyLength());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mOlmPkDecryption.setPrivateKey(privateKey);
|
mOlmPkDecryption.setPrivateKey(privateKey);
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception in setPrivateKey, Exception code=" + e.getExceptionCode(), false);
|
fail("Exception in setPrivateKey, Exception code=" + e.getExceptionCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] privateKeyCopy = null;
|
byte[] privateKeyCopy = null;
|
||||||
|
@ -130,10 +131,10 @@ public class OlmPkTest {
|
||||||
try {
|
try {
|
||||||
privateKeyCopy = mOlmPkDecryption.privateKey();
|
privateKeyCopy = mOlmPkDecryption.privateKey();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception in privateKey, Exception code=" + e.getExceptionCode(), false);
|
fail("Exception in privateKey, Exception code=" + e.getExceptionCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(Arrays.equals(privateKey, privateKeyCopy));
|
assertArrayEquals(privateKey, privateKeyCopy);
|
||||||
|
|
||||||
mOlmPkDecryption.releaseDecryption();
|
mOlmPkDecryption.releaseDecryption();
|
||||||
assertTrue(mOlmPkDecryption.isReleased());
|
assertTrue(mOlmPkDecryption.isReleased());
|
||||||
|
@ -145,7 +146,7 @@ public class OlmPkTest {
|
||||||
mOlmPkSigning = new OlmPkSigning();
|
mOlmPkSigning = new OlmPkSigning();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
assertTrue("OlmPkSigning failed " + e.getMessage(), false);
|
fail("OlmPkSigning failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertNotNull(mOlmPkSigning);
|
assertNotNull(mOlmPkSigning);
|
||||||
|
@ -155,17 +156,17 @@ public class OlmPkTest {
|
||||||
seed = OlmPkSigning.generateSeed();
|
seed = OlmPkSigning.generateSeed();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
assertTrue("generateSeed failed " + e.getMessage(), false);
|
fail("generateSeed failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(seed.length == OlmPkSigning.seedLength());
|
assertEquals(seed.length, OlmPkSigning.seedLength());
|
||||||
|
|
||||||
String pubkey = null;
|
String pubkey = null;
|
||||||
try {
|
try {
|
||||||
pubkey = mOlmPkSigning.initWithSeed(seed);
|
pubkey = mOlmPkSigning.initWithSeed(seed);
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
assertTrue("initWithSeed failed " + e.getMessage(), false);
|
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 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.";
|
||||||
|
@ -175,7 +176,7 @@ public class OlmPkTest {
|
||||||
signature = mOlmPkSigning.sign(message);
|
signature = mOlmPkSigning.sign(message);
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
assertTrue("sign failed " + e.getMessage(), false);
|
fail("sign failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
OlmUtility olmUtility = null;
|
OlmUtility olmUtility = null;
|
||||||
|
@ -183,14 +184,14 @@ public class OlmPkTest {
|
||||||
olmUtility = new OlmUtility();
|
olmUtility = new OlmUtility();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
assertTrue("olmUtility failed " + e.getMessage(), false);
|
fail("olmUtility failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
olmUtility.verifyEd25519Signature(signature, pubkey, message);
|
olmUtility.verifyEd25519Signature(signature, pubkey, message);
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
assertTrue("Signature verification failed " + e.getMessage(), false);
|
fail("Signature verification failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
mOlmPkSigning.releaseSigning();
|
mOlmPkSigning.releaseSigning();
|
||||||
|
|
|
@ -16,10 +16,10 @@
|
||||||
|
|
||||||
package org.matrix.olm;
|
package org.matrix.olm;
|
||||||
|
|
||||||
|
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.FixMethodOrder;
|
import org.junit.FixMethodOrder;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -29,6 +29,7 @@ import org.junit.runners.MethodSorters;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
|
@ -91,7 +92,7 @@ public class OlmSasTest {
|
||||||
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("OlmSas init failed " + e.getMessage(), false);
|
fail("OlmSas init failed " + e.getMessage());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} finally {
|
} finally {
|
||||||
if (aliceSas != null) {
|
if (aliceSas != null) {
|
||||||
|
|
|
@ -18,11 +18,12 @@
|
||||||
package org.matrix.olm;
|
package org.matrix.olm;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.json.JSONObject;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.FixMethodOrder;
|
import org.junit.FixMethodOrder;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -37,10 +38,12 @@ import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static android.support.test.InstrumentationRegistry.getInstrumentation;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
|
@ -84,7 +87,7 @@ public class OlmSessionTest {
|
||||||
aliceAccount = new OlmAccount();
|
aliceAccount = new OlmAccount();
|
||||||
bobAccount = new OlmAccount();
|
bobAccount = new OlmAccount();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue(e.getMessage(),false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// test accounts creation
|
// test accounts creation
|
||||||
|
@ -97,17 +100,17 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
bobIdentityKeys = bobAccount.identityKeys();
|
bobIdentityKeys = bobAccount.identityKeys();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("identityKeys failed " + e.getMessage(), false);
|
fail("identityKeys failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
bobIdentityKey = TestHelper.getIdentityKey(bobIdentityKeys);
|
bobIdentityKey = TestHelper.getIdentityKey(bobIdentityKeys);
|
||||||
assertTrue(null!=bobIdentityKey);
|
assertNotNull(bobIdentityKey);
|
||||||
|
|
||||||
// get bob one time keys
|
// get bob one time keys
|
||||||
try {
|
try {
|
||||||
bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER);
|
bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Map<String, String>> bobOneTimeKeys = null;
|
Map<String, Map<String, String>> bobOneTimeKeys = null;
|
||||||
|
@ -115,7 +118,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
bobOneTimeKeys = bobAccount.oneTimeKeys();
|
bobOneTimeKeys = bobAccount.oneTimeKeys();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
bobOneTimeKey = TestHelper.getOneTimeKey(bobOneTimeKeys, 1);
|
bobOneTimeKey = TestHelper.getOneTimeKey(bobOneTimeKeys, 1);
|
||||||
|
@ -126,7 +129,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
aliceSession = new OlmSession();
|
aliceSession = new OlmSession();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception Msg="+e.getMessage(), false);
|
fail("Exception Msg=" + e.getMessage());
|
||||||
}
|
}
|
||||||
assertTrue(0 != aliceSession.getOlmSessionId());
|
assertTrue(0 != aliceSession.getOlmSessionId());
|
||||||
|
|
||||||
|
@ -134,14 +137,14 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
aliceSession.initOutboundSession(aliceAccount, bobIdentityKey, bobOneTimeKey);
|
aliceSession.initOutboundSession(aliceAccount, bobIdentityKey, bobOneTimeKey);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
String clearMsg = "Heloo bob , this is alice!";
|
String clearMsg = "Heloo bob , this is alice!";
|
||||||
OlmMessage encryptedMsgToBob = null;
|
OlmMessage encryptedMsgToBob = null;
|
||||||
try {
|
try {
|
||||||
encryptedMsgToBob = aliceSession.encryptMessage(clearMsg);
|
encryptedMsgToBob = aliceSession.encryptMessage(clearMsg);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(encryptedMsgToBob);
|
assertNotNull(encryptedMsgToBob);
|
||||||
assertNotNull(encryptedMsgToBob.mCipherText);
|
assertNotNull(encryptedMsgToBob.mCipherText);
|
||||||
|
@ -152,32 +155,32 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
bobSession = new OlmSession();
|
bobSession = new OlmSession();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception Msg="+e.getMessage(), false);
|
fail("Exception Msg=" + e.getMessage());
|
||||||
}
|
}
|
||||||
assertTrue(0 != bobSession.getOlmSessionId());
|
assertTrue(0 != bobSession.getOlmSessionId());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bobSession.initInboundSession(bobAccount, encryptedMsgToBob.mCipherText);
|
bobSession.initInboundSession(bobAccount, encryptedMsgToBob.mCipherText);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("initInboundSessionWithAccount failed " + e.getMessage(), false);
|
fail("initInboundSessionWithAccount failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
String decryptedMsg = null;
|
String decryptedMsg = null;
|
||||||
try {
|
try {
|
||||||
decryptedMsg = bobSession.decryptMessage(encryptedMsgToBob);
|
decryptedMsg = bobSession.decryptMessage(encryptedMsgToBob);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(decryptedMsg);
|
assertNotNull(decryptedMsg);
|
||||||
|
|
||||||
// MESSAGE COMPARISON: decrypted vs encrypted
|
// MESSAGE COMPARISON: decrypted vs encrypted
|
||||||
assertTrue(clearMsg.equals(decryptedMsg));
|
assertEquals(clearMsg, decryptedMsg);
|
||||||
|
|
||||||
// clean objects..
|
// clean objects..
|
||||||
try {
|
try {
|
||||||
bobAccount.removeOneTimeKeys(bobSession);
|
bobAccount.removeOneTimeKeys(bobSession);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// release accounts
|
// release accounts
|
||||||
|
@ -220,7 +223,7 @@ public class OlmSessionTest {
|
||||||
aliceAccount = new OlmAccount();
|
aliceAccount = new OlmAccount();
|
||||||
bobAccount = new OlmAccount();
|
bobAccount = new OlmAccount();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue(e.getMessage(),false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// test accounts creation
|
// test accounts creation
|
||||||
|
@ -233,17 +236,17 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
bobIdentityKeys = bobAccount.identityKeys();
|
bobIdentityKeys = bobAccount.identityKeys();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("identityKeys failed " + e.getMessage(), false);
|
fail("identityKeys failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
bobIdentityKey = TestHelper.getIdentityKey(bobIdentityKeys);
|
bobIdentityKey = TestHelper.getIdentityKey(bobIdentityKeys);
|
||||||
assertTrue(null!=bobIdentityKey);
|
assertNotNull(bobIdentityKey);
|
||||||
|
|
||||||
// get bob one time keys
|
// get bob one time keys
|
||||||
try {
|
try {
|
||||||
bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER);
|
bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Map<String, String>> bobOneTimeKeys = null;
|
Map<String, Map<String, String>> bobOneTimeKeys = null;
|
||||||
|
@ -251,7 +254,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
bobOneTimeKeys = bobAccount.oneTimeKeys();
|
bobOneTimeKeys = bobAccount.oneTimeKeys();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
bobOneTimeKey = TestHelper.getOneTimeKey(bobOneTimeKeys, 1);
|
bobOneTimeKey = TestHelper.getOneTimeKey(bobOneTimeKeys, 1);
|
||||||
|
@ -262,7 +265,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
aliceSession = new OlmSession();
|
aliceSession = new OlmSession();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception Msg="+e.getMessage(), false);
|
fail("Exception Msg=" + e.getMessage());
|
||||||
}
|
}
|
||||||
assertTrue(0 != aliceSession.getOlmSessionId());
|
assertTrue(0 != aliceSession.getOlmSessionId());
|
||||||
|
|
||||||
|
@ -270,7 +273,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
aliceSession.initOutboundSession(aliceAccount, bobIdentityKey, bobOneTimeKey);
|
aliceSession.initOutboundSession(aliceAccount, bobIdentityKey, bobOneTimeKey);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
String helloClearMsg = "Hello I'm Alice!";
|
String helloClearMsg = "Hello I'm Alice!";
|
||||||
|
@ -280,7 +283,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
encryptedAliceToBobMsg1 = aliceSession.encryptMessage(helloClearMsg);
|
encryptedAliceToBobMsg1 = aliceSession.encryptMessage(helloClearMsg);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertNotNull(encryptedAliceToBobMsg1);
|
assertNotNull(encryptedAliceToBobMsg1);
|
||||||
|
@ -291,7 +294,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
bobSession = new OlmSession();
|
bobSession = new OlmSession();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception Msg="+e.getMessage(), false);
|
fail("Exception Msg=" + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(0 != bobSession.getOlmSessionId());
|
assertTrue(0 != bobSession.getOlmSessionId());
|
||||||
|
@ -299,7 +302,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
bobSession.initInboundSession(bobAccount, encryptedAliceToBobMsg1.mCipherText);
|
bobSession.initInboundSession(bobAccount, encryptedAliceToBobMsg1.mCipherText);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("initInboundSessionWithAccount failed " + e.getMessage(), false);
|
fail("initInboundSessionWithAccount failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// DECRYPT MESSAGE FROM ALICE
|
// DECRYPT MESSAGE FROM ALICE
|
||||||
|
@ -307,12 +310,12 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
decryptedMsg01 = bobSession.decryptMessage(encryptedAliceToBobMsg1);
|
decryptedMsg01 = bobSession.decryptMessage(encryptedAliceToBobMsg1);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(decryptedMsg01);
|
assertNotNull(decryptedMsg01);
|
||||||
|
|
||||||
// MESSAGE COMPARISON: decrypted vs encrypted
|
// MESSAGE COMPARISON: decrypted vs encrypted
|
||||||
assertTrue(helloClearMsg.equals(decryptedMsg01));
|
assertEquals(helloClearMsg, decryptedMsg01);
|
||||||
|
|
||||||
// BACK/FORTH MESSAGE COMPARISON
|
// BACK/FORTH MESSAGE COMPARISON
|
||||||
String clearMsg1 = "Hello I'm Bob!";
|
String clearMsg1 = "Hello I'm Bob!";
|
||||||
|
@ -324,7 +327,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
encryptedMsg1 = bobSession.encryptMessage(clearMsg1);
|
encryptedMsg1 = bobSession.encryptMessage(clearMsg1);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(encryptedMsg1);
|
assertNotNull(encryptedMsg1);
|
||||||
|
|
||||||
|
@ -332,7 +335,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
encryptedMsg2 = bobSession.encryptMessage(clearMsg2);
|
encryptedMsg2 = bobSession.encryptMessage(clearMsg2);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(encryptedMsg2);
|
assertNotNull(encryptedMsg2);
|
||||||
|
|
||||||
|
@ -341,7 +344,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
encryptedMsg3 = bobSession.encryptMessage(clearMsg3);
|
encryptedMsg3 = bobSession.encryptMessage(clearMsg3);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(encryptedMsg3);
|
assertNotNull(encryptedMsg3);
|
||||||
|
|
||||||
|
@ -350,7 +353,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
decryptedMsg1 = aliceSession.decryptMessage(encryptedMsg1);
|
decryptedMsg1 = aliceSession.decryptMessage(encryptedMsg1);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertNotNull(decryptedMsg1);
|
assertNotNull(decryptedMsg1);
|
||||||
|
@ -358,7 +361,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
decryptedMsg2 = aliceSession.decryptMessage(encryptedMsg2);
|
decryptedMsg2 = aliceSession.decryptMessage(encryptedMsg2);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertNotNull(decryptedMsg2);
|
assertNotNull(decryptedMsg2);
|
||||||
|
@ -366,14 +369,14 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
decryptedMsg3 = aliceSession.decryptMessage(encryptedMsg3);
|
decryptedMsg3 = aliceSession.decryptMessage(encryptedMsg3);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(decryptedMsg3);
|
assertNotNull(decryptedMsg3);
|
||||||
|
|
||||||
// comparison tests
|
// comparison tests
|
||||||
assertTrue(clearMsg1.equals(decryptedMsg1));
|
assertEquals(clearMsg1, decryptedMsg1);
|
||||||
assertTrue(clearMsg2.equals(decryptedMsg2));
|
assertEquals(clearMsg2, decryptedMsg2);
|
||||||
assertTrue(clearMsg3.equals(decryptedMsg3));
|
assertEquals(clearMsg3, decryptedMsg3);
|
||||||
|
|
||||||
// and one more from alice to bob
|
// and one more from alice to bob
|
||||||
clearMsg1 = "another message from Alice to Bob!!";
|
clearMsg1 = "another message from Alice to Bob!!";
|
||||||
|
@ -382,7 +385,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
encryptedMsg1 = aliceSession.encryptMessage(clearMsg1);
|
encryptedMsg1 = aliceSession.encryptMessage(clearMsg1);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(encryptedMsg1);
|
assertNotNull(encryptedMsg1);
|
||||||
|
|
||||||
|
@ -390,20 +393,20 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
decryptedMsg1 = bobSession.decryptMessage(encryptedMsg1);
|
decryptedMsg1 = bobSession.decryptMessage(encryptedMsg1);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertNotNull(decryptedMsg1);
|
assertNotNull(decryptedMsg1);
|
||||||
assertTrue(clearMsg1.equals(decryptedMsg1));
|
assertEquals(clearMsg1, decryptedMsg1);
|
||||||
|
|
||||||
// comparison test
|
// comparison test
|
||||||
assertTrue(clearMsg1.equals(decryptedMsg1));
|
assertEquals(clearMsg1, decryptedMsg1);
|
||||||
|
|
||||||
// clean objects..
|
// clean objects..
|
||||||
try {
|
try {
|
||||||
bobAccount.removeOneTimeKeys(bobSession);
|
bobAccount.removeOneTimeKeys(bobSession);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
bobAccount.releaseAccount();
|
bobAccount.releaseAccount();
|
||||||
|
@ -427,7 +430,7 @@ public class OlmSessionTest {
|
||||||
aliceAccount = new OlmAccount();
|
aliceAccount = new OlmAccount();
|
||||||
bobAccount = new OlmAccount();
|
bobAccount = new OlmAccount();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue(e.getMessage(),false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// test accounts creation
|
// test accounts creation
|
||||||
|
@ -440,7 +443,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
aliceSession = new OlmSession();
|
aliceSession = new OlmSession();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception Msg="+e.getMessage(), false);
|
fail("Exception Msg=" + e.getMessage());
|
||||||
}
|
}
|
||||||
assertTrue(0 != aliceSession.getOlmSessionId());
|
assertTrue(0 != aliceSession.getOlmSessionId());
|
||||||
|
|
||||||
|
@ -450,7 +453,7 @@ public class OlmSessionTest {
|
||||||
bobSession = new OlmSession();
|
bobSession = new OlmSession();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertTrue(0 != bobSession.getOlmSessionId());
|
assertTrue(0 != bobSession.getOlmSessionId());
|
||||||
|
|
||||||
|
@ -458,7 +461,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
aliceSessionId = aliceSession.sessionIdentifier();
|
aliceSessionId = aliceSession.sessionIdentifier();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertNotNull(aliceSessionId);
|
assertNotNull(aliceSessionId);
|
||||||
|
@ -467,12 +470,12 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
bobSessionId = bobSession.sessionIdentifier();
|
bobSessionId = bobSession.sessionIdentifier();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(bobSessionId);
|
assertNotNull(bobSessionId);
|
||||||
|
|
||||||
// must be the same for both ends of the conversation
|
// must be the same for both ends of the conversation
|
||||||
assertTrue(aliceSessionId.equals(bobSessionId));
|
assertEquals(aliceSessionId, bobSessionId);
|
||||||
|
|
||||||
aliceAccount.releaseAccount();
|
aliceAccount.releaseAccount();
|
||||||
bobAccount.releaseAccount();
|
bobAccount.releaseAccount();
|
||||||
|
@ -495,7 +498,7 @@ public class OlmSessionTest {
|
||||||
aliceAccount = new OlmAccount();
|
aliceAccount = new OlmAccount();
|
||||||
bobAccount = new OlmAccount();
|
bobAccount = new OlmAccount();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// CREATE ALICE SESSION
|
// CREATE ALICE SESSION
|
||||||
|
@ -503,7 +506,7 @@ public class OlmSessionTest {
|
||||||
aliceSession = new OlmSession();
|
aliceSession = new OlmSession();
|
||||||
bobSession = new OlmSession();
|
bobSession = new OlmSession();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception Msg=" + e.getMessage(), false);
|
fail("Exception Msg=" + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// get bob/luke identity key
|
// get bob/luke identity key
|
||||||
|
@ -512,7 +515,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
bobIdentityKeys = bobAccount.identityKeys();
|
bobIdentityKeys = bobAccount.identityKeys();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("identityKeys failed " + e.getMessage(), false);
|
fail("identityKeys failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, String> aliceIdentityKeys = null;
|
Map<String, String> aliceIdentityKeys = null;
|
||||||
|
@ -520,7 +523,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
aliceIdentityKeys = aliceAccount.identityKeys();
|
aliceIdentityKeys = aliceAccount.identityKeys();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("identityKeys failed " + e.getMessage(), false);
|
fail("identityKeys failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
String bobIdentityKey = TestHelper.getIdentityKey(bobIdentityKeys);
|
String bobIdentityKey = TestHelper.getIdentityKey(bobIdentityKeys);
|
||||||
|
@ -530,13 +533,13 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER);
|
bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
aliceAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER);
|
aliceAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Map<String, String>> bobOneTimeKeys = null;
|
Map<String, Map<String, String>> bobOneTimeKeys = null;
|
||||||
|
@ -544,7 +547,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
bobOneTimeKeys = bobAccount.oneTimeKeys();
|
bobOneTimeKeys = bobAccount.oneTimeKeys();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
String bobOneTimeKey1 = TestHelper.getOneTimeKey(bobOneTimeKeys, 1);
|
String bobOneTimeKey1 = TestHelper.getOneTimeKey(bobOneTimeKeys, 1);
|
||||||
|
@ -553,7 +556,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
aliceSession.initOutboundSession(aliceAccount, bobIdentityKey, bobOneTimeKey1);
|
aliceSession.initOutboundSession(aliceAccount, bobIdentityKey, bobOneTimeKey1);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
String aliceClearMsg = "hello helooo to bob!";
|
String aliceClearMsg = "hello helooo to bob!";
|
||||||
|
@ -562,7 +565,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
encryptedAliceToBobMsg1 = aliceSession.encryptMessage(aliceClearMsg);
|
encryptedAliceToBobMsg1 = aliceSession.encryptMessage(aliceClearMsg);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertFalse(bobSession.matchesInboundSession(encryptedAliceToBobMsg1.mCipherText));
|
assertFalse(bobSession.matchesInboundSession(encryptedAliceToBobMsg1.mCipherText));
|
||||||
|
@ -571,7 +574,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
bobSession.initInboundSession(bobAccount, encryptedAliceToBobMsg1.mCipherText);
|
bobSession.initInboundSession(bobAccount, encryptedAliceToBobMsg1.mCipherText);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("initInboundSessionWithAccount failed " + e.getMessage(), false);
|
fail("initInboundSessionWithAccount failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// test matchesInboundSession() and matchesInboundSessionFrom()
|
// test matchesInboundSession() and matchesInboundSessionFrom()
|
||||||
|
@ -584,7 +587,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
bobAccount.removeOneTimeKeys(bobSession);
|
bobAccount.removeOneTimeKeys(bobSession);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
aliceAccount.releaseAccount();
|
aliceAccount.releaseAccount();
|
||||||
|
@ -601,6 +604,7 @@ public class OlmSessionTest {
|
||||||
// ********************************************************
|
// ********************************************************
|
||||||
// ************* SERIALIZATION TEST ***********************
|
// ************* SERIALIZATION TEST ***********************
|
||||||
// ********************************************************
|
// ********************************************************
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as {@link #test02AliceToBobBackAndForth()}, but alice's session
|
* Same as {@link #test02AliceToBobBackAndForth()}, but alice's session
|
||||||
* is serialized and de-serialized before performing the final
|
* is serialized and de-serialized before performing the final
|
||||||
|
@ -620,7 +624,7 @@ public class OlmSessionTest {
|
||||||
aliceAccount = new OlmAccount();
|
aliceAccount = new OlmAccount();
|
||||||
bobAccount = new OlmAccount();
|
bobAccount = new OlmAccount();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue(e.getMessage(),false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// test accounts creation
|
// test accounts creation
|
||||||
|
@ -633,17 +637,17 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
bobIdentityKeys = bobAccount.identityKeys();
|
bobIdentityKeys = bobAccount.identityKeys();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("identityKeys failed " + e.getMessage(), false);
|
fail("identityKeys failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
bobIdentityKey = TestHelper.getIdentityKey(bobIdentityKeys);
|
bobIdentityKey = TestHelper.getIdentityKey(bobIdentityKeys);
|
||||||
assertTrue(null!=bobIdentityKey);
|
assertNotNull(bobIdentityKey);
|
||||||
|
|
||||||
// get bob one time keys
|
// get bob one time keys
|
||||||
try {
|
try {
|
||||||
bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER);
|
bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Map<String, String>> bobOneTimeKeys = null;
|
Map<String, Map<String, String>> bobOneTimeKeys = null;
|
||||||
|
@ -651,7 +655,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
bobOneTimeKeys = bobAccount.oneTimeKeys();
|
bobOneTimeKeys = bobAccount.oneTimeKeys();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
bobOneTimeKey = TestHelper.getOneTimeKey(bobOneTimeKeys, 1);
|
bobOneTimeKey = TestHelper.getOneTimeKey(bobOneTimeKeys, 1);
|
||||||
|
@ -662,7 +666,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
aliceSession = new OlmSession();
|
aliceSession = new OlmSession();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception Msg="+e.getMessage(), false);
|
fail("Exception Msg=" + e.getMessage());
|
||||||
}
|
}
|
||||||
assertTrue(0 != aliceSession.getOlmSessionId());
|
assertTrue(0 != aliceSession.getOlmSessionId());
|
||||||
|
|
||||||
|
@ -670,7 +674,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
aliceSession.initOutboundSession(aliceAccount, bobIdentityKey, bobOneTimeKey);
|
aliceSession.initOutboundSession(aliceAccount, bobIdentityKey, bobOneTimeKey);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
String helloClearMsg = "Hello I'm Alice!";
|
String helloClearMsg = "Hello I'm Alice!";
|
||||||
|
@ -679,7 +683,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
encryptedAliceToBobMsg1 = aliceSession.encryptMessage(helloClearMsg);
|
encryptedAliceToBobMsg1 = aliceSession.encryptMessage(helloClearMsg);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(encryptedAliceToBobMsg1);
|
assertNotNull(encryptedAliceToBobMsg1);
|
||||||
assertNotNull(encryptedAliceToBobMsg1.mCipherText);
|
assertNotNull(encryptedAliceToBobMsg1.mCipherText);
|
||||||
|
@ -689,7 +693,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
bobSession = new OlmSession();
|
bobSession = new OlmSession();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception Msg="+e.getMessage(), false);
|
fail("Exception Msg=" + e.getMessage());
|
||||||
}
|
}
|
||||||
assertTrue(0 != bobSession.getOlmSessionId());
|
assertTrue(0 != bobSession.getOlmSessionId());
|
||||||
|
|
||||||
|
@ -697,7 +701,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
bobSession.initInboundSession(bobAccount, encryptedAliceToBobMsg1.mCipherText);
|
bobSession.initInboundSession(bobAccount, encryptedAliceToBobMsg1.mCipherText);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("initInboundSessionWithAccount failed " + e.getMessage(), false);
|
fail("initInboundSessionWithAccount failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// DECRYPT MESSAGE FROM ALICE
|
// DECRYPT MESSAGE FROM ALICE
|
||||||
|
@ -706,13 +710,13 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
decryptedMsg01 = bobSession.decryptMessage(encryptedAliceToBobMsg1);
|
decryptedMsg01 = bobSession.decryptMessage(encryptedAliceToBobMsg1);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertNotNull(decryptedMsg01);
|
assertNotNull(decryptedMsg01);
|
||||||
|
|
||||||
// MESSAGE COMPARISON: decrypted vs encrypted
|
// MESSAGE COMPARISON: decrypted vs encrypted
|
||||||
assertTrue(helloClearMsg.equals(decryptedMsg01));
|
assertEquals(helloClearMsg, decryptedMsg01);
|
||||||
|
|
||||||
// BACK/FORTH MESSAGE COMPARISON
|
// BACK/FORTH MESSAGE COMPARISON
|
||||||
String clearMsg1 = "Hello I'm Bob!";
|
String clearMsg1 = "Hello I'm Bob!";
|
||||||
|
@ -724,7 +728,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
encryptedMsg1 = bobSession.encryptMessage(clearMsg1);
|
encryptedMsg1 = bobSession.encryptMessage(clearMsg1);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(encryptedMsg1);
|
assertNotNull(encryptedMsg1);
|
||||||
|
|
||||||
|
@ -732,7 +736,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
encryptedMsg2 = bobSession.encryptMessage(clearMsg2);
|
encryptedMsg2 = bobSession.encryptMessage(clearMsg2);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(encryptedMsg2);
|
assertNotNull(encryptedMsg2);
|
||||||
|
|
||||||
|
@ -740,12 +744,12 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
encryptedMsg3 = bobSession.encryptMessage(clearMsg3);
|
encryptedMsg3 = bobSession.encryptMessage(clearMsg3);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(encryptedMsg3);
|
assertNotNull(encryptedMsg3);
|
||||||
|
|
||||||
// serialize alice session
|
// serialize alice session
|
||||||
Context context = getInstrumentation().getContext();
|
Context context = ApplicationProvider.getApplicationContext();
|
||||||
try {
|
try {
|
||||||
FileOutputStream fileOutput = context.openFileOutput(FILE_NAME_SERIAL_SESSION, Context.MODE_PRIVATE);
|
FileOutputStream fileOutput = context.openFileOutput(FILE_NAME_SERIAL_SESSION, Context.MODE_PRIVATE);
|
||||||
ObjectOutputStream objectOutput = new ObjectOutputStream(fileOutput);
|
ObjectOutputStream objectOutput = new ObjectOutputStream(fileOutput);
|
||||||
|
@ -771,15 +775,15 @@ public class OlmSessionTest {
|
||||||
assertNotNull(decryptedMsg3);
|
assertNotNull(decryptedMsg3);
|
||||||
|
|
||||||
// comparison tests
|
// comparison tests
|
||||||
assertTrue(clearMsg1.equals(decryptedMsg1));
|
assertEquals(clearMsg1, decryptedMsg1);
|
||||||
assertTrue(clearMsg2.equals(decryptedMsg2));
|
assertEquals(clearMsg2, decryptedMsg2);
|
||||||
assertTrue(clearMsg3.equals(decryptedMsg3));
|
assertEquals(clearMsg3, decryptedMsg3);
|
||||||
|
|
||||||
// clean objects..
|
// clean objects..
|
||||||
try {
|
try {
|
||||||
bobAccount.removeOneTimeKeys(bobSession);
|
bobAccount.removeOneTimeKeys(bobSession);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
bobAccount.releaseAccount();
|
bobAccount.releaseAccount();
|
||||||
|
@ -793,25 +797,21 @@ public class OlmSessionTest {
|
||||||
assertTrue(bobSession.isReleased());
|
assertTrue(bobSession.isReleased());
|
||||||
assertTrue(aliceSession.isReleased());
|
assertTrue(aliceSession.isReleased());
|
||||||
assertTrue(aliceSessionDeserial.isReleased());
|
assertTrue(aliceSessionDeserial.isReleased());
|
||||||
}
|
} catch (FileNotFoundException e) {
|
||||||
catch (FileNotFoundException e) {
|
|
||||||
Log.e(LOG_TAG, "## test03SessionSerialization(): Exception FileNotFoundException Msg==" + e.getMessage());
|
Log.e(LOG_TAG, "## test03SessionSerialization(): Exception FileNotFoundException Msg==" + e.getMessage());
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
} catch (ClassNotFoundException e) {
|
||||||
catch (ClassNotFoundException e) {
|
|
||||||
Log.e(LOG_TAG, "## test03SessionSerialization(): Exception ClassNotFoundException Msg==" + e.getMessage());
|
Log.e(LOG_TAG, "## test03SessionSerialization(): Exception ClassNotFoundException Msg==" + e.getMessage());
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
} catch (IOException e) {
|
||||||
catch (IOException e) {
|
|
||||||
Log.e(LOG_TAG, "## test03SessionSerialization(): Exception IOException Msg==" + e.getMessage());
|
Log.e(LOG_TAG, "## test03SessionSerialization(): Exception IOException Msg==" + e.getMessage());
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
/*catch (OlmException e) {
|
/*catch (OlmException e) {
|
||||||
Log.e(LOG_TAG, "## test03SessionSerialization(): Exception OlmException Msg==" + e.getMessage());
|
Log.e(LOG_TAG, "## test03SessionSerialization(): Exception OlmException Msg==" + e.getMessage());
|
||||||
}*/
|
}*/ catch (Exception e) {
|
||||||
catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## test03SessionSerialization(): Exception Msg==" + e.getMessage());
|
Log.e(LOG_TAG, "## test03SessionSerialization(): Exception Msg==" + e.getMessage());
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -831,7 +831,7 @@ public class OlmSessionTest {
|
||||||
aliceAccount = new OlmAccount();
|
aliceAccount = new OlmAccount();
|
||||||
bobAccount = new OlmAccount();
|
bobAccount = new OlmAccount();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// get bob identity key
|
// get bob identity key
|
||||||
|
@ -840,17 +840,17 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
bobIdentityKeys = bobAccount.identityKeys();
|
bobIdentityKeys = bobAccount.identityKeys();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("identityKeys failed " + e.getMessage(), false);
|
fail("identityKeys failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
String bobIdentityKey = TestHelper.getIdentityKey(bobIdentityKeys);
|
String bobIdentityKey = TestHelper.getIdentityKey(bobIdentityKeys);
|
||||||
assertTrue(null != bobIdentityKey);
|
assertNotNull(bobIdentityKey);
|
||||||
|
|
||||||
// get bob one time keys
|
// get bob one time keys
|
||||||
try {
|
try {
|
||||||
bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER);
|
bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Map<String, String>> bobOneTimeKeys = null;
|
Map<String, Map<String, String>> bobOneTimeKeys = null;
|
||||||
|
@ -858,7 +858,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
bobOneTimeKeys = bobAccount.oneTimeKeys();
|
bobOneTimeKeys = bobAccount.oneTimeKeys();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertNotNull(bobOneTimeKeys);
|
assertNotNull(bobOneTimeKeys);
|
||||||
|
@ -870,7 +870,7 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
aliceSession = new OlmSession();
|
aliceSession = new OlmSession();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception Msg=" + e.getMessage(), false);
|
fail("Exception Msg=" + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// SANITY CHECK TESTS FOR: initOutboundSessionWithAccount()
|
// SANITY CHECK TESTS FOR: initOutboundSessionWithAccount()
|
||||||
|
@ -880,7 +880,7 @@ public class OlmSessionTest {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
errorMessage = e.getMessage();
|
errorMessage = e.getMessage();
|
||||||
}
|
}
|
||||||
assertTrue(null != errorMessage);
|
assertNotNull(errorMessage);
|
||||||
|
|
||||||
errorMessage = null;
|
errorMessage = null;
|
||||||
try {
|
try {
|
||||||
|
@ -888,7 +888,7 @@ public class OlmSessionTest {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
errorMessage = e.getMessage();
|
errorMessage = e.getMessage();
|
||||||
}
|
}
|
||||||
assertTrue(null != errorMessage);
|
assertNotNull(errorMessage);
|
||||||
|
|
||||||
errorMessage = null;
|
errorMessage = null;
|
||||||
try {
|
try {
|
||||||
|
@ -896,7 +896,7 @@ public class OlmSessionTest {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
errorMessage = e.getMessage();
|
errorMessage = e.getMessage();
|
||||||
}
|
}
|
||||||
assertTrue(null != errorMessage);
|
assertNotNull(errorMessage);
|
||||||
|
|
||||||
errorMessage = null;
|
errorMessage = null;
|
||||||
try {
|
try {
|
||||||
|
@ -904,7 +904,7 @@ public class OlmSessionTest {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
errorMessage = e.getMessage();
|
errorMessage = e.getMessage();
|
||||||
}
|
}
|
||||||
assertTrue(null != errorMessage);
|
assertNotNull(errorMessage);
|
||||||
|
|
||||||
// init properly
|
// init properly
|
||||||
errorMessage = null;
|
errorMessage = null;
|
||||||
|
@ -913,23 +913,23 @@ public class OlmSessionTest {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
errorMessage = e.getMessage();
|
errorMessage = e.getMessage();
|
||||||
}
|
}
|
||||||
assertTrue(null == errorMessage);
|
assertNull(errorMessage);
|
||||||
|
|
||||||
// SANITY CHECK TESTS FOR: encryptMessage()
|
// SANITY CHECK TESTS FOR: encryptMessage()
|
||||||
OlmMessage message = null;
|
OlmMessage message = null;
|
||||||
try {
|
try {
|
||||||
message = aliceSession.encryptMessage(null);
|
message = aliceSession.encryptMessage(null);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertTrue(null==message);
|
assertNull(message);
|
||||||
|
|
||||||
// encrypt properly
|
// encrypt properly
|
||||||
OlmMessage encryptedMsgToBob = null;
|
OlmMessage encryptedMsgToBob = null;
|
||||||
try {
|
try {
|
||||||
encryptedMsgToBob = aliceSession.encryptMessage("A message for bob");
|
encryptedMsgToBob = aliceSession.encryptMessage("A message for bob");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(encryptedMsgToBob);
|
assertNotNull(encryptedMsgToBob);
|
||||||
|
|
||||||
|
@ -944,7 +944,7 @@ public class OlmSessionTest {
|
||||||
errorMessage = e.getMessage();
|
errorMessage = e.getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(!TextUtils.isEmpty(errorMessage));
|
assertFalse(TextUtils.isEmpty(errorMessage));
|
||||||
|
|
||||||
errorMessage = null;
|
errorMessage = null;
|
||||||
try {
|
try {
|
||||||
|
@ -953,7 +953,7 @@ public class OlmSessionTest {
|
||||||
errorMessage = e.getMessage();
|
errorMessage = e.getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(!TextUtils.isEmpty(errorMessage));
|
assertFalse(TextUtils.isEmpty(errorMessage));
|
||||||
|
|
||||||
errorMessage = null;
|
errorMessage = null;
|
||||||
try {
|
try {
|
||||||
|
@ -962,7 +962,7 @@ public class OlmSessionTest {
|
||||||
errorMessage = e.getMessage();
|
errorMessage = e.getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(!TextUtils.isEmpty(errorMessage));
|
assertFalse(TextUtils.isEmpty(errorMessage));
|
||||||
|
|
||||||
// init properly
|
// init properly
|
||||||
errorMessage = null;
|
errorMessage = null;
|
||||||
|
@ -974,7 +974,7 @@ public class OlmSessionTest {
|
||||||
|
|
||||||
assertTrue(TextUtils.isEmpty(errorMessage));
|
assertTrue(TextUtils.isEmpty(errorMessage));
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue("Exception Msg="+e.getMessage(), false);
|
fail("Exception Msg=" + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// SANITY CHECK TESTS FOR: decryptMessage()
|
// SANITY CHECK TESTS FOR: decryptMessage()
|
||||||
|
@ -982,22 +982,22 @@ public class OlmSessionTest {
|
||||||
try {
|
try {
|
||||||
decryptedMsg = aliceSession.decryptMessage(null);
|
decryptedMsg = aliceSession.decryptMessage(null);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(null==decryptedMsg);
|
assertNull(decryptedMsg);
|
||||||
|
|
||||||
// SANITY CHECK TESTS FOR: matchesInboundSession()
|
// SANITY CHECK TESTS FOR: matchesInboundSession()
|
||||||
assertTrue(!aliceSession.matchesInboundSession(null));
|
assertFalse(aliceSession.matchesInboundSession(null));
|
||||||
|
|
||||||
// SANITY CHECK TESTS FOR: matchesInboundSessionFrom()
|
// SANITY CHECK TESTS FOR: matchesInboundSessionFrom()
|
||||||
assertTrue(!aliceSession.matchesInboundSessionFrom(null,null));
|
assertFalse(aliceSession.matchesInboundSessionFrom(null, null));
|
||||||
|
|
||||||
// release objects
|
// release objects
|
||||||
try {
|
try {
|
||||||
bobAccount.removeOneTimeKeys(bobSession);
|
bobAccount.removeOneTimeKeys(bobSession);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
aliceAccount.releaseAccount();
|
aliceAccount.releaseAccount();
|
||||||
|
@ -1011,4 +1011,75 @@ public class OlmSessionTest {
|
||||||
assertTrue(bobSession.isReleased());
|
assertTrue(bobSession.isReleased());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test07AliceBobSessionDescribe() {
|
||||||
|
// creates alice & bob accounts
|
||||||
|
OlmAccount aliceAccount = null;
|
||||||
|
OlmAccount bobAccount = null;
|
||||||
|
try {
|
||||||
|
aliceAccount = new OlmAccount();
|
||||||
|
bobAccount = new OlmAccount();
|
||||||
|
} catch (OlmException e) {
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// test accounts creation
|
||||||
|
assertTrue(0 != bobAccount.getOlmAccountId());
|
||||||
|
assertTrue(0 != aliceAccount.getOlmAccountId());
|
||||||
|
|
||||||
|
// CREATE ALICE SESSION
|
||||||
|
|
||||||
|
OlmSession aliceSession = null;
|
||||||
|
try {
|
||||||
|
aliceSession = new OlmSession();
|
||||||
|
} catch (OlmException e) {
|
||||||
|
fail("Exception Msg=" + e.getMessage());
|
||||||
|
}
|
||||||
|
assertTrue(0 != aliceSession.getOlmSessionId());
|
||||||
|
|
||||||
|
// CREATE ALICE SESSION
|
||||||
|
OlmSession bobSession = null;
|
||||||
|
try {
|
||||||
|
bobSession = new OlmSession();
|
||||||
|
} catch (OlmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
assertTrue(0 != bobSession.getOlmSessionId());
|
||||||
|
|
||||||
|
String aliceSessionDescribe = null;
|
||||||
|
try {
|
||||||
|
aliceSessionDescribe = aliceSession.sessionDescribe();
|
||||||
|
} catch (Exception e) {
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
assertNotNull(aliceSessionDescribe);
|
||||||
|
|
||||||
|
String bobSessionDescribe = null;
|
||||||
|
try {
|
||||||
|
bobSessionDescribe = bobSession.sessionDescribe();
|
||||||
|
} catch (Exception e) {
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
assertNotNull(bobSessionDescribe);
|
||||||
|
|
||||||
|
// must be the same for both ends of the conversation
|
||||||
|
assertEquals(aliceSessionDescribe, bobSessionDescribe);
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
"sender chain index: 0 receiver chain indices: skipped message keys:",
|
||||||
|
aliceSessionDescribe
|
||||||
|
);
|
||||||
|
|
||||||
|
aliceAccount.releaseAccount();
|
||||||
|
bobAccount.releaseAccount();
|
||||||
|
assertTrue(aliceAccount.isReleased());
|
||||||
|
assertTrue(bobAccount.isReleased());
|
||||||
|
|
||||||
|
bobSession.releaseSession();
|
||||||
|
aliceSession.releaseSession();
|
||||||
|
assertTrue(bobSession.isReleased());
|
||||||
|
assertTrue(aliceSession.isReleased());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,11 @@
|
||||||
|
|
||||||
package org.matrix.olm;
|
package org.matrix.olm;
|
||||||
|
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.json.JSONObject;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.FixMethodOrder;
|
import org.junit.FixMethodOrder;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -33,6 +33,7 @@ import java.util.Map;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
|
@ -66,7 +67,7 @@ public class OlmUtilityTest {
|
||||||
try {
|
try {
|
||||||
account = new OlmAccount();
|
account = new OlmAccount();
|
||||||
} catch (OlmException e) {
|
} catch (OlmException e) {
|
||||||
assertTrue(e.getMessage(),false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
assertNotNull(account);
|
assertNotNull(account);
|
||||||
|
|
||||||
|
@ -76,7 +77,7 @@ public class OlmUtilityTest {
|
||||||
try {
|
try {
|
||||||
messageSignature = account.signMessage(message);
|
messageSignature = account.signMessage(message);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue(e.getMessage(), false);
|
fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertNotNull(messageSignature);
|
assertNotNull(messageSignature);
|
||||||
|
@ -87,12 +88,12 @@ public class OlmUtilityTest {
|
||||||
try {
|
try {
|
||||||
identityKeys = account.identityKeys();
|
identityKeys = account.identityKeys();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("identityKeys failed " + e.getMessage(), false);
|
fail("identityKeys failed " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
assertNotNull(identityKeys);
|
assertNotNull(identityKeys);
|
||||||
fingerPrintKey = TestHelper.getFingerprintKey(identityKeys);
|
fingerPrintKey = TestHelper.getFingerprintKey(identityKeys);
|
||||||
assertTrue("fingerprint key missing",!TextUtils.isEmpty(fingerPrintKey));
|
assertFalse("fingerprint key missing", TextUtils.isEmpty(fingerPrintKey));
|
||||||
|
|
||||||
// instantiate utility object
|
// instantiate utility object
|
||||||
OlmUtility utility = null;
|
OlmUtility utility = null;
|
||||||
|
@ -100,7 +101,7 @@ public class OlmUtilityTest {
|
||||||
try {
|
try {
|
||||||
utility = new OlmUtility();
|
utility = new OlmUtility();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("failed to create OlmUtility", false);
|
fail("failed to create OlmUtility");
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify signature
|
// verify signature
|
||||||
|
@ -121,7 +122,7 @@ public class OlmUtilityTest {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
errorMsg = e.getMessage();
|
errorMsg = e.getMessage();
|
||||||
}
|
}
|
||||||
assertTrue(!TextUtils.isEmpty(errorMsg));
|
assertFalse(TextUtils.isEmpty(errorMsg));
|
||||||
|
|
||||||
// check bad fingerprint size => errorMsg = INVALID_BASE64
|
// check bad fingerprint size => errorMsg = INVALID_BASE64
|
||||||
String badSizeFingerPrintKey = fingerPrintKey.substring(fingerPrintKey.length() / 2);
|
String badSizeFingerPrintKey = fingerPrintKey.substring(fingerPrintKey.length() / 2);
|
||||||
|
@ -132,7 +133,7 @@ public class OlmUtilityTest {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
errorMsg = e.getMessage();
|
errorMsg = e.getMessage();
|
||||||
}
|
}
|
||||||
assertTrue(!TextUtils.isEmpty(errorMsg));
|
assertFalse(TextUtils.isEmpty(errorMsg));
|
||||||
|
|
||||||
utility.releaseUtility();
|
utility.releaseUtility();
|
||||||
assertTrue(utility.isReleased());
|
assertTrue(utility.isReleased());
|
||||||
|
@ -148,7 +149,7 @@ public class OlmUtilityTest {
|
||||||
try {
|
try {
|
||||||
utility = new OlmUtility();
|
utility = new OlmUtility();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("OlmUtility creation failed", false);
|
fail("OlmUtility creation failed");
|
||||||
}
|
}
|
||||||
String msgToHash = "The quick brown fox jumps over the lazy dog";
|
String msgToHash = "The quick brown fox jumps over the lazy dog";
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.util.Map;
|
||||||
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
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.
|
* Helper class providing helper methods used in the Olm Android SDK unit tests.
|
||||||
|
@ -39,7 +40,7 @@ public class TestHelper {
|
||||||
try {
|
try {
|
||||||
idKey = aIdentityKeysMap.get(OlmAccount.JSON_KEY_IDENTITY_KEY);
|
idKey = aIdentityKeysMap.get(OlmAccount.JSON_KEY_IDENTITY_KEY);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("Exception MSg=" + e.getMessage(), false);
|
fail("Exception MSg=" + e.getMessage());
|
||||||
}
|
}
|
||||||
return idKey;
|
return idKey;
|
||||||
}
|
}
|
||||||
|
@ -55,7 +56,7 @@ public class TestHelper {
|
||||||
try {
|
try {
|
||||||
fingerprintKey = aIdentityKeysMap.get(OlmAccount.JSON_KEY_FINGER_PRINT_KEY);
|
fingerprintKey = aIdentityKeysMap.get(OlmAccount.JSON_KEY_FINGER_PRINT_KEY);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("Exception MSg=" + e.getMessage(), false);
|
fail("Exception MSg=" + e.getMessage());
|
||||||
}
|
}
|
||||||
return fingerprintKey;
|
return fingerprintKey;
|
||||||
}
|
}
|
||||||
|
@ -75,7 +76,7 @@ public class TestHelper {
|
||||||
|
|
||||||
firstOneTimeKey = (new ArrayList<>(generatedKeys.values())).get(aKeyPosition - 1);
|
firstOneTimeKey = (new ArrayList<>(generatedKeys.values())).get(aKeyPosition - 1);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assertTrue("Exception Msg=" + e.getMessage(), false);
|
fail("Exception Msg=" + e.getMessage());
|
||||||
}
|
}
|
||||||
return firstOneTimeKey;
|
return firstOneTimeKey;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest package="org.matrix.olm" />
|
||||||
package="org.matrix.olm">
|
|
||||||
|
|
||||||
<application
|
|
||||||
android:allowBackup="true"
|
|
||||||
android:label="@string/app_name">
|
|
||||||
</application>
|
|
||||||
</manifest>
|
|
||||||
|
|
|
@ -114,11 +114,11 @@ public class OlmAccount extends CommonSerializeUtils implements Serializable {
|
||||||
/**
|
/**
|
||||||
* Return the identity keys (identity and fingerprint keys) in a dictionary.<br>
|
* Return the identity keys (identity and fingerprint keys) in a dictionary.<br>
|
||||||
* Public API for {@link #identityKeysJni()}.<br>
|
* Public API for {@link #identityKeysJni()}.<br>
|
||||||
* Ex:<tt>
|
* Ex:<code>
|
||||||
* {
|
* {
|
||||||
* "curve25519":"Vam++zZPMqDQM6ANKpO/uAl5ViJSHxV9hd+b0/fwRAg",
|
* "curve25519":"Vam++zZPMqDQM6ANKpO/uAl5ViJSHxV9hd+b0/fwRAg",
|
||||||
* "ed25519":"+v8SOlOASFTMrX3MCKBM4iVnYoZ+JIjpNt1fi8Z9O2I"
|
* "ed25519":"+v8SOlOASFTMrX3MCKBM4iVnYoZ+JIjpNt1fi8Z9O2I"
|
||||||
* }</tt>
|
* }</code>
|
||||||
* @return identity keys dictionary if operation succeeds, null otherwise
|
* @return identity keys dictionary if operation succeeds, null otherwise
|
||||||
* @exception OlmException the failure reason
|
* @exception OlmException the failure reason
|
||||||
*/
|
*/
|
||||||
|
@ -195,14 +195,14 @@ public class OlmAccount extends CommonSerializeUtils implements Serializable {
|
||||||
/**
|
/**
|
||||||
* Return the "one time keys" in a dictionary.<br>
|
* Return the "one time keys" in a dictionary.<br>
|
||||||
* The number of "one time keys", is specified by {@link #generateOneTimeKeys(int)}<br>
|
* The number of "one time keys", is specified by {@link #generateOneTimeKeys(int)}<br>
|
||||||
* Ex:<tt>
|
* Ex:<code>
|
||||||
* { "curve25519":
|
* { "curve25519":
|
||||||
* {
|
* {
|
||||||
* "AAAABQ":"qefVZd8qvjOpsFzoKSAdfUnJVkIreyxWFlipCHjSQQg",
|
* "AAAABQ":"qefVZd8qvjOpsFzoKSAdfUnJVkIreyxWFlipCHjSQQg",
|
||||||
* "AAAABA":"/X8szMU+p+lsTnr56wKjaLgjTMQQkCk8EIWEAilZtQ8",
|
* "AAAABA":"/X8szMU+p+lsTnr56wKjaLgjTMQQkCk8EIWEAilZtQ8",
|
||||||
* "AAAAAw":"qxNxxFHzevFntaaPdT0fhhO7tc7pco4+xB/5VRG81hA",
|
* "AAAAAw":"qxNxxFHzevFntaaPdT0fhhO7tc7pco4+xB/5VRG81hA",
|
||||||
* }
|
* }
|
||||||
* }</tt><br>
|
* }</code><br>
|
||||||
* Public API for {@link #oneTimeKeysJni()}.<br>
|
* Public API for {@link #oneTimeKeysJni()}.<br>
|
||||||
* Note: these keys are to be published on the server.
|
* Note: these keys are to be published on the server.
|
||||||
* @return one time keys in string dictionary.
|
* @return one time keys in string dictionary.
|
||||||
|
@ -234,7 +234,7 @@ public class OlmAccount extends CommonSerializeUtils implements Serializable {
|
||||||
/**
|
/**
|
||||||
* Get the public parts of the unpublished "one time keys" for the account.<br>
|
* 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
|
* The returned data is a JSON-formatted object with the single property
|
||||||
* <tt>curve25519</tt>, which is itself an object mapping key id to
|
* <code>curve25519</code>, which is itself an object mapping key id to
|
||||||
* base64-encoded Curve25519 key.<br>
|
* base64-encoded Curve25519 key.<br>
|
||||||
* @return byte array containing the one time keys or throw an exception if it fails
|
* @return byte array containing the one time keys or throw an exception if it fails
|
||||||
*/
|
*/
|
||||||
|
@ -417,4 +417,99 @@ public class OlmAccount extends CommonSerializeUtils implements Serializable {
|
||||||
* @return the deserialized account
|
* @return the deserialized account
|
||||||
**/
|
**/
|
||||||
private native long deserializeJni(byte[] aSerializedDataBuffer, byte[] aKeyBuffer);
|
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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,9 @@ public class OlmException extends IOException {
|
||||||
public static final int EXCEPTION_CODE_ACCOUNT_REMOVE_ONE_TIME_KEYS = 105;
|
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_MARK_ONE_KEYS_AS_PUBLISHED = 106;
|
||||||
public static final int EXCEPTION_CODE_ACCOUNT_SIGN_MESSAGE = 107;
|
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_CREATE_INBOUND_GROUP_SESSION = 200;
|
||||||
public static final int EXCEPTION_CODE_INIT_INBOUND_GROUP_SESSION = 201;
|
public static final int EXCEPTION_CODE_INIT_INBOUND_GROUP_SESSION = 201;
|
||||||
|
@ -57,6 +60,7 @@ public class OlmException extends IOException {
|
||||||
public static final int EXCEPTION_CODE_SESSION_ENCRYPT_MESSAGE = 404;
|
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_DECRYPT_MESSAGE = 405;
|
||||||
public static final int EXCEPTION_CODE_SESSION_SESSION_IDENTIFIER = 406;
|
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_CREATION = 500;
|
||||||
public static final int EXCEPTION_CODE_UTILITY_VERIFY_SIGNATURE = 501;
|
public static final int EXCEPTION_CODE_UTILITY_VERIFY_SIGNATURE = 501;
|
||||||
|
|
|
@ -369,4 +369,29 @@ public class OlmInboundGroupSession extends CommonSerializeUtils implements Seri
|
||||||
* @return the deserialized session
|
* @return the deserialized session
|
||||||
**/
|
**/
|
||||||
private native long deserializeJni(byte[] aSerializedData, byte[] aKey);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ public class OlmManager {
|
||||||
* @return the library version
|
* @return the library version
|
||||||
*/
|
*/
|
||||||
public String getVersion() {
|
public String getVersion() {
|
||||||
return BuildConfig.VERSION_NAME;
|
return BuildConfig.OLM_VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -293,4 +293,28 @@ public class OlmOutboundGroupSession extends CommonSerializeUtils implements Ser
|
||||||
**/
|
**/
|
||||||
private native long deserializeJni(byte[] aSerializedData, byte[] aKey);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,8 @@ public class OlmSAS {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the Public Key encoded in Base64 with no padding
|
* Gets the Public Key encoded in Base64 with no padding
|
||||||
|
* @return The public key
|
||||||
|
* @throws OlmException the failure reason
|
||||||
*/
|
*/
|
||||||
public String getPublicKey() throws OlmException {
|
public String getPublicKey() throws OlmException {
|
||||||
try {
|
try {
|
||||||
|
@ -60,7 +62,7 @@ public class OlmSAS {
|
||||||
* Sets the public key of other user.
|
* Sets the public key of other user.
|
||||||
*
|
*
|
||||||
* @param otherPkey other user public key (base64 encoded with no padding)
|
* @param otherPkey other user public key (base64 encoded with no padding)
|
||||||
* @throws OlmException
|
* @throws OlmException the failure reason
|
||||||
*/
|
*/
|
||||||
public void setTheirPublicKey(String otherPkey) throws OlmException {
|
public void setTheirPublicKey(String otherPkey) throws OlmException {
|
||||||
try {
|
try {
|
||||||
|
@ -79,7 +81,7 @@ public class OlmSAS {
|
||||||
* per the Matrix spec.
|
* per the Matrix spec.
|
||||||
* @param byteNumber The size of the short code to generate
|
* @param byteNumber The size of the short code to generate
|
||||||
* @return The generated shortcode
|
* @return The generated shortcode
|
||||||
* @throws OlmException
|
* @throws OlmException the failure reason
|
||||||
*/
|
*/
|
||||||
public byte[] generateShortCode(String info, int byteNumber) throws OlmException {
|
public byte[] generateShortCode(String info, int byteNumber) throws OlmException {
|
||||||
if (theirPublicKey == null || theirPublicKey.isEmpty()) {
|
if (theirPublicKey == null || theirPublicKey.isEmpty()) {
|
||||||
|
@ -104,6 +106,16 @@ public class OlmSAS {
|
||||||
return null;
|
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 {
|
public String calculateMacLongKdf(String message, String info) throws OlmException {
|
||||||
try {
|
try {
|
||||||
byte[] bytes = calculateMacLongKdfJni(message.getBytes("UTF-8"), info.getBytes("UTF-8"));
|
byte[] bytes = calculateMacLongKdfJni(message.getBytes("UTF-8"), info.getBytes("UTF-8"));
|
||||||
|
@ -138,6 +150,8 @@ public class OlmSAS {
|
||||||
|
|
||||||
private native byte[] calculateMacJni(byte[] message, byte[] info);
|
private native byte[] calculateMacJni(byte[] message, byte[] info);
|
||||||
|
|
||||||
|
private native byte[] calculateMacFixedBase64Jni(byte[] message, byte[] info);
|
||||||
|
|
||||||
private native byte[] calculateMacLongKdfJni(byte[] message, byte[] info);
|
private native byte[] calculateMacLongKdfJni(byte[] message, byte[] info);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -223,6 +223,23 @@ public class OlmSession extends CommonSerializeUtils implements Serializable {
|
||||||
*/
|
*/
|
||||||
private native byte[] getSessionIdentifierJni();
|
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>
|
* 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).
|
* This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
|
||||||
|
@ -448,5 +465,30 @@ public class OlmSession extends CommonSerializeUtils implements Serializable {
|
||||||
* @return the deserialized session
|
* @return the deserialized session
|
||||||
**/
|
**/
|
||||||
private native long deserializeJni(byte[] aSerializedData, byte[] aKey);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
APP_PLATFORM := android-16
|
APP_PLATFORM := android-16
|
||||||
APP_ABI := arm64-v8a armeabi-v7a x86_64 x86
|
APP_ABI := arm64-v8a armeabi-v7a x86_64 x86
|
||||||
APP_STL := gnustl_static
|
APP_STL := c++_static
|
|
@ -442,6 +442,161 @@ JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(markOneTimeKeysAsPublishedJni)(JNIEnv *env,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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>
|
* Sign a message with the ed25519 key (fingerprint) for this account.<br>
|
||||||
* The signed message is returned by the function.
|
* The signed message is returned by the function.
|
||||||
|
|
|
@ -42,6 +42,11 @@ JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(generateOneTimeKeysJni)(JNIEnv *env, jobject
|
||||||
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(removeOneTimeKeysJni)(JNIEnv *env, jobject thiz, jlong aNativeOlmSessionId);
|
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(removeOneTimeKeysJni)(JNIEnv *env, jobject thiz, jlong aNativeOlmSessionId);
|
||||||
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(markOneTimeKeysAsPublishedJni)(JNIEnv *env, jobject thiz);
|
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
|
// signing
|
||||||
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(signMessageJni)(JNIEnv *env, jobject thiz, jbyteArray aMessage);
|
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(signMessageJni)(JNIEnv *env, jobject thiz, jbyteArray aMessage);
|
||||||
|
|
||||||
|
|
|
@ -309,6 +309,86 @@ JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(calculateMacJni)(JNIEnv *env, jobject thiz
|
||||||
return returnValue;
|
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) {
|
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(calculateMacLongKdfJni)(JNIEnv *env, jobject thiz,jbyteArray messageBuffer,jbyteArray infoBuffer) {
|
||||||
LOGD("## calculateMacLongKdfJni(): IN");
|
LOGD("## calculateMacLongKdfJni(): IN");
|
||||||
const char* errorMessage = NULL;
|
const char* errorMessage = NULL;
|
||||||
|
|
|
@ -32,6 +32,7 @@ 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 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(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(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);
|
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(calculateMacLongKdfJni)(JNIEnv *env, jobject thiz, jbyteArray messageBuffer, jbyteArray infoBuffer);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
@ -798,6 +798,58 @@ JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(getSessionIdentifierJni)(JNIEnv *env,
|
||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(olmSessionDescribeJni(JNIEnv *env, jobject thiz))
|
||||||
|
{
|
||||||
|
const char* errorMessage = NULL;
|
||||||
|
jbyteArray returnValue = 0;
|
||||||
|
|
||||||
|
LOGD("## olmSessionDescribeJni(): IN ");
|
||||||
|
|
||||||
|
OlmSession *sessionPtr = getSessionInstanceId(env, thiz);
|
||||||
|
|
||||||
|
if (!sessionPtr)
|
||||||
|
{
|
||||||
|
LOGE("## olmSessionDescribeJni(): failure - invalid Session ptr=NULL");
|
||||||
|
errorMessage = "invalid Session ptr=NULL";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int maxLength = 600;
|
||||||
|
char* describePtr = NULL;
|
||||||
|
describePtr = (char*) malloc(maxLength * sizeof *describePtr);
|
||||||
|
if (!describePtr)
|
||||||
|
{
|
||||||
|
LOGE("## olmSessionDescribeJni(): failure - describe allocation OOM");
|
||||||
|
errorMessage = "describe allocation OOM";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
olm_session_describe(sessionPtr, describePtr, maxLength);
|
||||||
|
int length = strlen(describePtr);
|
||||||
|
if (length == 0)
|
||||||
|
{
|
||||||
|
LOGE("## olmSessionDescribeJni(): failure - get session describe");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOGD("## olmSessionDescribeJni(): success - describe=%.*s", (char*)describePtr);
|
||||||
|
|
||||||
|
returnValue = env->NewByteArray(length);
|
||||||
|
env->SetByteArrayRegion(returnValue, 0, length, (jbyte*)describePtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(describePtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorMessage)
|
||||||
|
{
|
||||||
|
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialize and encrypt session instance.<br>
|
* Serialize and encrypt session instance.<br>
|
||||||
* An exception is thrown if the operation fails.
|
* An exception is thrown if the operation fails.
|
||||||
|
|
|
@ -47,6 +47,7 @@ JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobjec
|
||||||
JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(decryptMessageJni)(JNIEnv *env, jobject thiz, 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(getSessionIdentifierJni)(JNIEnv *env, jobject thiz);
|
||||||
|
JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(olmSessionDescribeJni)(JNIEnv *env, jobject thiz);
|
||||||
|
|
||||||
// serialization
|
// serialization
|
||||||
JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(serializeJni)(JNIEnv *env, jobject thiz, jbyteArray aKey);
|
JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(serializeJni)(JNIEnv *env, jobject thiz, jbyteArray aKey);
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
<resources>
|
|
||||||
<string name="app_name">OlmSdk</string>
|
|
||||||
</resources>
|
|
|
@ -1,4 +1,4 @@
|
||||||
|
|
||||||
MAJOR := 3
|
MAJOR := 3
|
||||||
MINOR := 1
|
MINOR := 2
|
||||||
PATCH := 0
|
PATCH := 16
|
||||||
|
|
1
docs/DH ratchet.svg
Normal file
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
39
docs/DH ratchet.txt
Normal 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
BIN
docs/double_ratchet.dia
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load diff
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 32 KiB |
374
docs/megolm.md
Normal file
374
docs/megolm.md
Normal 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
|
362
docs/megolm.rst
362
docs/megolm.rst
|
@ -1,362 +0,0 @@
|
||||||
.. Copyright 2016 OpenMarket Ltd
|
|
||||||
..
|
|
||||||
.. Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
.. you may not use this file except in compliance with the License.
|
|
||||||
.. You may obtain a copy of the License at
|
|
||||||
..
|
|
||||||
.. http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
..
|
|
||||||
.. Unless required by applicable law or agreed to in writing, software
|
|
||||||
.. distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
.. See the License for the specific language governing permissions and
|
|
||||||
.. limitations under the License.
|
|
||||||
|
|
||||||
|
|
||||||
Megolm group ratchet
|
|
||||||
====================
|
|
||||||
|
|
||||||
An AES-based cryptographic ratchet intended for group communications.
|
|
||||||
|
|
||||||
.. contents::
|
|
||||||
|
|
||||||
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 :math:`R_i` consists of four parts, :math:`R_{i,j}` for
|
|
||||||
:math:`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{align}
|
|
||||||
R_{i,0} &=
|
|
||||||
\begin{cases}
|
|
||||||
H_0\left(R_{2^24(n-1),0}\right) &\text{if }\exists n | i = 2^24n\\
|
|
||||||
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^24n\\
|
|
||||||
H_1\left(R_{2^16(m-1),1}\right) &\text{if }\exists m | i = 2^16m\\
|
|
||||||
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^24n\\
|
|
||||||
H_2\left(R_{2^16(m-1),1}\right) &\text{if }\exists m | i = 2^16m\\
|
|
||||||
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^24n\\
|
|
||||||
H_3\left(R_{2^16(m-1),1}\right) &\text{if }\exists m | i = 2^16m\\
|
|
||||||
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{align}
|
|
||||||
|
|
||||||
where :math:`H_0`, :math:`H_1`, :math:`H_2`, and :math:`H_3` are different hash
|
|
||||||
functions. In summary: every :math:`2^8` iterations, :math:`R_{i,3}` is
|
|
||||||
reseeded from :math:`R_{i,2}`. Every :math:`2^16` iterations, :math:`R_{i,2}`
|
|
||||||
and :math:`R_{i,3}` are reseeded from :math:`R_{i,1}`. Every :math:`2^24`
|
|
||||||
iterations, :math:`R_{i,1}`, :math:`R_{i,2}` and :math:`R_{i,3}` are reseeded
|
|
||||||
from :math:`R_{i,0}`.
|
|
||||||
|
|
||||||
The complete ratchet value, :math:`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 1023 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, :math:`i`.
|
|
||||||
* an `Ed25519`_ keypair, :math:`K`.
|
|
||||||
* a ratchet, :math:`R_i`, which consists of four 256-bit values,
|
|
||||||
:math:`R_{i,j}` for :math:`j \in {0,1,2,3}`.
|
|
||||||
|
|
||||||
The counter :math:`i` is initialised to :math:`0`. A new Ed25519 keypair is
|
|
||||||
generated for :math:`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 :math:`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`_. 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 :math:`R_i`:
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
AES\_KEY_{i}\;\parallel\;HMAC\_KEY_{i}\;\parallel\;AES\_IV_{i}
|
|
||||||
&= HKDF\left(0,\,R_{i},\text{"MEGOLM\_KEYS"},\,80\right) \\
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
where :math:`\parallel` represents string splitting, and
|
|
||||||
:math:`HKDF\left(salt,\,IKM,\,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 :math:`salt`, input key material of
|
|
||||||
:math:`IKM`, context string :math:`info`, and output keying material length of
|
|
||||||
:math:`L` bytes.
|
|
||||||
|
|
||||||
The plain-text is encrypted with AES-256, using the key :math:`AES\_KEY_{i}`
|
|
||||||
and the IV :math:`AES\_IV_{i}` to give the cipher-text, :math:`X_{i}`.
|
|
||||||
|
|
||||||
The ratchet index :math:`i`, and the cipher-text :math:`X_{i}`, are then packed
|
|
||||||
into a message as described in `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 :math:`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`_, using the following definitions:
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
\begin{align}
|
|
||||||
H_0(A) &\equiv HMAC(A,\text{"\textbackslash x00"}) \\
|
|
||||||
H_1(A) &\equiv HMAC(A,\text{"\textbackslash x01"}) \\
|
|
||||||
H_2(A) &\equiv HMAC(A,\text{"\textbackslash x02"}) \\
|
|
||||||
H_3(A) &\equiv HMAC(A,\text{"\textbackslash x03"}) \\
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
where :math:`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`_\ ). 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
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The Megolm key-sharing format is as follows:
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
+---+----+--------+--------+--------+--------+------+-----------+
|
|
||||||
| 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, :math:`i`, which is encoded as a
|
|
||||||
big-endian 32-bit integer; the ratchet values :math:`R_{i,j}`; and the public
|
|
||||||
part of the Ed25519 keypair :math:`K`.
|
|
||||||
|
|
||||||
The data is then signed using the Ed25519 keypair, and the 64-byte signature is
|
|
||||||
appended.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
+---+------------------------------------+-----------+------------------+
|
|
||||||
| 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, :math:`i`
|
|
||||||
Cipher-Text 0x12 String The cipher-text, :math:`X_{i}`, 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
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Once the key to a Megolm session is compromised, the attacker can decrypt any
|
|
||||||
future messages sent via that session.
|
|
||||||
|
|
||||||
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
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
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
|
|
||||||
those past messages.
|
|
||||||
|
|
||||||
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`: ./olm.html
|
|
||||||
.. _`Protocol Buffers encoding`: https://developers.google.com/protocol-buffers/docs/encoding
|
|
331
docs/olm.md
Normal file
331
docs/olm.md
Normal 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
|
358
docs/olm.rst
358
docs/olm.rst
|
@ -1,358 +0,0 @@
|
||||||
Olm: A Cryptographic Ratchet
|
|
||||||
============================
|
|
||||||
|
|
||||||
An implementation of the double cryptographic ratchet described by
|
|
||||||
https://whispersystems.org/docs/specifications/doubleratchet/.
|
|
||||||
|
|
||||||
|
|
||||||
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 one-time 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-1}`. 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 `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{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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
.. admonition:: Example attacks
|
|
||||||
|
|
||||||
1. Alice publishes her public Curve25519 identity key, :math:`I_A`. Eve
|
|
||||||
publishes the same identity key, claiming it as her own. Bob downloads
|
|
||||||
Eve's keys, and associates :math:`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, :math:`I_B`. Eve
|
|
||||||
publishes the same identity key, claiming it as her own. Alice downloads
|
|
||||||
Eve's keys, and associates :math:`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
|
|
|
@ -1,20 +1,4 @@
|
||||||
.. Copyright 2016 OpenMarket Ltd
|
# Signature keys and user identity in libolm
|
||||||
..
|
|
||||||
.. 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.
|
|
||||||
|
|
||||||
|
|
||||||
Signature keys and user identity in libolm
|
|
||||||
==========================================
|
|
||||||
|
|
||||||
The use of any public-key based cryptography system such as Olm presents the
|
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
|
need for our users Alice and Bob to verify that they are in fact communicating
|
||||||
|
@ -23,13 +7,13 @@ 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
|
public keys for each other. For example, this might be done via physical
|
||||||
presence or via a voice call.
|
presence or via a voice call.
|
||||||
|
|
||||||
In the basic `Olm <olm.html>`_ protocol, it is sufficient to compare the public
|
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
|
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
|
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
|
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:
|
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
|
she has neither Alice's nor Bob's private identity key, so cannot successfully
|
||||||
complete the triple-DH calculation to compute the shared secret, :math:`S`,
|
complete the triple-DH calculation to compute the shared secret, $`S`$,
|
||||||
which in turn prevents her decrypting intercepted messages, or from creating
|
which in turn prevents her decrypting intercepted messages, or from creating
|
||||||
new messages with valid MACs. Obviously, for protection to be complete, Bob
|
new messages with valid MACs. Obviously, for protection to be complete, Bob
|
||||||
must similarly verify Alice's key.
|
must similarly verify Alice's key.
|
||||||
|
@ -41,7 +25,7 @@ one-time keys. Curve25519 keys are intended for use in DH calculations, and
|
||||||
their use to calculate signatures is non-trivial.
|
their use to calculate signatures is non-trivial.
|
||||||
|
|
||||||
The solution adopted in this library is to generate a signing key for each
|
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
|
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
|
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
|
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.
|
used as the device fingerprint which Alice and Bob verify with each other.
|
||||||
|
@ -50,8 +34,7 @@ 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
|
level of assurance about the ownership of the Curve25519 identity keys as if
|
||||||
they had compared those directly.
|
they had compared those directly.
|
||||||
|
|
||||||
Signing one-time keys
|
## Signing one-time keys
|
||||||
---------------------
|
|
||||||
|
|
||||||
The Olm protocol requires users to publish a set of one-time keys to a key
|
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
|
server. To establish an Olm session, the originator downloads a key for the
|
||||||
|
@ -60,19 +43,21 @@ is left to the application. There are both advantages and disadvantages to
|
||||||
doing so.
|
doing so.
|
||||||
|
|
||||||
Consider the scenario where one-time keys are unsigned. Alice wants to initiate
|
Consider the scenario where one-time keys are unsigned. Alice wants to initiate
|
||||||
an Olm session with Bob. Bob uploads his one-time keys, :math:`E_B`, but Eve
|
an Olm session with Bob. Bob uploads his one-time keys, $`E_B`$, but Eve
|
||||||
replaces them with ones she controls, :math:`E_E`. Alice downloads one of the
|
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 :math:`S`,
|
compromised keys, and sends a pre-key message using a shared secret $`S`$,
|
||||||
where:
|
where:
|
||||||
|
|
||||||
.. math::
|
```math
|
||||||
S = ECDH\left(I_A,\,E_E\right)\;\parallel\;ECDH\left(E_A,\,I_B\right)\;
|
S = \operatorname{ECDH}\left(I_A,E_E\right)\;\parallel\;
|
||||||
\parallel\;ECDH\left(E_A,\,E_E\right)
|
\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
|
Eve cannot decrypt the message because she does not have the private parts of
|
||||||
either :math:`E_A` nor :math:`I_B`, so cannot calculate
|
either $`E_A`$ nor $`I_B`$, so cannot calculate
|
||||||
:math:`ECDH\left(E_A,\,I_B\right)`. However, suppose she later compromises
|
$`ECDH\left(E_A,I_B\right)`$. However, suppose she later compromises
|
||||||
Bob's identity key :math:`I_B`. This would give her the ability to decrypt any
|
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
|
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
|
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
|
signing key (and Alice verifies the signature before using them), this problem
|
||||||
|
@ -81,38 +66,39 @@ is avoided.
|
||||||
On the other hand, signing the one-time keys leads to a reduction in
|
On the other hand, signing the one-time keys leads to a reduction in
|
||||||
deniability. Recall that the shared secret is calculated as follows:
|
deniability. Recall that the shared secret is calculated as follows:
|
||||||
|
|
||||||
.. math::
|
```math
|
||||||
S = ECDH\left(I_A,\,E_B\right)\;\parallel\;ECDH\left(E_A,\,I_B\right)\;
|
S = \operatorname{ECDH}\left(I_A,E_B\right)\;\parallel\;
|
||||||
\parallel\;ECDH\left(E_A,\,E_B\right)
|
\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 :math:`E_A` and
|
If keys are unsigned, a forger can make up values of $`E_A`$ and
|
||||||
:math:`E_B`, and construct a transcript of a conversation which looks like it
|
$`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
|
was between Alice and Bob. Alice and Bob can therefore plausibly deny their
|
||||||
partition in any conversation even if they are both forced to divulge 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
|
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.
|
a conversation between the two of them, rather than constructed by a forger.
|
||||||
|
|
||||||
If :math:`E_B` is signed, it is no longer possible to construct arbitrary
|
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
|
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,
|
show that at least one of Alice or Bob was involved in the conversation,
|
||||||
because the ability to calculate :math:`ECDH\left(I_A,\,E_B\right)` requires
|
because the ability to calculate $`\operatorname{ECDH}\left(I_A,E_B\right)`$ requires
|
||||||
knowledge of the private parts of either :math:`I_A` (proving Alice's
|
knowledge of the private parts of either $`I_A`$ (proving Alice's
|
||||||
involvement) or :math:`E_B` (proving Bob's involvement, via the
|
involvement) or $`E_B`$ (proving Bob's involvement, via the
|
||||||
signature). Note that it remains impossible to show that *both* Alice and Bob
|
signature). Note that it remains impossible to show that *both* Alice and Bob
|
||||||
were involved.
|
were involved.
|
||||||
|
|
||||||
In conclusion, applications should consider whether to sign one-time keys based
|
In conclusion, applications should consider whether to sign one-time keys based
|
||||||
on the trade-off between forward secrecy and deniability.
|
on the trade-off between forward secrecy and deniability.
|
||||||
|
|
||||||
License
|
## License
|
||||||
-------
|
|
||||||
|
|
||||||
This document is licensed under the `Apache License, Version 2.0
|
This document is licensed under the Apache License, Version 2.0
|
||||||
<http://www.apache.org/licenses/LICENSE-2.0>`_.
|
http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
|
||||||
Feedback
|
## Feedback
|
||||||
--------
|
|
||||||
|
|
||||||
Questions and feedback can be sent to olm at matrix.org.
|
Questions and feedback can be sent to olm at matrix.org.
|
||||||
|
|
||||||
.. _`Ed25519`: http://ed25519.cr.yp.to/
|
[Ed25519]: http://ed25519.cr.yp.to/
|
||||||
|
[Olm]: https://gitlab.matrix.org/matrix-org/olm/blob/master/docs/olm.md
|
|
@ -1,12 +1,12 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
import json
|
import json
|
||||||
|
|
||||||
expr = re.compile(r"(olm_[^( ]*)\(")
|
expr = re.compile(r"(_*olm_[^( ]*)\(")
|
||||||
|
|
||||||
exports = set()
|
exports = {'_free', '_malloc'}
|
||||||
|
|
||||||
for f in sys.argv[1:]:
|
for f in sys.argv[1:]:
|
||||||
with open(f) as fp:
|
with open(f) as fp:
|
||||||
|
|
60
flake.lock
Normal file
60
flake.lock
Normal 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
40
flake.nix
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
|
@ -1,73 +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];
|
|
||||||
|
|
||||||
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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
ignored = write(STDOUT_FILENO, plaintext, length);
|
|
||||||
ignored = write(STDOUT_FILENO, "\n", 1);
|
|
||||||
return ignored;
|
|
||||||
}
|
|
|
@ -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
10
fuzzing/README.md
Normal 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.
|
|
@ -11,4 +11,6 @@ int main(int argc, const char *argv[]) {
|
||||||
decode_message(*reader, message_buffer, message_length, 8);
|
decode_message(*reader, message_buffer, message_length, 8);
|
||||||
free(message_buffer);
|
free(message_buffer);
|
||||||
delete reader;
|
delete reader;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
|
@ -3,11 +3,10 @@
|
||||||
#include "fuzzing.hh"
|
#include "fuzzing.hh"
|
||||||
|
|
||||||
int main(int argc, const char *argv[]) {
|
int main(int argc, const char *argv[]) {
|
||||||
size_t ignored;
|
|
||||||
if (argc <= 3) {
|
if (argc <= 3) {
|
||||||
const char * message = "Usage: decrypt: <session_key> <session_file>"
|
const char * message = "Usage: decrypt: <session_key> <session_file>"
|
||||||
" <message_type>\n";
|
" <message_type>\n";
|
||||||
ignored = write(STDERR_FILENO, message, strlen(message));
|
(void)write(STDERR_FILENO, message, strlen(message));
|
||||||
exit(3);
|
exit(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +58,12 @@ int main(int argc, const char *argv[]) {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
ignored = write(STDOUT_FILENO, plaintext, length);
|
(void)write(STDOUT_FILENO, plaintext, length);
|
||||||
ignored = write(STDOUT_FILENO, "\n", 1);
|
(void)write(STDOUT_FILENO, "\n", 1);
|
||||||
return ignored;
|
|
||||||
|
free(session_buffer);
|
||||||
|
free(message_buffer);
|
||||||
|
free(tmp_buffer);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
102
fuzzing/fuzzers/fuzz_group_decrypt.cpp
Normal file
102
fuzzing/fuzzers/fuzz_group_decrypt.cpp
Normal 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;
|
||||||
|
}
|
41
fuzzing/fuzzers/fuzz_unpickle_account.cpp
Normal file
41
fuzzing/fuzzers/fuzz_unpickle_account.cpp
Normal 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;
|
||||||
|
}
|
28
fuzzing/fuzzers/fuzz_unpickle_megolm_outbound.c
Normal file
28
fuzzing/fuzzers/fuzz_unpickle_megolm_outbound.c
Normal 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;
|
||||||
|
}
|
|
@ -11,4 +11,6 @@ int main(int argc, const char *argv[]) {
|
||||||
unpickle(pickle_buffer, pickle_buffer + pickle_length, *session);
|
unpickle(pickle_buffer, pickle_buffer + pickle_length, *session);
|
||||||
free(pickle_buffer);
|
free(pickle_buffer);
|
||||||
delete session;
|
delete session;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
101
fuzzing/fuzzers/include/fuzzing.h
Normal file
101
fuzzing/fuzzers/include/fuzzing.h
Normal 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);
|
||||||
|
}
|
|
@ -15,28 +15,43 @@ ssize_t read_file(
|
||||||
uint8_t **buffer
|
uint8_t **buffer
|
||||||
) {
|
) {
|
||||||
size_t buffer_size = 4096;
|
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;
|
size_t buffer_pos = 0;
|
||||||
|
uint8_t * current_buffer = (uint8_t *) malloc(buffer_size);
|
||||||
|
if (!current_buffer) return -1;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
ssize_t count = read(
|
ssize_t count = read(
|
||||||
fd, current_buffer + buffer_pos, buffer_size - buffer_pos
|
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) {
|
if (count == 0) {
|
||||||
uint8_t * return_buffer = (uint8_t *) realloc(current_buffer, buffer_pos);
|
// Nothing more left to read. We downsize the buffer to fit the
|
||||||
if (return_buffer == NULL) break;
|
// data exactly, unless no data was read at all, in which case we
|
||||||
*buffer = return_buffer;
|
// 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;
|
return buffer_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer_pos += count;
|
buffer_pos += count;
|
||||||
|
|
||||||
|
// We've reached capacity, so enlarge the buffer.
|
||||||
if (buffer_pos == buffer_size) {
|
if (buffer_pos == buffer_size) {
|
||||||
buffer_size *= 2;
|
buffer_size *= 2;
|
||||||
uint8_t * new_buffer = (uint8_t *) realloc(current_buffer, buffer_size);
|
uint8_t * new_buffer = (uint8_t *) realloc(current_buffer, buffer_size);
|
||||||
if (new_buffer == NULL) break;
|
if (!new_buffer) break;
|
||||||
current_buffer = new_buffer;
|
current_buffer = new_buffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(current_buffer);
|
free(current_buffer);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -62,13 +77,12 @@ size_t check_error(
|
||||||
) {
|
) {
|
||||||
if (value == olm_error()) {
|
if (value == olm_error()) {
|
||||||
const char * olm_message = f(object);
|
const char * olm_message = f(object);
|
||||||
ssize_t ignored;
|
(void)write(STDERR_FILENO, message, strlen(message));
|
||||||
ignored = write(STDERR_FILENO, message, strlen(message));
|
(void)write(STDERR_FILENO, ": ", 2);
|
||||||
ignored = write(STDERR_FILENO, ": ", 2);
|
(void)write(STDERR_FILENO, olm_message, strlen(olm_message));
|
||||||
ignored = write(STDERR_FILENO, olm_message, strlen(olm_message));
|
(void)write(STDERR_FILENO, "\n", 1);
|
||||||
ignored = write(STDERR_FILENO, "\n", 1);
|
|
||||||
exit(2);
|
exit(2);
|
||||||
return ignored;
|
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
118
fuzzing/start_fuzzers.sh
Executable file
118
fuzzing/start_fuzzers.sh
Executable 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
17
gitlab-math.lua
Normal 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
4
include/module.modulemap
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
module libolm {
|
||||||
|
header "olm/olm.h"
|
||||||
|
export *
|
||||||
|
}
|
|
@ -43,11 +43,14 @@ struct Account {
|
||||||
Account();
|
Account();
|
||||||
IdentityKeys identity_keys;
|
IdentityKeys identity_keys;
|
||||||
List<OneTimeKey, MAX_ONE_TIME_KEYS> one_time_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;
|
std::uint32_t next_one_time_key_id;
|
||||||
OlmErrorCode last_error;
|
OlmErrorCode last_error;
|
||||||
|
|
||||||
/** Number of random bytes needed to create a new account */
|
/** 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
|
/** 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 */
|
* 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 */
|
/** 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
|
/** Output the identity keys for this account as JSON in the following
|
||||||
* format:
|
* format:
|
||||||
|
@ -75,7 +78,7 @@ struct Account {
|
||||||
/**
|
/**
|
||||||
* The length of an ed25519 signature in bytes.
|
* 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.
|
* 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 */
|
/** 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:
|
/** 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
|
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
|
/** Mark the current list of one_time_keys and the current fallback key as
|
||||||
* will no longer be returned by get_one_time_keys_json_length(). */
|
* 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();
|
std::size_t mark_keys_as_published();
|
||||||
|
|
||||||
/** The largest number of one time keys this account can store. */
|
/** 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
|
/** The number of random bytes needed to generate a given number of new one
|
||||||
* time keys. */
|
* time keys. */
|
||||||
std::size_t generate_one_time_keys_random_length(
|
std::size_t generate_one_time_keys_random_length(
|
||||||
std::size_t number_of_keys
|
std::size_t number_of_keys
|
||||||
);
|
) const;
|
||||||
|
|
||||||
/** Generates a number of new one time keys. If the total number of keys
|
/** 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
|
* stored by this account exceeds max_number_of_one_time_keys() then the
|
||||||
|
@ -126,6 +131,49 @@ struct Account {
|
||||||
std::uint8_t const * random, std::size_t random_length
|
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 */
|
/** Lookup a one time key with the given public key */
|
||||||
OneTimeKey const * lookup_key(
|
OneTimeKey const * lookup_key(
|
||||||
_olm_curve25519_public_key const & public_key
|
_olm_curve25519_public_key const & public_key
|
||||||
|
|
|
@ -22,6 +22,10 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.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
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -30,7 +34,7 @@ extern "C" {
|
||||||
/**
|
/**
|
||||||
* The number of bytes of unpadded base64 needed to encode a length of input.
|
* 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
|
size_t input_length
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -42,7 +46,7 @@ size_t _olm_encode_base64_length(
|
||||||
*
|
*
|
||||||
* Returns number of bytes encoded
|
* 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 const * input, size_t input_length,
|
||||||
uint8_t * output
|
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.
|
* 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.
|
* 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
|
size_t input_length
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -63,7 +67,7 @@ size_t _olm_decode_base64_length(
|
||||||
*
|
*
|
||||||
* Returns number of bytes decoded
|
* 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 const * input, size_t input_length,
|
||||||
uint8_t * output
|
uint8_t * output
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,12 +18,16 @@
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#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 {
|
namespace olm {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of bytes of unpadded base64 needed to encode a length of input.
|
* 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
|
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.
|
* 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.
|
* 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 const * input, std::size_t input_length,
|
||||||
std::uint8_t * output
|
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.
|
* 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.
|
* 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
|
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.
|
* Writes decode_base64_length(input_length) bytes to the output buffer.
|
||||||
* The output can overlap with the first three quarters of the input 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.
|
* 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 const * input, std::size_t input_length,
|
||||||
std::uint8_t * output
|
std::uint8_t * output
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,6 +19,10 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.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
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -111,7 +115,7 @@ struct _olm_cipher_aes_sha_256 {
|
||||||
size_t kdf_info_length;
|
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.
|
* get an initializer for an instance of struct _olm_cipher_aes_sha_256.
|
||||||
|
|
|
@ -20,6 +20,10 @@
|
||||||
#ifndef OLM_CRYPTO_H_
|
#ifndef OLM_CRYPTO_H_
|
||||||
#define OLM_CRYPTO_H_
|
#define OLM_CRYPTO_H_
|
||||||
|
|
||||||
|
// Note: exports in this file are only for unit tests. Nobody else should be
|
||||||
|
// using this externally
|
||||||
|
#include "olm/olm_export.h"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
@ -94,13 +98,13 @@ struct _olm_ed25519_key_pair {
|
||||||
|
|
||||||
|
|
||||||
/** The length of output the aes_encrypt_cbc function will write */
|
/** The length of output the aes_encrypt_cbc function will write */
|
||||||
size_t _olm_crypto_aes_encrypt_cbc_length(
|
OLM_EXPORT size_t _olm_crypto_aes_encrypt_cbc_length(
|
||||||
size_t input_length
|
size_t input_length
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Encrypts the input using AES256 in CBC mode with PKCS#7 padding.
|
/** Encrypts the input using AES256 in CBC mode with PKCS#7 padding.
|
||||||
* The output buffer must be big enough to hold the output including padding */
|
* The output buffer must be big enough to hold the output including padding */
|
||||||
void _olm_crypto_aes_encrypt_cbc(
|
OLM_EXPORT void _olm_crypto_aes_encrypt_cbc(
|
||||||
const struct _olm_aes256_key *key,
|
const struct _olm_aes256_key *key,
|
||||||
const struct _olm_aes256_iv *iv,
|
const struct _olm_aes256_iv *iv,
|
||||||
const uint8_t *input, size_t input_length,
|
const uint8_t *input, size_t input_length,
|
||||||
|
@ -111,7 +115,7 @@ void _olm_crypto_aes_encrypt_cbc(
|
||||||
* least the same size as the input buffer. Returns the length of the plaintext
|
* least the same size as the input buffer. Returns the length of the plaintext
|
||||||
* without padding on success or std::size_t(-1) if the padding is invalid.
|
* without padding on success or std::size_t(-1) if the padding is invalid.
|
||||||
*/
|
*/
|
||||||
size_t _olm_crypto_aes_decrypt_cbc(
|
OLM_EXPORT size_t _olm_crypto_aes_decrypt_cbc(
|
||||||
const struct _olm_aes256_key *key,
|
const struct _olm_aes256_key *key,
|
||||||
const struct _olm_aes256_iv *iv,
|
const struct _olm_aes256_iv *iv,
|
||||||
uint8_t const * input, size_t input_length,
|
uint8_t const * input, size_t input_length,
|
||||||
|
@ -121,7 +125,7 @@ size_t _olm_crypto_aes_decrypt_cbc(
|
||||||
|
|
||||||
/** Computes SHA-256 of the input. The output buffer must be a least
|
/** Computes SHA-256 of the input. The output buffer must be a least
|
||||||
* SHA256_OUTPUT_LENGTH (32) bytes long. */
|
* SHA256_OUTPUT_LENGTH (32) bytes long. */
|
||||||
void _olm_crypto_sha256(
|
OLM_EXPORT void _olm_crypto_sha256(
|
||||||
uint8_t const * input, size_t input_length,
|
uint8_t const * input, size_t input_length,
|
||||||
uint8_t * output
|
uint8_t * output
|
||||||
);
|
);
|
||||||
|
@ -130,7 +134,7 @@ void _olm_crypto_sha256(
|
||||||
* http://tools.ietf.org/html/rfc2104
|
* http://tools.ietf.org/html/rfc2104
|
||||||
* Computes HMAC-SHA-256 of the input for the key. The output buffer must
|
* Computes HMAC-SHA-256 of the input for the key. The output buffer must
|
||||||
* be at least SHA256_OUTPUT_LENGTH (32) bytes long. */
|
* be at least SHA256_OUTPUT_LENGTH (32) bytes long. */
|
||||||
void _olm_crypto_hmac_sha256(
|
OLM_EXPORT void _olm_crypto_hmac_sha256(
|
||||||
uint8_t const * key, size_t key_length,
|
uint8_t const * key, size_t key_length,
|
||||||
uint8_t const * input, size_t input_length,
|
uint8_t const * input, size_t input_length,
|
||||||
uint8_t * output
|
uint8_t * output
|
||||||
|
@ -140,7 +144,7 @@ void _olm_crypto_hmac_sha256(
|
||||||
/** HMAC-based Key Derivation Function (HKDF)
|
/** HMAC-based Key Derivation Function (HKDF)
|
||||||
* https://tools.ietf.org/html/rfc5869
|
* https://tools.ietf.org/html/rfc5869
|
||||||
* Derives key material from the input bytes. */
|
* Derives key material from the input bytes. */
|
||||||
void _olm_crypto_hkdf_sha256(
|
OLM_EXPORT void _olm_crypto_hkdf_sha256(
|
||||||
uint8_t const * input, size_t input_length,
|
uint8_t const * input, size_t input_length,
|
||||||
uint8_t const * info, size_t info_length,
|
uint8_t const * info, size_t info_length,
|
||||||
uint8_t const * salt, size_t salt_length,
|
uint8_t const * salt, size_t salt_length,
|
||||||
|
@ -151,7 +155,7 @@ void _olm_crypto_hkdf_sha256(
|
||||||
/** Generate a curve25519 key pair
|
/** Generate a curve25519 key pair
|
||||||
* random_32_bytes should be CURVE25519_RANDOM_LENGTH (32) bytes long.
|
* random_32_bytes should be CURVE25519_RANDOM_LENGTH (32) bytes long.
|
||||||
*/
|
*/
|
||||||
void _olm_crypto_curve25519_generate_key(
|
OLM_EXPORT void _olm_crypto_curve25519_generate_key(
|
||||||
uint8_t const * random_32_bytes,
|
uint8_t const * random_32_bytes,
|
||||||
struct _olm_curve25519_key_pair *output
|
struct _olm_curve25519_key_pair *output
|
||||||
);
|
);
|
||||||
|
@ -160,7 +164,7 @@ void _olm_crypto_curve25519_generate_key(
|
||||||
/** Create a shared secret using our private key and their public key.
|
/** Create a shared secret using our private key and their public key.
|
||||||
* The output buffer must be at least CURVE25519_SHARED_SECRET_LENGTH (32) bytes long.
|
* The output buffer must be at least CURVE25519_SHARED_SECRET_LENGTH (32) bytes long.
|
||||||
*/
|
*/
|
||||||
void _olm_crypto_curve25519_shared_secret(
|
OLM_EXPORT void _olm_crypto_curve25519_shared_secret(
|
||||||
const struct _olm_curve25519_key_pair *our_key,
|
const struct _olm_curve25519_key_pair *our_key,
|
||||||
const struct _olm_curve25519_public_key *their_key,
|
const struct _olm_curve25519_public_key *their_key,
|
||||||
uint8_t * output
|
uint8_t * output
|
||||||
|
@ -169,7 +173,7 @@ void _olm_crypto_curve25519_shared_secret(
|
||||||
/** Generate an ed25519 key pair
|
/** Generate an ed25519 key pair
|
||||||
* random_32_bytes should be ED25519_RANDOM_LENGTH (32) bytes long.
|
* random_32_bytes should be ED25519_RANDOM_LENGTH (32) bytes long.
|
||||||
*/
|
*/
|
||||||
void _olm_crypto_ed25519_generate_key(
|
OLM_EXPORT void _olm_crypto_ed25519_generate_key(
|
||||||
uint8_t const * random_bytes,
|
uint8_t const * random_bytes,
|
||||||
struct _olm_ed25519_key_pair *output
|
struct _olm_ed25519_key_pair *output
|
||||||
);
|
);
|
||||||
|
@ -178,7 +182,7 @@ void _olm_crypto_ed25519_generate_key(
|
||||||
*
|
*
|
||||||
* The output buffer must be at least ED25519_SIGNATURE_LENGTH (64) bytes
|
* The output buffer must be at least ED25519_SIGNATURE_LENGTH (64) bytes
|
||||||
* long. */
|
* long. */
|
||||||
void _olm_crypto_ed25519_sign(
|
OLM_EXPORT void _olm_crypto_ed25519_sign(
|
||||||
const struct _olm_ed25519_key_pair *our_key,
|
const struct _olm_ed25519_key_pair *our_key,
|
||||||
const uint8_t * message, size_t message_length,
|
const uint8_t * message, size_t message_length,
|
||||||
uint8_t * output
|
uint8_t * output
|
||||||
|
@ -187,7 +191,7 @@ void _olm_crypto_ed25519_sign(
|
||||||
/** Verify an ed25519 signature
|
/** Verify an ed25519 signature
|
||||||
* The signature input buffer must be ED25519_SIGNATURE_LENGTH (64) bytes long.
|
* The signature input buffer must be ED25519_SIGNATURE_LENGTH (64) bytes long.
|
||||||
* Returns non-zero if the signature is valid. */
|
* Returns non-zero if the signature is valid. */
|
||||||
int _olm_crypto_ed25519_verify(
|
OLM_EXPORT int _olm_crypto_ed25519_verify(
|
||||||
const struct _olm_ed25519_public_key *their_key,
|
const struct _olm_ed25519_public_key *their_key,
|
||||||
const uint8_t * message, size_t message_length,
|
const uint8_t * message, size_t message_length,
|
||||||
const uint8_t * signature
|
const uint8_t * signature
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
#ifndef OLM_ERROR_H_
|
#ifndef OLM_ERROR_H_
|
||||||
#define OLM_ERROR_H_
|
#define OLM_ERROR_H_
|
||||||
|
|
||||||
|
#include "olm/olm_export.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -53,17 +55,23 @@ enum OlmErrorCode {
|
||||||
|
|
||||||
OLM_INPUT_BUFFER_TOO_SMALL = 15,
|
OLM_INPUT_BUFFER_TOO_SMALL = 15,
|
||||||
|
|
||||||
// Not an error code, just here to pad out the enum past 16 because
|
/**
|
||||||
// otherwise the compiler warns about a redunant check. If you're
|
* SAS doesn't have their key set.
|
||||||
// adding an error code, replace this one!
|
*/
|
||||||
OLM_ERROR_NOT_INVENTED_YET = 16,
|
OLM_SAS_THEIR_KEY_NOT_SET = 16,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pickled object was successfully decoded, but the unpickling still failed
|
||||||
|
* because it had some extraneous junk data at the end.
|
||||||
|
*/
|
||||||
|
OLM_PICKLE_EXTRA_DATA = 17,
|
||||||
|
|
||||||
/* remember to update the list of string constants in error.c when updating
|
/* remember to update the list of string constants in error.c when updating
|
||||||
* this list. */
|
* this list. */
|
||||||
};
|
};
|
||||||
|
|
||||||
/** get a string representation of the given error code. */
|
/** get a string representation of the given error code. */
|
||||||
const char * _olm_error_to_string(enum OlmErrorCode error);
|
OLM_EXPORT const char * _olm_error_to_string(enum OlmErrorCode error);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
|
@ -18,6 +18,10 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "olm/error.h"
|
||||||
|
|
||||||
|
#include "olm/olm_export.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -25,31 +29,38 @@ extern "C" {
|
||||||
typedef struct OlmInboundGroupSession OlmInboundGroupSession;
|
typedef struct OlmInboundGroupSession OlmInboundGroupSession;
|
||||||
|
|
||||||
/** get the size of an inbound group session, in bytes. */
|
/** get the size of an inbound group session, in bytes. */
|
||||||
size_t olm_inbound_group_session_size(void);
|
OLM_EXPORT size_t olm_inbound_group_session_size(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise an inbound group session object using the supplied memory
|
* Initialise an inbound group session object using the supplied memory
|
||||||
* The supplied memory should be at least olm_inbound_group_session_size()
|
* The supplied memory should be at least olm_inbound_group_session_size()
|
||||||
* bytes.
|
* bytes.
|
||||||
*/
|
*/
|
||||||
OlmInboundGroupSession * olm_inbound_group_session(
|
OLM_EXPORT OlmInboundGroupSession * olm_inbound_group_session(
|
||||||
void *memory
|
void *memory
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A null terminated string describing the most recent error to happen to a
|
* A null terminated string describing the most recent error to happen to a
|
||||||
* group session */
|
* group session */
|
||||||
const char *olm_inbound_group_session_last_error(
|
OLM_EXPORT const char *olm_inbound_group_session_last_error(
|
||||||
|
const OlmInboundGroupSession *session
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An error code describing the most recent error to happen to a group
|
||||||
|
* session */
|
||||||
|
OLM_EXPORT enum OlmErrorCode olm_inbound_group_session_last_error_code(
|
||||||
const OlmInboundGroupSession *session
|
const OlmInboundGroupSession *session
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Clears the memory used to back this group session */
|
/** Clears the memory used to back this group session */
|
||||||
size_t olm_clear_inbound_group_session(
|
OLM_EXPORT size_t olm_clear_inbound_group_session(
|
||||||
OlmInboundGroupSession *session
|
OlmInboundGroupSession *session
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Returns the number of bytes needed to store an inbound group session */
|
/** Returns the number of bytes needed to store an inbound group session */
|
||||||
size_t olm_pickle_inbound_group_session_length(
|
OLM_EXPORT size_t olm_pickle_inbound_group_session_length(
|
||||||
const OlmInboundGroupSession *session
|
const OlmInboundGroupSession *session
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -61,7 +72,7 @@ size_t olm_pickle_inbound_group_session_length(
|
||||||
* is smaller than olm_pickle_inbound_group_session_length() then
|
* is smaller than olm_pickle_inbound_group_session_length() then
|
||||||
* olm_inbound_group_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL"
|
* olm_inbound_group_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL"
|
||||||
*/
|
*/
|
||||||
size_t olm_pickle_inbound_group_session(
|
OLM_EXPORT size_t olm_pickle_inbound_group_session(
|
||||||
OlmInboundGroupSession *session,
|
OlmInboundGroupSession *session,
|
||||||
void const * key, size_t key_length,
|
void const * key, size_t key_length,
|
||||||
void * pickled, size_t pickled_length
|
void * pickled, size_t pickled_length
|
||||||
|
@ -77,7 +88,7 @@ size_t olm_pickle_inbound_group_session(
|
||||||
* olm_inbound_group_session_last_error() will be "INVALID_BASE64". The input
|
* olm_inbound_group_session_last_error() will be "INVALID_BASE64". The input
|
||||||
* pickled buffer is destroyed
|
* pickled buffer is destroyed
|
||||||
*/
|
*/
|
||||||
size_t olm_unpickle_inbound_group_session(
|
OLM_EXPORT size_t olm_unpickle_inbound_group_session(
|
||||||
OlmInboundGroupSession *session,
|
OlmInboundGroupSession *session,
|
||||||
void const * key, size_t key_length,
|
void const * key, size_t key_length,
|
||||||
void * pickled, size_t pickled_length
|
void * pickled, size_t pickled_length
|
||||||
|
@ -94,7 +105,7 @@ size_t olm_unpickle_inbound_group_session(
|
||||||
* * OLM_INVALID_BASE64 if the session_key is not valid base64
|
* * OLM_INVALID_BASE64 if the session_key is not valid base64
|
||||||
* * OLM_BAD_SESSION_KEY if the session_key is invalid
|
* * OLM_BAD_SESSION_KEY if the session_key is invalid
|
||||||
*/
|
*/
|
||||||
size_t olm_init_inbound_group_session(
|
OLM_EXPORT size_t olm_init_inbound_group_session(
|
||||||
OlmInboundGroupSession *session,
|
OlmInboundGroupSession *session,
|
||||||
/* base64-encoded keys */
|
/* base64-encoded keys */
|
||||||
uint8_t const * session_key, size_t session_key_length
|
uint8_t const * session_key, size_t session_key_length
|
||||||
|
@ -109,7 +120,7 @@ size_t olm_init_inbound_group_session(
|
||||||
* * OLM_INVALID_BASE64 if the session_key is not valid base64
|
* * OLM_INVALID_BASE64 if the session_key is not valid base64
|
||||||
* * OLM_BAD_SESSION_KEY if the session_key is invalid
|
* * OLM_BAD_SESSION_KEY if the session_key is invalid
|
||||||
*/
|
*/
|
||||||
size_t olm_import_inbound_group_session(
|
OLM_EXPORT size_t olm_import_inbound_group_session(
|
||||||
OlmInboundGroupSession *session,
|
OlmInboundGroupSession *session,
|
||||||
/* base64-encoded keys; note that it will be overwritten with the base64-decoded
|
/* base64-encoded keys; note that it will be overwritten with the base64-decoded
|
||||||
data. */
|
data. */
|
||||||
|
@ -126,7 +137,7 @@ size_t olm_import_inbound_group_session(
|
||||||
*
|
*
|
||||||
* Returns olm_error() on failure.
|
* Returns olm_error() on failure.
|
||||||
*/
|
*/
|
||||||
size_t olm_group_decrypt_max_plaintext_length(
|
OLM_EXPORT size_t olm_group_decrypt_max_plaintext_length(
|
||||||
OlmInboundGroupSession *session,
|
OlmInboundGroupSession *session,
|
||||||
uint8_t * message, size_t message_length
|
uint8_t * message, size_t message_length
|
||||||
);
|
);
|
||||||
|
@ -150,7 +161,7 @@ size_t olm_group_decrypt_max_plaintext_length(
|
||||||
* message's index (ie, it was sent before the session key was shared with
|
* message's index (ie, it was sent before the session key was shared with
|
||||||
* us)
|
* us)
|
||||||
*/
|
*/
|
||||||
size_t olm_group_decrypt(
|
OLM_EXPORT size_t olm_group_decrypt(
|
||||||
OlmInboundGroupSession *session,
|
OlmInboundGroupSession *session,
|
||||||
|
|
||||||
/* input; note that it will be overwritten with the base64-decoded
|
/* input; note that it will be overwritten with the base64-decoded
|
||||||
|
@ -166,7 +177,7 @@ size_t olm_group_decrypt(
|
||||||
/**
|
/**
|
||||||
* Get the number of bytes returned by olm_inbound_group_session_id()
|
* Get the number of bytes returned by olm_inbound_group_session_id()
|
||||||
*/
|
*/
|
||||||
size_t olm_inbound_group_session_id_length(
|
OLM_EXPORT size_t olm_inbound_group_session_id_length(
|
||||||
const OlmInboundGroupSession *session
|
const OlmInboundGroupSession *session
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -178,7 +189,7 @@ size_t olm_inbound_group_session_id_length(
|
||||||
* last_error will be OUTPUT_BUFFER_TOO_SMALL if the id buffer was too
|
* last_error will be OUTPUT_BUFFER_TOO_SMALL if the id buffer was too
|
||||||
* small.
|
* small.
|
||||||
*/
|
*/
|
||||||
size_t olm_inbound_group_session_id(
|
OLM_EXPORT size_t olm_inbound_group_session_id(
|
||||||
OlmInboundGroupSession *session,
|
OlmInboundGroupSession *session,
|
||||||
uint8_t * id, size_t id_length
|
uint8_t * id, size_t id_length
|
||||||
);
|
);
|
||||||
|
@ -186,7 +197,7 @@ size_t olm_inbound_group_session_id(
|
||||||
/**
|
/**
|
||||||
* Get the first message index we know how to decrypt.
|
* Get the first message index we know how to decrypt.
|
||||||
*/
|
*/
|
||||||
uint32_t olm_inbound_group_session_first_known_index(
|
OLM_EXPORT uint32_t olm_inbound_group_session_first_known_index(
|
||||||
const OlmInboundGroupSession *session
|
const OlmInboundGroupSession *session
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -199,14 +210,14 @@ uint32_t olm_inbound_group_session_first_known_index(
|
||||||
*
|
*
|
||||||
* This is mainly intended for the unit tests, currently.
|
* This is mainly intended for the unit tests, currently.
|
||||||
*/
|
*/
|
||||||
int olm_inbound_group_session_is_verified(
|
OLM_EXPORT int olm_inbound_group_session_is_verified(
|
||||||
const OlmInboundGroupSession *session
|
const OlmInboundGroupSession *session
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the number of bytes returned by olm_export_inbound_group_session()
|
* Get the number of bytes returned by olm_export_inbound_group_session()
|
||||||
*/
|
*/
|
||||||
size_t olm_export_inbound_group_session_length(
|
OLM_EXPORT size_t olm_export_inbound_group_session_length(
|
||||||
const OlmInboundGroupSession *session
|
const OlmInboundGroupSession *session
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -222,7 +233,7 @@ size_t olm_export_inbound_group_session_length(
|
||||||
* given index (ie, it was sent before the session key was shared with
|
* given index (ie, it was sent before the session key was shared with
|
||||||
* us)
|
* us)
|
||||||
*/
|
*/
|
||||||
size_t olm_export_inbound_group_session(
|
OLM_EXPORT size_t olm_export_inbound_group_session(
|
||||||
OlmInboundGroupSession *session,
|
OlmInboundGroupSession *session,
|
||||||
uint8_t * key, size_t key_length, uint32_t message_index
|
uint8_t * key, size_t key_length, uint32_t message_index
|
||||||
);
|
);
|
||||||
|
|
|
@ -99,9 +99,9 @@ public:
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
T * this_pos = _data;
|
T * this_pos = _data;
|
||||||
T * const other_pos = other._data;
|
const T * other_pos = other._data;
|
||||||
while (other_pos != other._end) {
|
while (other_pos != other._end) {
|
||||||
*this_pos = *other;
|
*this_pos = *other_pos;
|
||||||
++this_pos;
|
++this_pos;
|
||||||
++other_pos;
|
++other_pos;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,10 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.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
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -59,25 +63,25 @@ extern const struct _olm_cipher *megolm_cipher;
|
||||||
* initialize the megolm ratchet. random_data should be at least
|
* initialize the megolm ratchet. random_data should be at least
|
||||||
* MEGOLM_RATCHET_LENGTH bytes of randomness.
|
* MEGOLM_RATCHET_LENGTH bytes of randomness.
|
||||||
*/
|
*/
|
||||||
void megolm_init(Megolm *megolm, uint8_t const *random_data, uint32_t counter);
|
OLM_EXPORT void megolm_init(Megolm *megolm, uint8_t const *random_data, uint32_t counter);
|
||||||
|
|
||||||
/** Returns the number of bytes needed to store a megolm */
|
/** Returns the number of bytes needed to store a megolm */
|
||||||
size_t megolm_pickle_length(const Megolm *megolm);
|
OLM_EXPORT size_t megolm_pickle_length(const Megolm *megolm);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pickle the megolm. Returns a pointer to the next free space in the buffer.
|
* Pickle the megolm. Returns a pointer to the next free space in the buffer.
|
||||||
*/
|
*/
|
||||||
uint8_t * megolm_pickle(const Megolm *megolm, uint8_t *pos);
|
OLM_EXPORT uint8_t * megolm_pickle(const Megolm *megolm, uint8_t *pos);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unpickle the megolm. Returns a pointer to the next item in the buffer.
|
* Unpickle the megolm. Returns a pointer to the next item in the buffer.
|
||||||
*/
|
*/
|
||||||
const uint8_t * megolm_unpickle(Megolm *megolm, const uint8_t *pos,
|
OLM_EXPORT const uint8_t * megolm_unpickle(Megolm *megolm, const uint8_t *pos,
|
||||||
const uint8_t *end);
|
const uint8_t *end);
|
||||||
|
|
||||||
|
|
||||||
/** advance the ratchet by one step */
|
/** advance the ratchet by one step */
|
||||||
void megolm_advance(Megolm *megolm);
|
OLM_EXPORT void megolm_advance(Megolm *megolm);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the key data in the ratchet. The returned data is
|
* get the key data in the ratchet. The returned data is
|
||||||
|
@ -86,7 +90,7 @@ void megolm_advance(Megolm *megolm);
|
||||||
#define megolm_get_data(megolm) ((const uint8_t *)((megolm)->data))
|
#define megolm_get_data(megolm) ((const uint8_t *)((megolm)->data))
|
||||||
|
|
||||||
/** advance the ratchet to a given count */
|
/** advance the ratchet to a given count */
|
||||||
void megolm_advance_to(Megolm *megolm, uint32_t advance_to);
|
OLM_EXPORT void megolm_advance_to(Megolm *megolm, uint32_t advance_to);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
|
@ -27,6 +27,10 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.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
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -34,7 +38,7 @@ extern "C" {
|
||||||
/**
|
/**
|
||||||
* The length of the buffer needed to hold a group message.
|
* The length of the buffer needed to hold a group message.
|
||||||
*/
|
*/
|
||||||
size_t _olm_encode_group_message_length(
|
OLM_EXPORT size_t _olm_encode_group_message_length(
|
||||||
uint32_t chain_index,
|
uint32_t chain_index,
|
||||||
size_t ciphertext_length,
|
size_t ciphertext_length,
|
||||||
size_t mac_length,
|
size_t mac_length,
|
||||||
|
@ -55,7 +59,7 @@ size_t _olm_encode_group_message_length(
|
||||||
*
|
*
|
||||||
* Returns the size of the message, up to the MAC.
|
* Returns the size of the message, up to the MAC.
|
||||||
*/
|
*/
|
||||||
size_t _olm_encode_group_message(
|
OLM_EXPORT size_t _olm_encode_group_message(
|
||||||
uint8_t version,
|
uint8_t version,
|
||||||
uint32_t message_index,
|
uint32_t message_index,
|
||||||
size_t ciphertext_length,
|
size_t ciphertext_length,
|
||||||
|
@ -76,7 +80,7 @@ struct _OlmDecodeGroupMessageResults {
|
||||||
/**
|
/**
|
||||||
* Reads the message headers from the input buffer.
|
* Reads the message headers from the input buffer.
|
||||||
*/
|
*/
|
||||||
void _olm_decode_group_message(
|
OLM_EXPORT void _olm_decode_group_message(
|
||||||
const uint8_t *input, size_t input_length,
|
const uint8_t *input, size_t input_length,
|
||||||
size_t mac_length, size_t signature_length,
|
size_t mac_length, size_t signature_length,
|
||||||
|
|
||||||
|
|
|
@ -27,13 +27,16 @@
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#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 {
|
namespace olm {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the buffer needed to hold a message.
|
* The length of the buffer needed to hold a message.
|
||||||
*/
|
*/
|
||||||
std::size_t encode_message_length(
|
OLM_EXPORT std::size_t encode_message_length(
|
||||||
std::uint32_t counter,
|
std::uint32_t counter,
|
||||||
std::size_t ratchet_key_length,
|
std::size_t ratchet_key_length,
|
||||||
std::size_t ciphertext_length,
|
std::size_t ciphertext_length,
|
||||||
|
@ -61,7 +64,7 @@ struct MessageReader {
|
||||||
* Writes the message headers into the output buffer.
|
* Writes the message headers into the output buffer.
|
||||||
* Populates the writer struct with pointers into the output buffer.
|
* Populates the writer struct with pointers into the output buffer.
|
||||||
*/
|
*/
|
||||||
void encode_message(
|
OLM_EXPORT void encode_message(
|
||||||
MessageWriter & writer,
|
MessageWriter & writer,
|
||||||
std::uint8_t version,
|
std::uint8_t version,
|
||||||
std::uint32_t counter,
|
std::uint32_t counter,
|
||||||
|
@ -75,7 +78,7 @@ void encode_message(
|
||||||
* Reads the message headers from the input buffer.
|
* Reads the message headers from the input buffer.
|
||||||
* Populates the reader struct with pointers into the input buffer.
|
* Populates the reader struct with pointers into the input buffer.
|
||||||
*/
|
*/
|
||||||
void decode_message(
|
OLM_EXPORT void decode_message(
|
||||||
MessageReader & reader,
|
MessageReader & reader,
|
||||||
std::uint8_t const * input, std::size_t input_length,
|
std::uint8_t const * input, std::size_t input_length,
|
||||||
std::size_t mac_length
|
std::size_t mac_length
|
||||||
|
|
|
@ -19,9 +19,12 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "olm/error.h"
|
||||||
#include "olm/inbound_group_session.h"
|
#include "olm/inbound_group_session.h"
|
||||||
#include "olm/outbound_group_session.h"
|
#include "olm/outbound_group_session.h"
|
||||||
|
|
||||||
|
#include "olm/olm_export.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -36,79 +39,94 @@ typedef struct OlmUtility OlmUtility;
|
||||||
/** Get the version number of the library.
|
/** Get the version number of the library.
|
||||||
* Arguments will be updated if non-null.
|
* Arguments will be updated if non-null.
|
||||||
*/
|
*/
|
||||||
void olm_get_library_version(uint8_t *major, uint8_t *minor, uint8_t *patch);
|
OLM_EXPORT void olm_get_library_version(uint8_t *major, uint8_t *minor, uint8_t *patch);
|
||||||
|
|
||||||
/** The size of an account object in bytes */
|
/** The size of an account object in bytes */
|
||||||
size_t olm_account_size(void);
|
OLM_EXPORT size_t olm_account_size(void);
|
||||||
|
|
||||||
/** The size of a session object in bytes */
|
/** The size of a session object in bytes */
|
||||||
size_t olm_session_size(void);
|
OLM_EXPORT size_t olm_session_size(void);
|
||||||
|
|
||||||
/** The size of a utility object in bytes */
|
/** The size of a utility object in bytes */
|
||||||
size_t olm_utility_size(void);
|
OLM_EXPORT size_t olm_utility_size(void);
|
||||||
|
|
||||||
/** Initialise an account object using the supplied memory
|
/** Initialise an account object using the supplied memory
|
||||||
* The supplied memory must be at least olm_account_size() bytes */
|
* The supplied memory must be at least olm_account_size() bytes */
|
||||||
OlmAccount * olm_account(
|
OLM_EXPORT OlmAccount * olm_account(
|
||||||
void * memory
|
void * memory
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Initialise a session object using the supplied memory
|
/** Initialise a session object using the supplied memory
|
||||||
* The supplied memory must be at least olm_session_size() bytes */
|
* The supplied memory must be at least olm_session_size() bytes */
|
||||||
OlmSession * olm_session(
|
OLM_EXPORT OlmSession * olm_session(
|
||||||
void * memory
|
void * memory
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Initialise a utility object using the supplied memory
|
/** Initialise a utility object using the supplied memory
|
||||||
* The supplied memory must be at least olm_utility_size() bytes */
|
* The supplied memory must be at least olm_utility_size() bytes */
|
||||||
OlmUtility * olm_utility(
|
OLM_EXPORT OlmUtility * olm_utility(
|
||||||
void * memory
|
void * memory
|
||||||
);
|
);
|
||||||
|
|
||||||
/** The value that olm will return from a function if there was an error */
|
/** The value that olm will return from a function if there was an error */
|
||||||
size_t olm_error(void);
|
OLM_EXPORT size_t olm_error(void);
|
||||||
|
|
||||||
/** A null terminated string describing the most recent error to happen to an
|
/** A null terminated string describing the most recent error to happen to an
|
||||||
* account */
|
* account */
|
||||||
const char * olm_account_last_error(
|
OLM_EXPORT const char * olm_account_last_error(
|
||||||
OlmAccount * account
|
OlmAccount const * account
|
||||||
|
);
|
||||||
|
|
||||||
|
/** An error code describing the most recent error to happen to an account */
|
||||||
|
OLM_EXPORT enum OlmErrorCode olm_account_last_error_code(
|
||||||
|
OlmAccount const * account
|
||||||
);
|
);
|
||||||
|
|
||||||
/** A null terminated string describing the most recent error to happen to a
|
/** A null terminated string describing the most recent error to happen to a
|
||||||
* session */
|
* session */
|
||||||
const char * olm_session_last_error(
|
OLM_EXPORT const char * olm_session_last_error(
|
||||||
OlmSession * session
|
OlmSession const * session
|
||||||
|
);
|
||||||
|
|
||||||
|
/** An error code describing the most recent error to happen to a session */
|
||||||
|
OLM_EXPORT enum OlmErrorCode olm_session_last_error_code(
|
||||||
|
OlmSession const * session
|
||||||
);
|
);
|
||||||
|
|
||||||
/** A null terminated string describing the most recent error to happen to a
|
/** A null terminated string describing the most recent error to happen to a
|
||||||
* utility */
|
* utility */
|
||||||
const char * olm_utility_last_error(
|
OLM_EXPORT const char * olm_utility_last_error(
|
||||||
OlmUtility * utility
|
OlmUtility const * utility
|
||||||
|
);
|
||||||
|
|
||||||
|
/** An error code describing the most recent error to happen to a utility */
|
||||||
|
OLM_EXPORT enum OlmErrorCode olm_utility_last_error_code(
|
||||||
|
OlmUtility const * utility
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Clears the memory used to back this account */
|
/** Clears the memory used to back this account */
|
||||||
size_t olm_clear_account(
|
OLM_EXPORT size_t olm_clear_account(
|
||||||
OlmAccount * account
|
OlmAccount * account
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Clears the memory used to back this session */
|
/** Clears the memory used to back this session */
|
||||||
size_t olm_clear_session(
|
OLM_EXPORT size_t olm_clear_session(
|
||||||
OlmSession * session
|
OlmSession * session
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Clears the memory used to back this utility */
|
/** Clears the memory used to back this utility */
|
||||||
size_t olm_clear_utility(
|
OLM_EXPORT size_t olm_clear_utility(
|
||||||
OlmUtility * utility
|
OlmUtility * utility
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Returns the number of bytes needed to store an account */
|
/** Returns the number of bytes needed to store an account */
|
||||||
size_t olm_pickle_account_length(
|
OLM_EXPORT size_t olm_pickle_account_length(
|
||||||
OlmAccount * account
|
OlmAccount const * account
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Returns the number of bytes needed to store a session */
|
/** Returns the number of bytes needed to store a session */
|
||||||
size_t olm_pickle_session_length(
|
OLM_EXPORT size_t olm_pickle_session_length(
|
||||||
OlmSession * session
|
OlmSession const * session
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Stores an account as a base64 string. Encrypts the account using the
|
/** Stores an account as a base64 string. Encrypts the account using the
|
||||||
|
@ -116,7 +134,7 @@ size_t olm_pickle_session_length(
|
||||||
* Returns olm_error() on failure. If the pickle output buffer
|
* Returns olm_error() on failure. If the pickle output buffer
|
||||||
* is smaller than olm_pickle_account_length() then
|
* is smaller than olm_pickle_account_length() then
|
||||||
* olm_account_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */
|
* olm_account_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */
|
||||||
size_t olm_pickle_account(
|
OLM_EXPORT size_t olm_pickle_account(
|
||||||
OlmAccount * account,
|
OlmAccount * account,
|
||||||
void const * key, size_t key_length,
|
void const * key, size_t key_length,
|
||||||
void * pickled, size_t pickled_length
|
void * pickled, size_t pickled_length
|
||||||
|
@ -127,7 +145,7 @@ size_t olm_pickle_account(
|
||||||
* Returns olm_error() on failure. If the pickle output buffer
|
* Returns olm_error() on failure. If the pickle output buffer
|
||||||
* is smaller than olm_pickle_session_length() then
|
* is smaller than olm_pickle_session_length() then
|
||||||
* olm_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */
|
* olm_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */
|
||||||
size_t olm_pickle_session(
|
OLM_EXPORT size_t olm_pickle_session(
|
||||||
OlmSession * session,
|
OlmSession * session,
|
||||||
void const * key, size_t key_length,
|
void const * key, size_t key_length,
|
||||||
void * pickled, size_t pickled_length
|
void * pickled, size_t pickled_length
|
||||||
|
@ -139,7 +157,7 @@ size_t olm_pickle_session(
|
||||||
* will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then
|
* will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then
|
||||||
* olm_account_last_error() will be "INVALID_BASE64". The input pickled
|
* olm_account_last_error() will be "INVALID_BASE64". The input pickled
|
||||||
* buffer is destroyed */
|
* buffer is destroyed */
|
||||||
size_t olm_unpickle_account(
|
OLM_EXPORT size_t olm_unpickle_account(
|
||||||
OlmAccount * account,
|
OlmAccount * account,
|
||||||
void const * key, size_t key_length,
|
void const * key, size_t key_length,
|
||||||
void * pickled, size_t pickled_length
|
void * pickled, size_t pickled_length
|
||||||
|
@ -151,57 +169,57 @@ size_t olm_unpickle_account(
|
||||||
* will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then
|
* will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then
|
||||||
* olm_session_last_error() will be "INVALID_BASE64". The input pickled
|
* olm_session_last_error() will be "INVALID_BASE64". The input pickled
|
||||||
* buffer is destroyed */
|
* buffer is destroyed */
|
||||||
size_t olm_unpickle_session(
|
OLM_EXPORT size_t olm_unpickle_session(
|
||||||
OlmSession * session,
|
OlmSession * session,
|
||||||
void const * key, size_t key_length,
|
void const * key, size_t key_length,
|
||||||
void * pickled, size_t pickled_length
|
void * pickled, size_t pickled_length
|
||||||
);
|
);
|
||||||
|
|
||||||
/** The number of random bytes needed to create an account.*/
|
/** The number of random bytes needed to create an account.*/
|
||||||
size_t olm_create_account_random_length(
|
OLM_EXPORT size_t olm_create_account_random_length(
|
||||||
OlmAccount * account
|
OlmAccount const * account
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Creates a new account. Returns olm_error() on failure. If there weren't
|
/** Creates a new account. Returns olm_error() on failure. If there weren't
|
||||||
* enough random bytes then olm_account_last_error() will be
|
* enough random bytes then olm_account_last_error() will be
|
||||||
* "NOT_ENOUGH_RANDOM" */
|
* "NOT_ENOUGH_RANDOM" */
|
||||||
size_t olm_create_account(
|
OLM_EXPORT size_t olm_create_account(
|
||||||
OlmAccount * account,
|
OlmAccount * account,
|
||||||
void * random, size_t random_length
|
void * random, size_t random_length
|
||||||
);
|
);
|
||||||
|
|
||||||
/** The size of the output buffer needed to hold the identity keys */
|
/** The size of the output buffer needed to hold the identity keys */
|
||||||
size_t olm_account_identity_keys_length(
|
OLM_EXPORT size_t olm_account_identity_keys_length(
|
||||||
OlmAccount * account
|
OlmAccount const * account
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Writes the public parts of the identity keys for the account into the
|
/** Writes the public parts of the identity keys for the account into the
|
||||||
* identity_keys output buffer. Returns olm_error() on failure. If the
|
* identity_keys output buffer. Returns olm_error() on failure. If the
|
||||||
* identity_keys buffer was too small then olm_account_last_error() will be
|
* identity_keys buffer was too small then olm_account_last_error() will be
|
||||||
* "OUTPUT_BUFFER_TOO_SMALL". */
|
* "OUTPUT_BUFFER_TOO_SMALL". */
|
||||||
size_t olm_account_identity_keys(
|
OLM_EXPORT size_t olm_account_identity_keys(
|
||||||
OlmAccount * account,
|
OlmAccount * account,
|
||||||
void * identity_keys, size_t identity_key_length
|
void * identity_keys, size_t identity_key_length
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
/** The length of an ed25519 signature encoded as base64. */
|
/** The length of an ed25519 signature encoded as base64. */
|
||||||
size_t olm_account_signature_length(
|
OLM_EXPORT size_t olm_account_signature_length(
|
||||||
OlmAccount * account
|
OlmAccount const * account
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Signs a message with the ed25519 key for this account. Returns olm_error()
|
/** Signs a message with the ed25519 key for this account. Returns olm_error()
|
||||||
* on failure. If the signature buffer was too small then
|
* on failure. If the signature buffer was too small then
|
||||||
* olm_account_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */
|
* olm_account_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */
|
||||||
size_t olm_account_sign(
|
OLM_EXPORT size_t olm_account_sign(
|
||||||
OlmAccount * account,
|
OlmAccount * account,
|
||||||
void const * message, size_t message_length,
|
void const * message, size_t message_length,
|
||||||
void * signature, size_t signature_length
|
void * signature, size_t signature_length
|
||||||
);
|
);
|
||||||
|
|
||||||
/** The size of the output buffer needed to hold the one time keys */
|
/** The size of the output buffer needed to hold the one time keys */
|
||||||
size_t olm_account_one_time_keys_length(
|
OLM_EXPORT size_t olm_account_one_time_keys_length(
|
||||||
OlmAccount * account
|
OlmAccount const * account
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Writes the public parts of the unpublished one time keys for the account
|
/** Writes the public parts of the unpublished one time keys for the account
|
||||||
|
@ -222,25 +240,31 @@ size_t olm_account_one_time_keys_length(
|
||||||
* <p>
|
* <p>
|
||||||
* If the one_time_keys buffer was too small then olm_account_last_error()
|
* If the one_time_keys buffer was too small then olm_account_last_error()
|
||||||
* will be "OUTPUT_BUFFER_TOO_SMALL". */
|
* will be "OUTPUT_BUFFER_TOO_SMALL". */
|
||||||
size_t olm_account_one_time_keys(
|
OLM_EXPORT size_t olm_account_one_time_keys(
|
||||||
OlmAccount * account,
|
OlmAccount * account,
|
||||||
void * one_time_keys, size_t one_time_keys_length
|
void * one_time_keys, size_t one_time_keys_length
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Marks the current set of one time keys as being published. */
|
/** Marks the current set of one time keys and fallback key as being published
|
||||||
size_t olm_account_mark_keys_as_published(
|
* Once marked as published, the one time keys will no longer be returned by
|
||||||
|
* olm_account_one_time_keys(), and the fallback key will no longer be returned
|
||||||
|
* by olm_account_unpublished_fallback_key().
|
||||||
|
*
|
||||||
|
* Returns the number of one-time keys that were marked as published. Note that
|
||||||
|
* this count does not include the fallback key. */
|
||||||
|
OLM_EXPORT size_t olm_account_mark_keys_as_published(
|
||||||
OlmAccount * account
|
OlmAccount * account
|
||||||
);
|
);
|
||||||
|
|
||||||
/** The largest number of one time keys this account can store. */
|
/** The largest number of one time keys this account can store. */
|
||||||
size_t olm_account_max_number_of_one_time_keys(
|
OLM_EXPORT size_t olm_account_max_number_of_one_time_keys(
|
||||||
OlmAccount * account
|
OlmAccount const * account
|
||||||
);
|
);
|
||||||
|
|
||||||
/** The number of random bytes needed to generate a given number of new one
|
/** The number of random bytes needed to generate a given number of new one
|
||||||
* time keys. */
|
* time keys. */
|
||||||
size_t olm_account_generate_one_time_keys_random_length(
|
OLM_EXPORT size_t olm_account_generate_one_time_keys_random_length(
|
||||||
OlmAccount * account,
|
OlmAccount const * account,
|
||||||
size_t number_of_keys
|
size_t number_of_keys
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -248,15 +272,63 @@ size_t olm_account_generate_one_time_keys_random_length(
|
||||||
* by this account exceeds max_number_of_one_time_keys() then the old keys are
|
* by this account exceeds max_number_of_one_time_keys() then the old keys are
|
||||||
* discarded. Returns olm_error() on error. If the number of random bytes is
|
* discarded. Returns olm_error() on error. If the number of random bytes is
|
||||||
* too small then olm_account_last_error() will be "NOT_ENOUGH_RANDOM". */
|
* too small then olm_account_last_error() will be "NOT_ENOUGH_RANDOM". */
|
||||||
size_t olm_account_generate_one_time_keys(
|
OLM_EXPORT size_t olm_account_generate_one_time_keys(
|
||||||
OlmAccount * account,
|
OlmAccount * account,
|
||||||
size_t number_of_keys,
|
size_t number_of_keys,
|
||||||
void * random, size_t random_length
|
void * random, size_t random_length
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/** The number of random bytes needed to generate a fallback key. */
|
||||||
|
OLM_EXPORT size_t olm_account_generate_fallback_key_random_length(
|
||||||
|
OlmAccount const * account
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Generates a new fallback key. Only one previous fallback key is
|
||||||
|
* stored. Returns olm_error() on error. If the number of random bytes is too
|
||||||
|
* small then olm_account_last_error() will be "NOT_ENOUGH_RANDOM". */
|
||||||
|
OLM_EXPORT size_t olm_account_generate_fallback_key(
|
||||||
|
OlmAccount * account,
|
||||||
|
void * random, size_t random_length
|
||||||
|
);
|
||||||
|
|
||||||
|
/** The number of bytes needed to hold the fallback key as returned by
|
||||||
|
* olm_account_fallback_key. */
|
||||||
|
OLM_EXPORT size_t olm_account_fallback_key_length(
|
||||||
|
OlmAccount const * account
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Deprecated: use olm_account_unpublished_fallback_key instead */
|
||||||
|
OLM_EXPORT size_t olm_account_fallback_key(
|
||||||
|
OlmAccount * account,
|
||||||
|
void * fallback_key, size_t fallback_key_size
|
||||||
|
);
|
||||||
|
|
||||||
|
/** The number of bytes needed to hold the unpublished fallback key as returned
|
||||||
|
* by olm_account_unpublished fallback_key. */
|
||||||
|
OLM_EXPORT size_t olm_account_unpublished_fallback_key_length(
|
||||||
|
OlmAccount const * account
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Returns the fallback key (if present, and if unpublished) into the
|
||||||
|
* fallback_key buffer */
|
||||||
|
OLM_EXPORT size_t olm_account_unpublished_fallback_key(
|
||||||
|
OlmAccount * account,
|
||||||
|
void * fallback_key, size_t fallback_key_size
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 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).
|
||||||
|
*/
|
||||||
|
OLM_EXPORT void olm_account_forget_old_fallback_key(
|
||||||
|
OlmAccount * account
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
/** The number of random bytes needed to create an outbound session */
|
/** The number of random bytes needed to create an outbound session */
|
||||||
size_t olm_create_outbound_session_random_length(
|
OLM_EXPORT size_t olm_create_outbound_session_random_length(
|
||||||
OlmSession * session
|
OlmSession const * session
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Creates a new out-bound session for sending messages to a given identity_key
|
/** Creates a new out-bound session for sending messages to a given identity_key
|
||||||
|
@ -264,9 +336,9 @@ size_t olm_create_outbound_session_random_length(
|
||||||
* decoded as base64 then olm_session_last_error() will be "INVALID_BASE64"
|
* decoded as base64 then olm_session_last_error() will be "INVALID_BASE64"
|
||||||
* If there weren't enough random bytes then olm_session_last_error() will
|
* If there weren't enough random bytes then olm_session_last_error() will
|
||||||
* be "NOT_ENOUGH_RANDOM". */
|
* be "NOT_ENOUGH_RANDOM". */
|
||||||
size_t olm_create_outbound_session(
|
OLM_EXPORT size_t olm_create_outbound_session(
|
||||||
OlmSession * session,
|
OlmSession * session,
|
||||||
OlmAccount * account,
|
OlmAccount const * account,
|
||||||
void const * their_identity_key, size_t their_identity_key_length,
|
void const * their_identity_key, size_t their_identity_key_length,
|
||||||
void const * their_one_time_key, size_t their_one_time_key_length,
|
void const * their_one_time_key, size_t their_one_time_key_length,
|
||||||
void * random, size_t random_length
|
void * random, size_t random_length
|
||||||
|
@ -277,24 +349,19 @@ size_t olm_create_outbound_session(
|
||||||
* couldn't be decoded then olm_session_last_error will be "INVALID_BASE64".
|
* couldn't be decoded then olm_session_last_error will be "INVALID_BASE64".
|
||||||
* If the message was for an unsupported protocol version then
|
* If the message was for an unsupported protocol version then
|
||||||
* olm_session_last_error() will be "BAD_MESSAGE_VERSION". If the message
|
* olm_session_last_error() will be "BAD_MESSAGE_VERSION". If the message
|
||||||
* couldn't be decoded then then olm_session_last_error() will be
|
* couldn't be decoded then olm_session_last_error() will be
|
||||||
* "BAD_MESSAGE_FORMAT". If the message refers to an unknown one time
|
* "BAD_MESSAGE_FORMAT". If the message refers to an unknown one time
|
||||||
* key then olm_session_last_error() will be "BAD_MESSAGE_KEY_ID". */
|
* key then olm_session_last_error() will be "BAD_MESSAGE_KEY_ID". */
|
||||||
size_t olm_create_inbound_session(
|
OLM_EXPORT size_t olm_create_inbound_session(
|
||||||
OlmSession * session,
|
OlmSession * session,
|
||||||
OlmAccount * account,
|
OlmAccount * account,
|
||||||
void * one_time_key_message, size_t message_length
|
void * one_time_key_message, size_t message_length
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Create a new in-bound session for sending/receiving messages from an
|
/** Same as olm_create_inbound_session, but ensures that the identity key
|
||||||
* incoming PRE_KEY message. Returns olm_error() on failure. If the base64
|
* in the pre-key message matches the expected identity key, supplied via the
|
||||||
* couldn't be decoded then olm_session_last_error will be "INVALID_BASE64".
|
* `their_identity_key` parameter. Fails early if there is no match. */
|
||||||
* If the message was for an unsupported protocol version then
|
OLM_EXPORT size_t olm_create_inbound_session_from(
|
||||||
* olm_session_last_error() will be "BAD_MESSAGE_VERSION". If the message
|
|
||||||
* couldn't be decoded then then olm_session_last_error() will be
|
|
||||||
* "BAD_MESSAGE_FORMAT". If the message refers to an unknown one time
|
|
||||||
* key then olm_session_last_error() will be "BAD_MESSAGE_KEY_ID". */
|
|
||||||
size_t olm_create_inbound_session_from(
|
|
||||||
OlmSession * session,
|
OlmSession * session,
|
||||||
OlmAccount * account,
|
OlmAccount * account,
|
||||||
void const * their_identity_key, size_t their_identity_key_length,
|
void const * their_identity_key, size_t their_identity_key_length,
|
||||||
|
@ -302,22 +369,31 @@ size_t olm_create_inbound_session_from(
|
||||||
);
|
);
|
||||||
|
|
||||||
/** The length of the buffer needed to return the id for this session. */
|
/** The length of the buffer needed to return the id for this session. */
|
||||||
size_t olm_session_id_length(
|
OLM_EXPORT size_t olm_session_id_length(
|
||||||
OlmSession * session
|
OlmSession const * session
|
||||||
);
|
);
|
||||||
|
|
||||||
/** An identifier for this session. Will be the same for both ends of the
|
/** An identifier for this session. Will be the same for both ends of the
|
||||||
* conversation. If the id buffer is too small then olm_session_last_error()
|
* conversation. If the id buffer is too small then olm_session_last_error()
|
||||||
* will be "OUTPUT_BUFFER_TOO_SMALL". */
|
* will be "OUTPUT_BUFFER_TOO_SMALL". */
|
||||||
size_t olm_session_id(
|
OLM_EXPORT size_t olm_session_id(
|
||||||
OlmSession * session,
|
OlmSession * session,
|
||||||
void * id, size_t id_length
|
void * id, size_t id_length
|
||||||
);
|
);
|
||||||
|
|
||||||
int olm_session_has_received_message(
|
OLM_EXPORT int olm_session_has_received_message(
|
||||||
OlmSession *session
|
OlmSession const *session
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a null-terminated string describing the internal state of an olm
|
||||||
|
* session to the buffer provided for debugging and logging purposes. If the
|
||||||
|
* buffer is not large enough to hold the entire string, it will be truncated
|
||||||
|
* and will end with "...". A buffer length of 600 will be enough to hold any
|
||||||
|
* output.
|
||||||
|
*/
|
||||||
|
OLM_EXPORT void olm_session_describe(OlmSession * session, char *buf, size_t buflen);
|
||||||
|
|
||||||
/** Checks if the PRE_KEY message is for this in-bound session. This can happen
|
/** Checks if the PRE_KEY message is for this in-bound session. This can happen
|
||||||
* if multiple messages are sent to this account before this account sends a
|
* if multiple messages are sent to this account before this account sends a
|
||||||
* message in reply. The one_time_key_message buffer is destroyed. Returns 1 if
|
* message in reply. The one_time_key_message buffer is destroyed. Returns 1 if
|
||||||
|
@ -327,7 +403,7 @@ int olm_session_has_received_message(
|
||||||
* unsupported protocol version then olm_session_last_error() will be
|
* unsupported protocol version then olm_session_last_error() will be
|
||||||
* "BAD_MESSAGE_VERSION". If the message couldn't be decoded then then
|
* "BAD_MESSAGE_VERSION". If the message couldn't be decoded then then
|
||||||
* olm_session_last_error() will be "BAD_MESSAGE_FORMAT". */
|
* olm_session_last_error() will be "BAD_MESSAGE_FORMAT". */
|
||||||
size_t olm_matches_inbound_session(
|
OLM_EXPORT size_t olm_matches_inbound_session(
|
||||||
OlmSession * session,
|
OlmSession * session,
|
||||||
void * one_time_key_message, size_t message_length
|
void * one_time_key_message, size_t message_length
|
||||||
);
|
);
|
||||||
|
@ -341,7 +417,7 @@ size_t olm_matches_inbound_session(
|
||||||
* unsupported protocol version then olm_session_last_error() will be
|
* unsupported protocol version then olm_session_last_error() will be
|
||||||
* "BAD_MESSAGE_VERSION". If the message couldn't be decoded then then
|
* "BAD_MESSAGE_VERSION". If the message couldn't be decoded then then
|
||||||
* olm_session_last_error() will be "BAD_MESSAGE_FORMAT". */
|
* olm_session_last_error() will be "BAD_MESSAGE_FORMAT". */
|
||||||
size_t olm_matches_inbound_session_from(
|
OLM_EXPORT size_t olm_matches_inbound_session_from(
|
||||||
OlmSession * session,
|
OlmSession * session,
|
||||||
void const * their_identity_key, size_t their_identity_key_length,
|
void const * their_identity_key, size_t their_identity_key_length,
|
||||||
void * one_time_key_message, size_t message_length
|
void * one_time_key_message, size_t message_length
|
||||||
|
@ -350,7 +426,7 @@ size_t olm_matches_inbound_session_from(
|
||||||
/** Removes the one time keys that the session used from the account. Returns
|
/** Removes the one time keys that the session used from the account. Returns
|
||||||
* olm_error() on failure. If the account doesn't have any matching one time
|
* olm_error() on failure. If the account doesn't have any matching one time
|
||||||
* keys then olm_account_last_error() will be "BAD_MESSAGE_KEY_ID". */
|
* keys then olm_account_last_error() will be "BAD_MESSAGE_KEY_ID". */
|
||||||
size_t olm_remove_one_time_keys(
|
OLM_EXPORT size_t olm_remove_one_time_keys(
|
||||||
OlmAccount * account,
|
OlmAccount * account,
|
||||||
OlmSession * session
|
OlmSession * session
|
||||||
);
|
);
|
||||||
|
@ -359,19 +435,19 @@ size_t olm_remove_one_time_keys(
|
||||||
* OLM_MESSAGE_TYPE_PRE_KEY if the message will be a PRE_KEY message.
|
* OLM_MESSAGE_TYPE_PRE_KEY if the message will be a PRE_KEY message.
|
||||||
* Returns OLM_MESSAGE_TYPE_MESSAGE if the message will be a normal message.
|
* Returns OLM_MESSAGE_TYPE_MESSAGE if the message will be a normal message.
|
||||||
* Returns olm_error on failure. */
|
* Returns olm_error on failure. */
|
||||||
size_t olm_encrypt_message_type(
|
OLM_EXPORT size_t olm_encrypt_message_type(
|
||||||
OlmSession * session
|
OlmSession const * session
|
||||||
);
|
);
|
||||||
|
|
||||||
/** The number of random bytes needed to encrypt the next message. */
|
/** The number of random bytes needed to encrypt the next message. */
|
||||||
size_t olm_encrypt_random_length(
|
OLM_EXPORT size_t olm_encrypt_random_length(
|
||||||
OlmSession * session
|
OlmSession const * session
|
||||||
);
|
);
|
||||||
|
|
||||||
/** The size of the next message in bytes for the given number of plain-text
|
/** The size of the next message in bytes for the given number of plain-text
|
||||||
* bytes. */
|
* bytes. */
|
||||||
size_t olm_encrypt_message_length(
|
OLM_EXPORT size_t olm_encrypt_message_length(
|
||||||
OlmSession * session,
|
OlmSession const * session,
|
||||||
size_t plaintext_length
|
size_t plaintext_length
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -381,7 +457,7 @@ size_t olm_encrypt_message_length(
|
||||||
* olm_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". If there
|
* olm_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". If there
|
||||||
* weren't enough random bytes then olm_session_last_error() will be
|
* weren't enough random bytes then olm_session_last_error() will be
|
||||||
* "NOT_ENOUGH_RANDOM". */
|
* "NOT_ENOUGH_RANDOM". */
|
||||||
size_t olm_encrypt(
|
OLM_EXPORT size_t olm_encrypt(
|
||||||
OlmSession * session,
|
OlmSession * session,
|
||||||
void const * plaintext, size_t plaintext_length,
|
void const * plaintext, size_t plaintext_length,
|
||||||
void * random, size_t random_length,
|
void * random, size_t random_length,
|
||||||
|
@ -396,7 +472,7 @@ size_t olm_encrypt(
|
||||||
* protocol then olm_session_last_error() will be "BAD_MESSAGE_VERSION".
|
* protocol then olm_session_last_error() will be "BAD_MESSAGE_VERSION".
|
||||||
* If the message couldn't be decoded then olm_session_last_error() will be
|
* If the message couldn't be decoded then olm_session_last_error() will be
|
||||||
* "BAD_MESSAGE_FORMAT". */
|
* "BAD_MESSAGE_FORMAT". */
|
||||||
size_t olm_decrypt_max_plaintext_length(
|
OLM_EXPORT size_t olm_decrypt_max_plaintext_length(
|
||||||
OlmSession * session,
|
OlmSession * session,
|
||||||
size_t message_type,
|
size_t message_type,
|
||||||
void * message, size_t message_length
|
void * message, size_t message_length
|
||||||
|
@ -413,7 +489,7 @@ size_t olm_decrypt_max_plaintext_length(
|
||||||
* olm_session_last_error() will be BAD_MESSAGE_FORMAT".
|
* olm_session_last_error() will be BAD_MESSAGE_FORMAT".
|
||||||
* If the MAC on the message was invalid then olm_session_last_error() will
|
* If the MAC on the message was invalid then olm_session_last_error() will
|
||||||
* be "BAD_MESSAGE_MAC". */
|
* be "BAD_MESSAGE_MAC". */
|
||||||
size_t olm_decrypt(
|
OLM_EXPORT size_t olm_decrypt(
|
||||||
OlmSession * session,
|
OlmSession * session,
|
||||||
size_t message_type,
|
size_t message_type,
|
||||||
void * message, size_t message_length,
|
void * message, size_t message_length,
|
||||||
|
@ -421,23 +497,23 @@ size_t olm_decrypt(
|
||||||
);
|
);
|
||||||
|
|
||||||
/** The length of the buffer needed to hold the SHA-256 hash. */
|
/** The length of the buffer needed to hold the SHA-256 hash. */
|
||||||
size_t olm_sha256_length(
|
OLM_EXPORT size_t olm_sha256_length(
|
||||||
OlmUtility * utility
|
OlmUtility const * utility
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Calculates the SHA-256 hash of the input and encodes it as base64. If the
|
/** Calculates the SHA-256 hash of the input and encodes it as base64. If the
|
||||||
* output buffer is smaller than olm_sha256_length() then
|
* output buffer is smaller than olm_sha256_length() then
|
||||||
* olm_utility_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". */
|
* olm_utility_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". */
|
||||||
size_t olm_sha256(
|
OLM_EXPORT size_t olm_sha256(
|
||||||
OlmUtility * utility,
|
OlmUtility * utility,
|
||||||
void const * input, size_t input_length,
|
void const * input, size_t input_length,
|
||||||
void * output, size_t output_length
|
void * output, size_t output_length
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Verify an ed25519 signature. If the key was too small then
|
/** Verify an ed25519 signature. If the key was too small then
|
||||||
* olm_session_last_error will be "INVALID_BASE64". If the signature was invalid
|
* olm_utility_last_error() will be "INVALID_BASE64". If the signature was invalid
|
||||||
* then olm_utility_last_error() will be "BAD_MESSAGE_MAC". */
|
* then olm_utility_last_error() will be "BAD_MESSAGE_MAC". */
|
||||||
size_t olm_ed25519_verify(
|
OLM_EXPORT size_t olm_ed25519_verify(
|
||||||
OlmUtility * utility,
|
OlmUtility * utility,
|
||||||
void const * key, size_t key_length,
|
void const * key, size_t key_length,
|
||||||
void const * message, size_t message_length,
|
void const * message, size_t message_length,
|
||||||
|
|
42
include/olm/olm_export.h
Normal file
42
include/olm/olm_export.h
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
|
||||||
|
#ifndef OLM_EXPORT_H
|
||||||
|
#define OLM_EXPORT_H
|
||||||
|
|
||||||
|
#ifdef OLM_STATIC_DEFINE
|
||||||
|
# define OLM_EXPORT
|
||||||
|
# define OLM_NO_EXPORT
|
||||||
|
#else
|
||||||
|
# ifndef OLM_EXPORT
|
||||||
|
# ifdef olm_EXPORTS
|
||||||
|
/* We are building this library */
|
||||||
|
# define OLM_EXPORT
|
||||||
|
# else
|
||||||
|
/* We are using this library */
|
||||||
|
# define OLM_EXPORT
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifndef OLM_NO_EXPORT
|
||||||
|
# define OLM_NO_EXPORT
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OLM_DEPRECATED
|
||||||
|
# define OLM_DEPRECATED __attribute__ ((__deprecated__))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OLM_DEPRECATED_EXPORT
|
||||||
|
# define OLM_DEPRECATED_EXPORT OLM_EXPORT OLM_DEPRECATED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OLM_DEPRECATED_NO_EXPORT
|
||||||
|
# define OLM_DEPRECATED_NO_EXPORT OLM_NO_EXPORT OLM_DEPRECATED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0 /* DEFINE_NO_DEPRECATED */
|
||||||
|
# ifndef OLM_NO_DEPRECATED
|
||||||
|
# define OLM_NO_DEPRECATED
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* OLM_EXPORT_H */
|
|
@ -18,6 +18,10 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "olm/error.h"
|
||||||
|
|
||||||
|
#include "olm/olm_export.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -25,31 +29,38 @@ extern "C" {
|
||||||
typedef struct OlmOutboundGroupSession OlmOutboundGroupSession;
|
typedef struct OlmOutboundGroupSession OlmOutboundGroupSession;
|
||||||
|
|
||||||
/** get the size of an outbound group session, in bytes. */
|
/** get the size of an outbound group session, in bytes. */
|
||||||
size_t olm_outbound_group_session_size(void);
|
OLM_EXPORT size_t olm_outbound_group_session_size(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise an outbound group session object using the supplied memory
|
* Initialise an outbound group session object using the supplied memory
|
||||||
* The supplied memory should be at least olm_outbound_group_session_size()
|
* The supplied memory should be at least olm_outbound_group_session_size()
|
||||||
* bytes.
|
* bytes.
|
||||||
*/
|
*/
|
||||||
OlmOutboundGroupSession * olm_outbound_group_session(
|
OLM_EXPORT OlmOutboundGroupSession * olm_outbound_group_session(
|
||||||
void *memory
|
void *memory
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A null terminated string describing the most recent error to happen to a
|
* A null terminated string describing the most recent error to happen to a
|
||||||
* group session */
|
* group session */
|
||||||
const char *olm_outbound_group_session_last_error(
|
OLM_EXPORT const char *olm_outbound_group_session_last_error(
|
||||||
|
const OlmOutboundGroupSession *session
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An error code describing the most recent error to happen to a group
|
||||||
|
* session */
|
||||||
|
OLM_EXPORT enum OlmErrorCode olm_outbound_group_session_last_error_code(
|
||||||
const OlmOutboundGroupSession *session
|
const OlmOutboundGroupSession *session
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Clears the memory used to back this group session */
|
/** Clears the memory used to back this group session */
|
||||||
size_t olm_clear_outbound_group_session(
|
OLM_EXPORT size_t olm_clear_outbound_group_session(
|
||||||
OlmOutboundGroupSession *session
|
OlmOutboundGroupSession *session
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Returns the number of bytes needed to store an outbound group session */
|
/** Returns the number of bytes needed to store an outbound group session */
|
||||||
size_t olm_pickle_outbound_group_session_length(
|
OLM_EXPORT size_t olm_pickle_outbound_group_session_length(
|
||||||
const OlmOutboundGroupSession *session
|
const OlmOutboundGroupSession *session
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -61,7 +72,7 @@ size_t olm_pickle_outbound_group_session_length(
|
||||||
* is smaller than olm_pickle_outbound_group_session_length() then
|
* is smaller than olm_pickle_outbound_group_session_length() then
|
||||||
* olm_outbound_group_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL"
|
* olm_outbound_group_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL"
|
||||||
*/
|
*/
|
||||||
size_t olm_pickle_outbound_group_session(
|
OLM_EXPORT size_t olm_pickle_outbound_group_session(
|
||||||
OlmOutboundGroupSession *session,
|
OlmOutboundGroupSession *session,
|
||||||
void const * key, size_t key_length,
|
void const * key, size_t key_length,
|
||||||
void * pickled, size_t pickled_length
|
void * pickled, size_t pickled_length
|
||||||
|
@ -77,7 +88,7 @@ size_t olm_pickle_outbound_group_session(
|
||||||
* olm_outbound_group_session_last_error() will be "INVALID_BASE64". The input
|
* olm_outbound_group_session_last_error() will be "INVALID_BASE64". The input
|
||||||
* pickled buffer is destroyed
|
* pickled buffer is destroyed
|
||||||
*/
|
*/
|
||||||
size_t olm_unpickle_outbound_group_session(
|
OLM_EXPORT size_t olm_unpickle_outbound_group_session(
|
||||||
OlmOutboundGroupSession *session,
|
OlmOutboundGroupSession *session,
|
||||||
void const * key, size_t key_length,
|
void const * key, size_t key_length,
|
||||||
void * pickled, size_t pickled_length
|
void * pickled, size_t pickled_length
|
||||||
|
@ -85,7 +96,7 @@ size_t olm_unpickle_outbound_group_session(
|
||||||
|
|
||||||
|
|
||||||
/** The number of random bytes needed to create an outbound group session */
|
/** The number of random bytes needed to create an outbound group session */
|
||||||
size_t olm_init_outbound_group_session_random_length(
|
OLM_EXPORT size_t olm_init_outbound_group_session_random_length(
|
||||||
const OlmOutboundGroupSession *session
|
const OlmOutboundGroupSession *session
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -94,7 +105,7 @@ size_t olm_init_outbound_group_session_random_length(
|
||||||
* failure last_error will be set with an error code. The last_error will be
|
* failure last_error will be set with an error code. The last_error will be
|
||||||
* NOT_ENOUGH_RANDOM if the number of random bytes was too small.
|
* NOT_ENOUGH_RANDOM if the number of random bytes was too small.
|
||||||
*/
|
*/
|
||||||
size_t olm_init_outbound_group_session(
|
OLM_EXPORT size_t olm_init_outbound_group_session(
|
||||||
OlmOutboundGroupSession *session,
|
OlmOutboundGroupSession *session,
|
||||||
uint8_t *random, size_t random_length
|
uint8_t *random, size_t random_length
|
||||||
);
|
);
|
||||||
|
@ -102,7 +113,7 @@ size_t olm_init_outbound_group_session(
|
||||||
/**
|
/**
|
||||||
* The number of bytes that will be created by encrypting a message
|
* The number of bytes that will be created by encrypting a message
|
||||||
*/
|
*/
|
||||||
size_t olm_group_encrypt_message_length(
|
OLM_EXPORT size_t olm_group_encrypt_message_length(
|
||||||
OlmOutboundGroupSession *session,
|
OlmOutboundGroupSession *session,
|
||||||
size_t plaintext_length
|
size_t plaintext_length
|
||||||
);
|
);
|
||||||
|
@ -113,7 +124,7 @@ size_t olm_group_encrypt_message_length(
|
||||||
* error code. The last_error will be OUTPUT_BUFFER_TOO_SMALL if the output
|
* error code. The last_error will be OUTPUT_BUFFER_TOO_SMALL if the output
|
||||||
* buffer is too small.
|
* buffer is too small.
|
||||||
*/
|
*/
|
||||||
size_t olm_group_encrypt(
|
OLM_EXPORT size_t olm_group_encrypt(
|
||||||
OlmOutboundGroupSession *session,
|
OlmOutboundGroupSession *session,
|
||||||
uint8_t const * plaintext, size_t plaintext_length,
|
uint8_t const * plaintext, size_t plaintext_length,
|
||||||
uint8_t * message, size_t message_length
|
uint8_t * message, size_t message_length
|
||||||
|
@ -123,7 +134,7 @@ size_t olm_group_encrypt(
|
||||||
/**
|
/**
|
||||||
* Get the number of bytes returned by olm_outbound_group_session_id()
|
* Get the number of bytes returned by olm_outbound_group_session_id()
|
||||||
*/
|
*/
|
||||||
size_t olm_outbound_group_session_id_length(
|
OLM_EXPORT size_t olm_outbound_group_session_id_length(
|
||||||
const OlmOutboundGroupSession *session
|
const OlmOutboundGroupSession *session
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -135,7 +146,7 @@ size_t olm_outbound_group_session_id_length(
|
||||||
* last_error will be OUTPUT_BUFFER_TOO_SMALL if the id buffer was too
|
* last_error will be OUTPUT_BUFFER_TOO_SMALL if the id buffer was too
|
||||||
* small.
|
* small.
|
||||||
*/
|
*/
|
||||||
size_t olm_outbound_group_session_id(
|
OLM_EXPORT size_t olm_outbound_group_session_id(
|
||||||
OlmOutboundGroupSession *session,
|
OlmOutboundGroupSession *session,
|
||||||
uint8_t * id, size_t id_length
|
uint8_t * id, size_t id_length
|
||||||
);
|
);
|
||||||
|
@ -146,14 +157,14 @@ size_t olm_outbound_group_session_id(
|
||||||
* Each message is sent with an increasing index; this returns the index for
|
* Each message is sent with an increasing index; this returns the index for
|
||||||
* the next message.
|
* the next message.
|
||||||
*/
|
*/
|
||||||
uint32_t olm_outbound_group_session_message_index(
|
OLM_EXPORT uint32_t olm_outbound_group_session_message_index(
|
||||||
OlmOutboundGroupSession *session
|
OlmOutboundGroupSession *session
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the number of bytes returned by olm_outbound_group_session_key()
|
* Get the number of bytes returned by olm_outbound_group_session_key()
|
||||||
*/
|
*/
|
||||||
size_t olm_outbound_group_session_key_length(
|
OLM_EXPORT size_t olm_outbound_group_session_key_length(
|
||||||
const OlmOutboundGroupSession *session
|
const OlmOutboundGroupSession *session
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -167,7 +178,7 @@ size_t olm_outbound_group_session_key_length(
|
||||||
* failure. On failure last_error will be set with an error code. The
|
* failure. On failure last_error will be set with an error code. The
|
||||||
* last_error will be OUTPUT_BUFFER_TOO_SMALL if the buffer was too small.
|
* last_error will be OUTPUT_BUFFER_TOO_SMALL if the buffer was too small.
|
||||||
*/
|
*/
|
||||||
size_t olm_outbound_group_session_key(
|
OLM_EXPORT size_t olm_outbound_group_session_key(
|
||||||
OlmOutboundGroupSession *session,
|
OlmOutboundGroupSession *session,
|
||||||
uint8_t * key, size_t key_length
|
uint8_t * key, size_t key_length
|
||||||
);
|
);
|
||||||
|
|
|
@ -15,8 +15,25 @@
|
||||||
#ifndef OLM_PICKLE_H_
|
#ifndef OLM_PICKLE_H_
|
||||||
#define OLM_PICKLE_H_
|
#define OLM_PICKLE_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* Convenience macro for checking the return value of internal unpickling
|
||||||
|
* functions and returning early on failure. */
|
||||||
|
#ifndef UNPICKLE_OK
|
||||||
|
#define UNPICKLE_OK(x) do { if (!(x)) return NULL; } while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Convenience macro for failing on corrupted pickles from public
|
||||||
|
* API unpickling functions. */
|
||||||
|
#define FAIL_ON_CORRUPTED_PICKLE(pos, session) \
|
||||||
|
do { \
|
||||||
|
if (!pos) { \
|
||||||
|
session->last_error = OLM_CORRUPTED_PICKLE; \
|
||||||
|
return (size_t)-1; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -59,7 +76,7 @@ uint8_t * _olm_pickle_ed25519_public_key(
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Unpickle the ed25519 public key. Returns a pointer to the next item in the
|
/** Unpickle the ed25519 public key. Returns a pointer to the next item in the
|
||||||
* buffer. */
|
* buffer on success, NULL on error. */
|
||||||
const uint8_t * _olm_unpickle_ed25519_public_key(
|
const uint8_t * _olm_unpickle_ed25519_public_key(
|
||||||
const uint8_t *pos, const uint8_t *end,
|
const uint8_t *pos, const uint8_t *end,
|
||||||
struct _olm_ed25519_public_key * value
|
struct _olm_ed25519_public_key * value
|
||||||
|
@ -77,7 +94,7 @@ uint8_t * _olm_pickle_ed25519_key_pair(
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Unpickle the ed25519 key pair. Returns a pointer to the next item in the
|
/** Unpickle the ed25519 key pair. Returns a pointer to the next item in the
|
||||||
* buffer. */
|
* buffer on success, NULL on error. */
|
||||||
const uint8_t * _olm_unpickle_ed25519_key_pair(
|
const uint8_t * _olm_unpickle_ed25519_key_pair(
|
||||||
const uint8_t *pos, const uint8_t *end,
|
const uint8_t *pos, const uint8_t *end,
|
||||||
struct _olm_ed25519_key_pair * value
|
struct _olm_ed25519_key_pair * value
|
||||||
|
|
|
@ -21,6 +21,12 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
/* Convenience macro for checking the return value of internal unpickling
|
||||||
|
* functions and returning early on failure. */
|
||||||
|
#ifndef UNPICKLE_OK
|
||||||
|
#define UNPICKLE_OK(x) do { if (!(x)) return nullptr; } while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace olm {
|
namespace olm {
|
||||||
|
|
||||||
inline std::size_t pickle_length(
|
inline std::size_t pickle_length(
|
||||||
|
@ -40,6 +46,23 @@ std::uint8_t const * unpickle(
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
inline std::size_t pickle_length(
|
||||||
|
const std::uint8_t & value
|
||||||
|
) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint8_t * pickle(
|
||||||
|
std::uint8_t * pos,
|
||||||
|
std::uint8_t value
|
||||||
|
);
|
||||||
|
|
||||||
|
std::uint8_t const * unpickle(
|
||||||
|
std::uint8_t const * pos, std::uint8_t const * end,
|
||||||
|
std::uint8_t & value
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
inline std::size_t pickle_length(
|
inline std::size_t pickle_length(
|
||||||
const bool & value
|
const bool & value
|
||||||
) {
|
) {
|
||||||
|
@ -88,11 +111,21 @@ std::uint8_t const * unpickle(
|
||||||
olm::List<T, max_size> & list
|
olm::List<T, max_size> & list
|
||||||
) {
|
) {
|
||||||
std::uint32_t size;
|
std::uint32_t size;
|
||||||
|
|
||||||
pos = unpickle(pos, end, size);
|
pos = unpickle(pos, end, size);
|
||||||
|
if (!pos) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
while (size-- && pos != end) {
|
while (size-- && pos != end) {
|
||||||
T * value = list.insert(list.end());
|
T * value = list.insert(list.end());
|
||||||
pos = unpickle(pos, end, *value);
|
pos = unpickle(pos, end, *value);
|
||||||
|
|
||||||
|
if (!pos) {
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,10 @@
|
||||||
|
|
||||||
#include "olm/error.h"
|
#include "olm/error.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
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -31,7 +35,7 @@ extern "C" {
|
||||||
/**
|
/**
|
||||||
* Get the number of bytes needed to encode a pickle of the length given
|
* Get the number of bytes needed to encode a pickle of the length given
|
||||||
*/
|
*/
|
||||||
size_t _olm_enc_output_length(size_t raw_length);
|
OLM_EXPORT size_t _olm_enc_output_length(size_t raw_length);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the point in the output buffer that the raw pickle should be written to.
|
* Get the point in the output buffer that the raw pickle should be written to.
|
||||||
|
@ -41,7 +45,7 @@ size_t _olm_enc_output_length(size_t raw_length);
|
||||||
* base-64 encoding would otherwise overwrite the end of the input before it
|
* base-64 encoding would otherwise overwrite the end of the input before it
|
||||||
* was encoded.)
|
* was encoded.)
|
||||||
*/
|
*/
|
||||||
uint8_t *_olm_enc_output_pos(uint8_t * output, size_t raw_length);
|
OLM_EXPORT uint8_t *_olm_enc_output_pos(uint8_t * output, size_t raw_length);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encrypt and encode the given pickle in-situ.
|
* Encrypt and encode the given pickle in-situ.
|
||||||
|
@ -51,7 +55,7 @@ size_t _olm_enc_output_length(size_t raw_length);
|
||||||
*
|
*
|
||||||
* Returns the number of bytes in the encoded pickle.
|
* Returns the number of bytes in the encoded pickle.
|
||||||
*/
|
*/
|
||||||
size_t _olm_enc_output(
|
OLM_EXPORT size_t _olm_enc_output(
|
||||||
uint8_t const * key, size_t key_length,
|
uint8_t const * key, size_t key_length,
|
||||||
uint8_t *pickle, size_t raw_length
|
uint8_t *pickle, size_t raw_length
|
||||||
);
|
);
|
||||||
|
@ -62,7 +66,7 @@ size_t _olm_enc_output(
|
||||||
* Returns the number of bytes in the decoded pickle, or olm_error() on error,
|
* Returns the number of bytes in the decoded pickle, or olm_error() on error,
|
||||||
* in which case *last_error will be updated, if last_error is non-NULL.
|
* in which case *last_error will be updated, if last_error is non-NULL.
|
||||||
*/
|
*/
|
||||||
size_t _olm_enc_input(
|
OLM_EXPORT size_t _olm_enc_input(
|
||||||
uint8_t const * key, size_t key_length,
|
uint8_t const * key, size_t key_length,
|
||||||
uint8_t * input, size_t b64_length,
|
uint8_t * input, size_t b64_length,
|
||||||
enum OlmErrorCode * last_error
|
enum OlmErrorCode * last_error
|
||||||
|
|
136
include/olm/pk.h
136
include/olm/pk.h
|
@ -19,6 +19,10 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "olm/error.h"
|
||||||
|
|
||||||
|
#include "olm/olm_export.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -26,49 +30,55 @@ extern "C" {
|
||||||
typedef struct OlmPkEncryption OlmPkEncryption;
|
typedef struct OlmPkEncryption OlmPkEncryption;
|
||||||
|
|
||||||
/* The size of an encryption object in bytes */
|
/* The size of an encryption object in bytes */
|
||||||
size_t olm_pk_encryption_size(void);
|
OLM_EXPORT size_t olm_pk_encryption_size(void);
|
||||||
|
|
||||||
/** Initialise an encryption object using the supplied memory
|
/** Initialise an encryption object using the supplied memory
|
||||||
* The supplied memory must be at least olm_pk_encryption_size() bytes */
|
* The supplied memory must be at least olm_pk_encryption_size() bytes */
|
||||||
OlmPkEncryption *olm_pk_encryption(
|
OLM_EXPORT OlmPkEncryption *olm_pk_encryption(
|
||||||
void * memory
|
void * memory
|
||||||
);
|
);
|
||||||
|
|
||||||
/** A null terminated string describing the most recent error to happen to an
|
/** A null terminated string describing the most recent error to happen to an
|
||||||
* encryption object */
|
* encryption object */
|
||||||
const char * olm_pk_encryption_last_error(
|
OLM_EXPORT const char * olm_pk_encryption_last_error(
|
||||||
OlmPkEncryption * encryption
|
const OlmPkEncryption * encryption
|
||||||
|
);
|
||||||
|
|
||||||
|
/** An error code describing the most recent error to happen to an encryption
|
||||||
|
* object */
|
||||||
|
OLM_EXPORT enum OlmErrorCode olm_pk_encryption_last_error_code(
|
||||||
|
const OlmPkEncryption * encryption
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Clears the memory used to back this encryption object */
|
/** Clears the memory used to back this encryption object */
|
||||||
size_t olm_clear_pk_encryption(
|
OLM_EXPORT size_t olm_clear_pk_encryption(
|
||||||
OlmPkEncryption *encryption
|
OlmPkEncryption *encryption
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Set the recipient's public key for encrypting to */
|
/** Set the recipient's public key for encrypting to */
|
||||||
size_t olm_pk_encryption_set_recipient_key(
|
OLM_EXPORT size_t olm_pk_encryption_set_recipient_key(
|
||||||
OlmPkEncryption *encryption,
|
OlmPkEncryption *encryption,
|
||||||
void const *public_key, size_t public_key_length
|
void const *public_key, size_t public_key_length
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Get the length of the ciphertext that will correspond to a plaintext of the
|
/** Get the length of the ciphertext that will correspond to a plaintext of the
|
||||||
* given length. */
|
* given length. */
|
||||||
size_t olm_pk_ciphertext_length(
|
OLM_EXPORT size_t olm_pk_ciphertext_length(
|
||||||
OlmPkEncryption *encryption,
|
const OlmPkEncryption *encryption,
|
||||||
size_t plaintext_length
|
size_t plaintext_length
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Get the length of the message authentication code. */
|
/** Get the length of the message authentication code. */
|
||||||
size_t olm_pk_mac_length(
|
OLM_EXPORT size_t olm_pk_mac_length(
|
||||||
OlmPkEncryption *encryption
|
const OlmPkEncryption *encryption
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Get the length of a public or ephemeral key */
|
/** Get the length of a public or ephemeral key */
|
||||||
size_t olm_pk_key_length(void);
|
OLM_EXPORT size_t olm_pk_key_length(void);
|
||||||
|
|
||||||
/** The number of random bytes needed to encrypt a message. */
|
/** The number of random bytes needed to encrypt a message. */
|
||||||
size_t olm_pk_encrypt_random_length(
|
OLM_EXPORT size_t olm_pk_encrypt_random_length(
|
||||||
OlmPkEncryption *encryption
|
const OlmPkEncryption *encryption
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Encrypt a plaintext for the recipient set using
|
/** Encrypt a plaintext for the recipient set using
|
||||||
|
@ -81,44 +91,50 @@ size_t olm_pk_encrypt_random_length(
|
||||||
* ephemeral_key buffers were too small then olm_pk_encryption_last_error()
|
* ephemeral_key buffers were too small then olm_pk_encryption_last_error()
|
||||||
* will be "OUTPUT_BUFFER_TOO_SMALL". If there weren't enough random bytes then
|
* will be "OUTPUT_BUFFER_TOO_SMALL". If there weren't enough random bytes then
|
||||||
* olm_pk_encryption_last_error() will be "OLM_INPUT_BUFFER_TOO_SMALL". */
|
* olm_pk_encryption_last_error() will be "OLM_INPUT_BUFFER_TOO_SMALL". */
|
||||||
size_t olm_pk_encrypt(
|
OLM_EXPORT size_t olm_pk_encrypt(
|
||||||
OlmPkEncryption *encryption,
|
OlmPkEncryption *encryption,
|
||||||
void const * plaintext, size_t plaintext_length,
|
void const * plaintext, size_t plaintext_length,
|
||||||
void * ciphertext, size_t ciphertext_length,
|
void * ciphertext, size_t ciphertext_length,
|
||||||
void * mac, size_t mac_length,
|
void * mac, size_t mac_length,
|
||||||
void * ephemeral_key, size_t ephemeral_key_size,
|
void * ephemeral_key, size_t ephemeral_key_size,
|
||||||
void * random, size_t random_length
|
const void * random, size_t random_length
|
||||||
);
|
);
|
||||||
|
|
||||||
typedef struct OlmPkDecryption OlmPkDecryption;
|
typedef struct OlmPkDecryption OlmPkDecryption;
|
||||||
|
|
||||||
/* The size of a decryption object in bytes */
|
/* The size of a decryption object in bytes */
|
||||||
size_t olm_pk_decryption_size(void);
|
OLM_EXPORT size_t olm_pk_decryption_size(void);
|
||||||
|
|
||||||
/** Initialise a decryption object using the supplied memory
|
/** Initialise a decryption object using the supplied memory
|
||||||
* The supplied memory must be at least olm_pk_decryption_size() bytes */
|
* The supplied memory must be at least olm_pk_decryption_size() bytes */
|
||||||
OlmPkDecryption *olm_pk_decryption(
|
OLM_EXPORT OlmPkDecryption *olm_pk_decryption(
|
||||||
void * memory
|
void * memory
|
||||||
);
|
);
|
||||||
|
|
||||||
/** A null terminated string describing the most recent error to happen to a
|
/** A null terminated string describing the most recent error to happen to a
|
||||||
* decription object */
|
* decription object */
|
||||||
const char * olm_pk_decryption_last_error(
|
OLM_EXPORT const char * olm_pk_decryption_last_error(
|
||||||
OlmPkDecryption * decryption
|
const OlmPkDecryption * decryption
|
||||||
|
);
|
||||||
|
|
||||||
|
/** An error code describing the most recent error to happen to a decription
|
||||||
|
* object */
|
||||||
|
OLM_EXPORT enum OlmErrorCode olm_pk_decryption_last_error_code(
|
||||||
|
const OlmPkDecryption * decryption
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Clears the memory used to back this decryption object */
|
/** Clears the memory used to back this decryption object */
|
||||||
size_t olm_clear_pk_decryption(
|
OLM_EXPORT size_t olm_clear_pk_decryption(
|
||||||
OlmPkDecryption *decryption
|
OlmPkDecryption *decryption
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Get the number of bytes required to store an olm private key
|
/** Get the number of bytes required to store an olm private key
|
||||||
*/
|
*/
|
||||||
size_t olm_pk_private_key_length(void);
|
OLM_EXPORT size_t olm_pk_private_key_length(void);
|
||||||
|
|
||||||
/** DEPRECATED: Use olm_pk_private_key_length()
|
/** DEPRECATED: Use olm_pk_private_key_length()
|
||||||
*/
|
*/
|
||||||
size_t olm_pk_generate_key_random_length(void);
|
OLM_EXPORT size_t olm_pk_generate_key_random_length(void);
|
||||||
|
|
||||||
/** Initialise the key from the private part of a key as returned by
|
/** Initialise the key from the private part of a key as returned by
|
||||||
* olm_pk_get_private_key(). The associated public key will be written to the
|
* olm_pk_get_private_key(). The associated public key will be written to the
|
||||||
|
@ -130,31 +146,31 @@ size_t olm_pk_generate_key_random_length(void);
|
||||||
* Note that the pubkey is a base64 encoded string, but the private key is
|
* Note that the pubkey is a base64 encoded string, but the private key is
|
||||||
* an unencoded byte array
|
* an unencoded byte array
|
||||||
*/
|
*/
|
||||||
size_t olm_pk_key_from_private(
|
OLM_EXPORT size_t olm_pk_key_from_private(
|
||||||
OlmPkDecryption * decryption,
|
OlmPkDecryption * decryption,
|
||||||
void * pubkey, size_t pubkey_length,
|
void * pubkey, size_t pubkey_length,
|
||||||
void * privkey, size_t privkey_length
|
const void * privkey, size_t privkey_length
|
||||||
);
|
);
|
||||||
|
|
||||||
/** DEPRECATED: Use olm_pk_key_from_private
|
/** DEPRECATED: Use olm_pk_key_from_private
|
||||||
*/
|
*/
|
||||||
size_t olm_pk_generate_key(
|
OLM_EXPORT size_t olm_pk_generate_key(
|
||||||
OlmPkDecryption * decryption,
|
OlmPkDecryption * decryption,
|
||||||
void * pubkey, size_t pubkey_length,
|
void * pubkey, size_t pubkey_length,
|
||||||
void * privkey, size_t privkey_length
|
const void * privkey, size_t privkey_length
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Returns the number of bytes needed to store a decryption object. */
|
/** Returns the number of bytes needed to store a decryption object. */
|
||||||
size_t olm_pickle_pk_decryption_length(
|
OLM_EXPORT size_t olm_pickle_pk_decryption_length(
|
||||||
OlmPkDecryption * decryption
|
const OlmPkDecryption * decryption
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Stores decryption object as a base64 string. Encrypts the object using the
|
/** Stores decryption object as a base64 string. Encrypts the object using the
|
||||||
* supplied key. Returns the length of the pickled object on success.
|
* supplied key. Returns the length of the pickled object on success.
|
||||||
* Returns olm_error() on failure. If the pickle output buffer
|
* Returns olm_error() on failure. If the pickle output buffer
|
||||||
* is smaller than olm_pickle_account_length() then
|
* is smaller than olm_pickle_pk_decryption_length() then
|
||||||
* olm_pk_decryption_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */
|
* olm_pk_decryption_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */
|
||||||
size_t olm_pickle_pk_decryption(
|
OLM_EXPORT size_t olm_pickle_pk_decryption(
|
||||||
OlmPkDecryption * decryption,
|
OlmPkDecryption * decryption,
|
||||||
void const * key, size_t key_length,
|
void const * key, size_t key_length,
|
||||||
void *pickled, size_t pickled_length
|
void *pickled, size_t pickled_length
|
||||||
|
@ -167,7 +183,7 @@ size_t olm_pickle_pk_decryption(
|
||||||
* will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then
|
* will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then
|
||||||
* olm_pk_decryption_last_error() will be "INVALID_BASE64". The input pickled
|
* olm_pk_decryption_last_error() will be "INVALID_BASE64". The input pickled
|
||||||
* buffer is destroyed */
|
* buffer is destroyed */
|
||||||
size_t olm_unpickle_pk_decryption(
|
OLM_EXPORT size_t olm_unpickle_pk_decryption(
|
||||||
OlmPkDecryption * decryption,
|
OlmPkDecryption * decryption,
|
||||||
void const * key, size_t key_length,
|
void const * key, size_t key_length,
|
||||||
void *pickled, size_t pickled_length,
|
void *pickled, size_t pickled_length,
|
||||||
|
@ -176,8 +192,8 @@ size_t olm_unpickle_pk_decryption(
|
||||||
|
|
||||||
/** Get the length of the plaintext that will correspond to a ciphertext of the
|
/** Get the length of the plaintext that will correspond to a ciphertext of the
|
||||||
* given length. */
|
* given length. */
|
||||||
size_t olm_pk_max_plaintext_length(
|
OLM_EXPORT size_t olm_pk_max_plaintext_length(
|
||||||
OlmPkDecryption * decryption,
|
const OlmPkDecryption * decryption,
|
||||||
size_t ciphertext_length
|
size_t ciphertext_length
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -186,7 +202,7 @@ size_t olm_pk_max_plaintext_length(
|
||||||
* arguments. Returns the length of the plaintext on success. Returns
|
* arguments. Returns the length of the plaintext on success. Returns
|
||||||
* olm_error() on failure. If the plaintext buffer is too small then
|
* olm_error() on failure. If the plaintext buffer is too small then
|
||||||
* olm_pk_encryption_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". */
|
* olm_pk_encryption_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". */
|
||||||
size_t olm_pk_decrypt(
|
OLM_EXPORT size_t olm_pk_decrypt(
|
||||||
OlmPkDecryption * decryption,
|
OlmPkDecryption * decryption,
|
||||||
void const * ephemeral_key, size_t ephemeral_key_length,
|
void const * ephemeral_key, size_t ephemeral_key_length,
|
||||||
void const * mac, size_t mac_length,
|
void const * mac, size_t mac_length,
|
||||||
|
@ -202,7 +218,7 @@ size_t olm_pk_decrypt(
|
||||||
* and olm_pk_encryption_last_error() will be "OUTPUT_BUFFER_TOO_SMALL".
|
* and olm_pk_encryption_last_error() will be "OUTPUT_BUFFER_TOO_SMALL".
|
||||||
* Returns the number of bytes written.
|
* Returns the number of bytes written.
|
||||||
*/
|
*/
|
||||||
size_t olm_pk_get_private_key(
|
OLM_EXPORT size_t olm_pk_get_private_key(
|
||||||
OlmPkDecryption * decryption,
|
OlmPkDecryption * decryption,
|
||||||
void *private_key, size_t private_key_length
|
void *private_key, size_t private_key_length
|
||||||
);
|
);
|
||||||
|
@ -210,40 +226,66 @@ size_t olm_pk_get_private_key(
|
||||||
typedef struct OlmPkSigning OlmPkSigning;
|
typedef struct OlmPkSigning OlmPkSigning;
|
||||||
|
|
||||||
/* The size of a signing object in bytes */
|
/* The size of a signing object in bytes */
|
||||||
size_t olm_pk_signing_size(void);
|
OLM_EXPORT size_t olm_pk_signing_size(void);
|
||||||
|
|
||||||
/** Initialise a signing object using the supplied memory
|
/** Initialise a signing object using the supplied memory
|
||||||
* The supplied memory must be at least olm_pk_signing_size() bytes */
|
* The supplied memory must be at least olm_pk_signing_size() bytes */
|
||||||
OlmPkSigning *olm_pk_signing(
|
OLM_EXPORT OlmPkSigning *olm_pk_signing(
|
||||||
void * memory
|
void * memory
|
||||||
);
|
);
|
||||||
|
|
||||||
/** A null terminated string describing the most recent error to happen to a
|
/** A null terminated string describing the most recent error to happen to a
|
||||||
* signing object */
|
* signing object */
|
||||||
const char * olm_pk_signing_last_error(
|
OLM_EXPORT const char * olm_pk_signing_last_error(
|
||||||
OlmPkSigning * sign
|
const OlmPkSigning * sign
|
||||||
|
);
|
||||||
|
|
||||||
|
/** A null terminated string describing the most recent error to happen to a
|
||||||
|
* signing object */
|
||||||
|
OLM_EXPORT enum OlmErrorCode olm_pk_signing_last_error_code(
|
||||||
|
const OlmPkSigning * sign
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Clears the memory used to back this signing object */
|
/** Clears the memory used to back this signing object */
|
||||||
size_t olm_clear_pk_signing(
|
OLM_EXPORT size_t olm_clear_pk_signing(
|
||||||
OlmPkSigning *sign
|
OlmPkSigning *sign
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise the signing object with a public/private keypair from a seed
|
* Initialise the signing object with a public/private keypair from a seed. The
|
||||||
|
* associated public key will be written to the pubkey buffer. Returns
|
||||||
|
* olm_error() on failure. If the public key buffer is too small then
|
||||||
|
* olm_pk_signing_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". If the seed
|
||||||
|
* buffer is too small then olm_pk_signing_last_error() will be
|
||||||
|
* "INPUT_BUFFER_TOO_SMALL".
|
||||||
*/
|
*/
|
||||||
size_t olm_pk_signing_key_from_seed(
|
OLM_EXPORT size_t olm_pk_signing_key_from_seed(
|
||||||
OlmPkSigning * sign,
|
OlmPkSigning * sign,
|
||||||
void * pubkey, size_t pubkey_length,
|
void * pubkey, size_t pubkey_length,
|
||||||
void * seed, size_t seed_length
|
const void * seed, size_t seed_length
|
||||||
);
|
);
|
||||||
|
|
||||||
size_t olm_pk_signing_seed_length(void);
|
/**
|
||||||
size_t olm_pk_signing_public_key_length(void);
|
* The size required for the seed for initialising a signing object.
|
||||||
|
*/
|
||||||
|
OLM_EXPORT size_t olm_pk_signing_seed_length(void);
|
||||||
|
|
||||||
size_t olm_pk_signature_length();
|
/**
|
||||||
|
* The size of the public key of a signing object.
|
||||||
|
*/
|
||||||
|
OLM_EXPORT size_t olm_pk_signing_public_key_length(void);
|
||||||
|
|
||||||
size_t olm_pk_sign(
|
/**
|
||||||
|
* The size of a signature created by a signing object.
|
||||||
|
*/
|
||||||
|
OLM_EXPORT size_t olm_pk_signature_length(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign a message. The signature will be written to the signature
|
||||||
|
* buffer. Returns olm_error() on failure. If the signature buffer is too
|
||||||
|
* small, olm_pk_signing_last_error() will be "OUTPUT_BUFFER_TOO_SMALL".
|
||||||
|
*/
|
||||||
|
OLM_EXPORT size_t olm_pk_sign(
|
||||||
OlmPkSigning *sign,
|
OlmPkSigning *sign,
|
||||||
uint8_t const * message, size_t message_length,
|
uint8_t const * message, size_t message_length,
|
||||||
uint8_t * signature, size_t signature_length
|
uint8_t * signature, size_t signature_length
|
||||||
|
|
|
@ -19,6 +19,10 @@
|
||||||
#include "olm/list.hh"
|
#include "olm/list.hh"
|
||||||
#include "olm/error.h"
|
#include "olm/error.h"
|
||||||
|
|
||||||
|
// Note: exports in this file are only for unit tests. Nobody else should be
|
||||||
|
// using this externally
|
||||||
|
#include "olm/olm_export.h"
|
||||||
|
|
||||||
struct _olm_cipher;
|
struct _olm_cipher;
|
||||||
|
|
||||||
namespace olm {
|
namespace olm {
|
||||||
|
@ -72,7 +76,7 @@ struct KdfInfo {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct Ratchet {
|
struct OLM_EXPORT Ratchet {
|
||||||
|
|
||||||
Ratchet(
|
Ratchet(
|
||||||
KdfInfo const & kdf_info,
|
KdfInfo const & kdf_info,
|
||||||
|
@ -94,7 +98,7 @@ struct Ratchet {
|
||||||
|
|
||||||
/** The sender chain is used to send messages. Each time a new ephemeral
|
/** The sender chain is used to send messages. Each time a new ephemeral
|
||||||
* key is received from the remote server we generate a new sender chain
|
* key is received from the remote server we generate a new sender chain
|
||||||
* with a new empheral key when we next send a message. */
|
* with a new ephemeral key when we next send a message. */
|
||||||
List<SenderChain, 1> sender_chain;
|
List<SenderChain, 1> sender_chain;
|
||||||
|
|
||||||
/** The receiver chain is used to decrypt received messages. We store the
|
/** The receiver chain is used to decrypt received messages. We store the
|
||||||
|
@ -124,12 +128,12 @@ struct Ratchet {
|
||||||
* a given message length. */
|
* a given message length. */
|
||||||
std::size_t encrypt_output_length(
|
std::size_t encrypt_output_length(
|
||||||
std::size_t plaintext_length
|
std::size_t plaintext_length
|
||||||
);
|
) const;
|
||||||
|
|
||||||
/** The number of bytes of random data the encrypt method will need to
|
/** The number of bytes of random data the encrypt method will need to
|
||||||
* encrypt a message. This will be 32 bytes if the session needs to
|
* encrypt a message. This will be 32 bytes if the session needs to
|
||||||
* generate a new ephemeral key, or will be 0 bytes otherwise.*/
|
* generate a new ephemeral key, or will be 0 bytes otherwise.*/
|
||||||
std::size_t encrypt_random_length();
|
std::size_t encrypt_random_length() const;
|
||||||
|
|
||||||
/** Encrypt some plain-text. Returns the length of the encrypted message
|
/** Encrypt some plain-text. Returns the length of the encrypted message
|
||||||
* or std::size_t(-1) on failure. On failure last_error will be set with
|
* or std::size_t(-1) on failure. On failure last_error will be set with
|
||||||
|
|
|
@ -19,6 +19,10 @@
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "olm/error.h"
|
||||||
|
|
||||||
|
#include "olm/olm_export.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -33,46 +37,53 @@ typedef struct OlmSAS OlmSAS;
|
||||||
|
|
||||||
/** A null terminated string describing the most recent error to happen to an
|
/** A null terminated string describing the most recent error to happen to an
|
||||||
* SAS object. */
|
* SAS object. */
|
||||||
const char * olm_sas_last_error(
|
OLM_EXPORT const char * olm_sas_last_error(
|
||||||
OlmSAS * sas
|
const OlmSAS * sas
|
||||||
|
);
|
||||||
|
|
||||||
|
/** An error code describing the most recent error to happen to an SAS
|
||||||
|
* object. */
|
||||||
|
OLM_EXPORT enum OlmErrorCode olm_sas_last_error_code(
|
||||||
|
const OlmSAS * sas
|
||||||
);
|
);
|
||||||
|
|
||||||
/** The size of an SAS object in bytes. */
|
/** The size of an SAS object in bytes. */
|
||||||
size_t olm_sas_size(void);
|
OLM_EXPORT size_t olm_sas_size(void);
|
||||||
|
|
||||||
/** Initialize an SAS object using the supplied memory.
|
/** Initialize an SAS object using the supplied memory.
|
||||||
* The supplied memory must be at least `olm_sas_size()` bytes. */
|
* The supplied memory must be at least `olm_sas_size()` bytes. */
|
||||||
OlmSAS * olm_sas(
|
OLM_EXPORT OlmSAS * olm_sas(
|
||||||
void * memory
|
void * memory
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Clears the memory used to back an SAS object. */
|
/** Clears the memory used to back an SAS object. */
|
||||||
size_t olm_clear_sas(
|
OLM_EXPORT size_t olm_clear_sas(
|
||||||
OlmSAS * sas
|
OlmSAS * sas
|
||||||
);
|
);
|
||||||
|
|
||||||
/** The number of random bytes needed to create an SAS object. */
|
/** The number of random bytes needed to create an SAS object. */
|
||||||
size_t olm_create_sas_random_length(
|
OLM_EXPORT size_t olm_create_sas_random_length(
|
||||||
OlmSAS * sas
|
const OlmSAS * sas
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Creates a new SAS object.
|
/** Creates a new SAS object.
|
||||||
*
|
*
|
||||||
* @param[in] sas the SAS object to create, initialized by `olm_sas()`.
|
* @param[in] sas the SAS object to create, initialized by `olm_sas()`.
|
||||||
* @param[in] random array of random bytes.
|
* @param[in] random array of random bytes. The contents of the buffer may be
|
||||||
|
* overwritten.
|
||||||
* @param[in] random_length the number of random bytes provided. Must be at
|
* @param[in] random_length the number of random bytes provided. Must be at
|
||||||
* least `olm_create_sas_random_length()`.
|
* least `olm_create_sas_random_length()`.
|
||||||
*
|
*
|
||||||
* @return `olm_error()` on failure. If there weren't enough random bytes then
|
* @return `olm_error()` on failure. If there weren't enough random bytes then
|
||||||
* `olm_sas_last_error()` will be `NOT_ENOUGH_RANDOM`.
|
* `olm_sas_last_error()` will be `NOT_ENOUGH_RANDOM`.
|
||||||
*/
|
*/
|
||||||
size_t olm_create_sas(
|
OLM_EXPORT size_t olm_create_sas(
|
||||||
OlmSAS * sas,
|
OlmSAS * sas,
|
||||||
void * random, size_t random_length
|
void * random, size_t random_length
|
||||||
);
|
);
|
||||||
|
|
||||||
/** The size of a public key in bytes. */
|
/** The size of a public key in bytes. */
|
||||||
size_t olm_sas_pubkey_length(OlmSAS * sas);
|
OLM_EXPORT size_t olm_sas_pubkey_length(const OlmSAS * sas);
|
||||||
|
|
||||||
/** Get the public key for the SAS object.
|
/** Get the public key for the SAS object.
|
||||||
*
|
*
|
||||||
|
@ -84,7 +95,7 @@ size_t olm_sas_pubkey_length(OlmSAS * sas);
|
||||||
* @return `olm_error()` on failure. If the `pubkey` buffer is too small, then
|
* @return `olm_error()` on failure. If the `pubkey` buffer is too small, then
|
||||||
* `olm_sas_last_error()` will be `OUTPUT_BUFFER_TOO_SMALL`.
|
* `olm_sas_last_error()` will be `OUTPUT_BUFFER_TOO_SMALL`.
|
||||||
*/
|
*/
|
||||||
size_t olm_sas_get_pubkey(
|
OLM_EXPORT size_t olm_sas_get_pubkey(
|
||||||
OlmSAS * sas,
|
OlmSAS * sas,
|
||||||
void * pubkey, size_t pubkey_length
|
void * pubkey, size_t pubkey_length
|
||||||
);
|
);
|
||||||
|
@ -92,17 +103,27 @@ size_t olm_sas_get_pubkey(
|
||||||
/** Sets the public key of other user.
|
/** Sets the public key of other user.
|
||||||
*
|
*
|
||||||
* @param[in] sas the SAS object.
|
* @param[in] sas the SAS object.
|
||||||
* @param[in] their_key the other user's public key.
|
* @param[in] their_key the other user's public key. The contents of the
|
||||||
|
* buffer will be overwritten.
|
||||||
* @param[in] their_key_length the size of the `their_key` buffer.
|
* @param[in] their_key_length the size of the `their_key` buffer.
|
||||||
*
|
*
|
||||||
* @return `olm_error()` on failure. If the `their_key` buffer is too small,
|
* @return `olm_error()` on failure. If the `their_key` buffer is too small,
|
||||||
* then `olm_sas_last_error()` will be `INPUT_BUFFER_TOO_SMALL`.
|
* then `olm_sas_last_error()` will be `INPUT_BUFFER_TOO_SMALL`.
|
||||||
*/
|
*/
|
||||||
size_t olm_sas_set_their_key(
|
OLM_EXPORT size_t olm_sas_set_their_key(
|
||||||
OlmSAS *sas,
|
OlmSAS *sas,
|
||||||
void * their_key, size_t their_key_length
|
void * their_key, size_t their_key_length
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/** Checks if their key was set.
|
||||||
|
*
|
||||||
|
* @param[in] sas the SAS object.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
OLM_EXPORT int olm_sas_is_their_key_set(
|
||||||
|
const OlmSAS *sas
|
||||||
|
);
|
||||||
|
|
||||||
/** Generate bytes to use for the short authentication string.
|
/** Generate bytes to use for the short authentication string.
|
||||||
*
|
*
|
||||||
* @param[in] sas the SAS object.
|
* @param[in] sas the SAS object.
|
||||||
|
@ -112,8 +133,11 @@ size_t olm_sas_set_their_key(
|
||||||
* @param[out] output the output buffer.
|
* @param[out] output the output buffer.
|
||||||
* @param[in] output_length the size of the output buffer. For hex-based SAS
|
* @param[in] output_length the size of the output buffer. For hex-based SAS
|
||||||
* as in the Matrix spec, this will be 5.
|
* as in the Matrix spec, this will be 5.
|
||||||
|
*
|
||||||
|
* @return `olm_error()` on failure. If their key wasn't set then
|
||||||
|
* `olm_sas_last_error()` will be `SAS_THEIR_KEY_NOT_SET`.
|
||||||
*/
|
*/
|
||||||
size_t olm_sas_generate_bytes(
|
OLM_EXPORT size_t olm_sas_generate_bytes(
|
||||||
OlmSAS * sas,
|
OlmSAS * sas,
|
||||||
const void * info, size_t info_length,
|
const void * info, size_t info_length,
|
||||||
void * output, size_t output_length
|
void * output, size_t output_length
|
||||||
|
@ -121,8 +145,8 @@ size_t olm_sas_generate_bytes(
|
||||||
|
|
||||||
/** The size of the message authentication code generated by
|
/** The size of the message authentication code generated by
|
||||||
* olm_sas_calculate_mac()`. */
|
* olm_sas_calculate_mac()`. */
|
||||||
size_t olm_sas_mac_length(
|
OLM_EXPORT size_t olm_sas_mac_length(
|
||||||
OlmSAS *sas
|
const OlmSAS *sas
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Generate a message authentication code (MAC) based on the shared secret.
|
/** Generate a message authentication code (MAC) based on the shared secret.
|
||||||
|
@ -140,17 +164,26 @@ size_t olm_sas_mac_length(
|
||||||
* @return `olm_error()` on failure. If the `mac` buffer is too small, then
|
* @return `olm_error()` on failure. If the `mac` buffer is too small, then
|
||||||
* `olm_sas_last_error()` will be `OUTPUT_BUFFER_TOO_SMALL`.
|
* `olm_sas_last_error()` will be `OUTPUT_BUFFER_TOO_SMALL`.
|
||||||
*/
|
*/
|
||||||
size_t olm_sas_calculate_mac(
|
OLM_EXPORT size_t olm_sas_calculate_mac(
|
||||||
OlmSAS * sas,
|
OlmSAS * sas,
|
||||||
void * input, size_t input_length,
|
const void * input, size_t input_length,
|
||||||
|
const void * info, size_t info_length,
|
||||||
|
void * mac, size_t mac_length
|
||||||
|
);
|
||||||
|
|
||||||
|
// A version of the calculate mac function that produces base64 strings that are
|
||||||
|
// compatible with other base64 implementations.
|
||||||
|
OLM_EXPORT size_t olm_sas_calculate_mac_fixed_base64(
|
||||||
|
OlmSAS * sas,
|
||||||
|
const void * input, size_t input_length,
|
||||||
const void * info, size_t info_length,
|
const void * info, size_t info_length,
|
||||||
void * mac, size_t mac_length
|
void * mac, size_t mac_length
|
||||||
);
|
);
|
||||||
|
|
||||||
// for compatibility with an old version of Riot
|
// for compatibility with an old version of Riot
|
||||||
size_t olm_sas_calculate_mac_long_kdf(
|
OLM_EXPORT size_t olm_sas_calculate_mac_long_kdf(
|
||||||
OlmSAS * sas,
|
OlmSAS * sas,
|
||||||
void * input, size_t input_length,
|
const void * input, size_t input_length,
|
||||||
const void * info, size_t info_length,
|
const void * info, size_t info_length,
|
||||||
void * mac, size_t mac_length
|
void * mac, size_t mac_length
|
||||||
);
|
);
|
||||||
|
|
|
@ -17,6 +17,10 @@
|
||||||
|
|
||||||
#include "olm/ratchet.hh"
|
#include "olm/ratchet.hh"
|
||||||
|
|
||||||
|
// Note: exports in this file are only for unit tests. Nobody else should be
|
||||||
|
// using this externally
|
||||||
|
#include "olm/olm_export.h"
|
||||||
|
|
||||||
namespace olm {
|
namespace olm {
|
||||||
|
|
||||||
struct Account;
|
struct Account;
|
||||||
|
@ -26,7 +30,7 @@ enum struct MessageType {
|
||||||
MESSAGE = 1,
|
MESSAGE = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Session {
|
struct OLM_EXPORT Session {
|
||||||
|
|
||||||
Session();
|
Session();
|
||||||
|
|
||||||
|
@ -41,7 +45,7 @@ struct Session {
|
||||||
|
|
||||||
/** The number of random bytes that are needed to create a new outbound
|
/** The number of random bytes that are needed to create a new outbound
|
||||||
* session. This will be 64 bytes since two ephemeral keys are needed. */
|
* session. This will be 64 bytes since two ephemeral keys are needed. */
|
||||||
std::size_t new_outbound_session_random_length();
|
std::size_t new_outbound_session_random_length() const;
|
||||||
|
|
||||||
/** Start a new outbound session. Returns std::size_t(-1) on failure. On
|
/** Start a new outbound session. Returns std::size_t(-1) on failure. On
|
||||||
* failure last_error will be set with an error code. The last_error will be
|
* failure last_error will be set with an error code. The last_error will be
|
||||||
|
@ -64,7 +68,7 @@ struct Session {
|
||||||
);
|
);
|
||||||
|
|
||||||
/** The number of bytes written by session_id() */
|
/** The number of bytes written by session_id() */
|
||||||
std::size_t session_id_length();
|
std::size_t session_id_length() const;
|
||||||
|
|
||||||
/** An identifier for this session. Generated by hashing the public keys
|
/** An identifier for this session. Generated by hashing the public keys
|
||||||
* used to create the session. Returns the length of the session id on
|
* used to create the session. Returns the length of the session id on
|
||||||
|
@ -84,21 +88,21 @@ struct Session {
|
||||||
bool matches_inbound_session(
|
bool matches_inbound_session(
|
||||||
_olm_curve25519_public_key const * their_identity_key,
|
_olm_curve25519_public_key const * their_identity_key,
|
||||||
std::uint8_t const * pre_key_message, std::size_t message_length
|
std::uint8_t const * pre_key_message, std::size_t message_length
|
||||||
);
|
) const;
|
||||||
|
|
||||||
/** Whether the next message will be a pre-key message or a normal message.
|
/** Whether the next message will be a pre-key message or a normal message.
|
||||||
* An outbound session will send pre-key messages until it receives a
|
* An outbound session will send pre-key messages until it receives a
|
||||||
* message with a ratchet key. */
|
* message with a ratchet key. */
|
||||||
MessageType encrypt_message_type();
|
MessageType encrypt_message_type() const;
|
||||||
|
|
||||||
std::size_t encrypt_message_length(
|
std::size_t encrypt_message_length(
|
||||||
std::size_t plaintext_length
|
std::size_t plaintext_length
|
||||||
);
|
) const;
|
||||||
|
|
||||||
/** The number of bytes of random data the encrypt method will need to
|
/** The number of bytes of random data the encrypt method will need to
|
||||||
* encrypt a message. This will be 32 bytes if the session needs to
|
* encrypt a message. This will be 32 bytes if the session needs to
|
||||||
* generate a new ephemeral key, or will be 0 bytes otherwise. */
|
* generate a new ephemeral key, or will be 0 bytes otherwise. */
|
||||||
std::size_t encrypt_random_length();
|
std::size_t encrypt_random_length() const;
|
||||||
|
|
||||||
/** Encrypt some plain-text. Returns the length of the encrypted message
|
/** Encrypt some plain-text. Returns the length of the encrypted message
|
||||||
* or std::size_t(-1) on failure. On failure last_error will be set with
|
* or std::size_t(-1) on failure. On failure last_error will be set with
|
||||||
|
@ -131,6 +135,14 @@ struct Session {
|
||||||
std::uint8_t const * message, std::size_t message_length,
|
std::uint8_t const * message, std::size_t message_length,
|
||||||
std::uint8_t * plaintext, std::size_t max_plaintext_length
|
std::uint8_t * plaintext, std::size_t max_plaintext_length
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a string describing this session and its state (not including the
|
||||||
|
* private key) into the buffer provided.
|
||||||
|
*
|
||||||
|
* Takes a buffer to write to and the length of that buffer
|
||||||
|
*/
|
||||||
|
void describe(char *buf, size_t buflen);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -145,7 +157,7 @@ std::uint8_t * pickle(
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
std::uint8_t const * unpickle(
|
OLM_EXPORT std::uint8_t const * unpickle(
|
||||||
std::uint8_t const * pos, std::uint8_t const * end,
|
std::uint8_t const * pos, std::uint8_t const * end,
|
||||||
Session & value
|
Session & value
|
||||||
);
|
);
|
||||||
|
|
|
@ -32,7 +32,7 @@ struct Utility {
|
||||||
OlmErrorCode last_error;
|
OlmErrorCode last_error;
|
||||||
|
|
||||||
/** The length of a SHA-256 hash in bytes. */
|
/** The length of a SHA-256 hash in bytes. */
|
||||||
std::size_t sha256_length();
|
std::size_t sha256_length() const;
|
||||||
|
|
||||||
/** Compute a SHA-256 hash. Returns the length of the SHA-256 hash in bytes
|
/** Compute a SHA-256 hash. Returns the length of the SHA-256 hash in bytes
|
||||||
* on success. Returns std::size_t(-1) on failure. On failure last_error
|
* on success. Returns std::size_t(-1) on failure. On failure last_error
|
||||||
|
|
29
javascript/.gitlab-ci.yml
Normal file
29
javascript/.gitlab-ci.yml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
default:
|
||||||
|
image: docker.io/emscripten/emsdk:latest
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- build
|
||||||
|
- test
|
||||||
|
|
||||||
|
build:js:
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- ln -sf $(which python3) /usr/local/bin/python
|
||||||
|
- make js
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- build/javascript
|
||||||
|
- javascript/olm.js
|
||||||
|
- javascript/olm.wasm
|
||||||
|
- javascript/olm_legacy.js
|
||||||
|
- javascript/index.d.ts
|
||||||
|
- javascript/exported_functions.json
|
||||||
|
|
||||||
|
test:js:
|
||||||
|
stage: test
|
||||||
|
needs:
|
||||||
|
- build:js
|
||||||
|
script:
|
||||||
|
- pushd javascript
|
||||||
|
- npm i
|
||||||
|
- npm run test
|
|
@ -1,6 +1,15 @@
|
||||||
Olm
|
Olm
|
||||||
===
|
===
|
||||||
|
|
||||||
|
Note: before using any of the olm functions, you must call `Olm.init()`, and
|
||||||
|
wait for the promise to resolve, otherwise you will get errors like:
|
||||||
|
`Uncaught TypeError: Olm.Account is not a constructor`
|
||||||
|
|
||||||
|
If you get errors about failure to compile the wasm file, it is likely that Olm
|
||||||
|
is not locating the wasm file properly. You can tell Olm where the wasm file
|
||||||
|
is by passing a `locateFile` parameter to `Olm.init()`, for example:
|
||||||
|
`Olm.init({locateFile: () => pathToWasmFile})`.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
var alice = new Olm.Account();
|
var alice = new Olm.Account();
|
||||||
|
|
|
@ -57,5 +57,56 @@
|
||||||
<div class="user_progress"></div>
|
<div class="user_progress"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="user3" class="user">
|
||||||
|
<h1>User 3</h1>
|
||||||
|
|
||||||
|
<textarea class="user_plain_input"></textarea>
|
||||||
|
<button class="user_encrypt">Encrypt</button>
|
||||||
|
|
||||||
|
<h2>Outgoing</h2>
|
||||||
|
|
||||||
|
<h3>One-to-one output</h3>
|
||||||
|
<div class="user_cipher_output"></div>
|
||||||
|
|
||||||
|
<h3>Group output</h3>
|
||||||
|
<div class="group_output"></div>
|
||||||
|
|
||||||
|
<h2>Incoming</h2>
|
||||||
|
|
||||||
|
<h3>One-to-one Received</h3>
|
||||||
|
<div class="user_cipher_input"></div>
|
||||||
|
|
||||||
|
<h3>Group received</h3>
|
||||||
|
<div class="group_input"></div>
|
||||||
|
|
||||||
|
<h2>Tasks</h2>
|
||||||
|
<div class="user_progress"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="user4" class="user">
|
||||||
|
<h1>User 4</h1>
|
||||||
|
|
||||||
|
<textarea class="user_plain_input"></textarea>
|
||||||
|
<button class="user_encrypt">Encrypt</button>
|
||||||
|
|
||||||
|
<h2>Outgoing</h2>
|
||||||
|
|
||||||
|
<h3>One-to-one output</h3>
|
||||||
|
<div class="user_cipher_output"></div>
|
||||||
|
|
||||||
|
<h3>Group output</h3>
|
||||||
|
<div class="group_output"></div>
|
||||||
|
|
||||||
|
<h2>Incoming</h2>
|
||||||
|
|
||||||
|
<h3>One-to-one Received</h3>
|
||||||
|
<div class="user_cipher_input"></div>
|
||||||
|
|
||||||
|
<h3>Group received</h3>
|
||||||
|
<div class="group_input"></div>
|
||||||
|
|
||||||
|
<h2>Tasks</h2>
|
||||||
|
<div class="user_progress"></div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -167,20 +167,13 @@ DemoUser.prototype.getIdKeys = function() {
|
||||||
return JSON.parse(this.olmAccount.identity_keys());
|
return JSON.parse(this.olmAccount.identity_keys());
|
||||||
};
|
};
|
||||||
|
|
||||||
DemoUser.prototype.generateKeys = function(callback) {
|
|
||||||
var self = this;
|
|
||||||
this.addTask("generate one time key", function(done) {
|
|
||||||
self.olmAccount.generate_one_time_keys(1);
|
|
||||||
done();
|
|
||||||
}, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
DemoUser.prototype.getOneTimeKey = function() {
|
DemoUser.prototype.getOneTimeKey = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
var keys = JSON.parse(self.olmAccount.one_time_keys())
|
self.olmAccount.generate_one_time_keys(1);
|
||||||
.curve25519;
|
var keys = JSON.parse(self.olmAccount.one_time_keys()).curve25519;
|
||||||
for (key_id in keys) {
|
for (key_id in keys) {
|
||||||
if (keys.hasOwnProperty(key_id)) {
|
if (keys.hasOwnProperty(key_id)) {
|
||||||
|
self.olmAccount.mark_keys_as_published();
|
||||||
return keys[key_id];
|
return keys[key_id];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -478,15 +471,36 @@ function initUserDiv(demoUser, div) {
|
||||||
function startDemo() {
|
function startDemo() {
|
||||||
var user1 = new DemoUser();
|
var user1 = new DemoUser();
|
||||||
initUserDiv(user1, document.getElementById("user1"));
|
initUserDiv(user1, document.getElementById("user1"));
|
||||||
user1.generateKeys();
|
|
||||||
|
|
||||||
var user2 = new DemoUser();
|
var user2 = new DemoUser();
|
||||||
initUserDiv(user2, document.getElementById("user2"));
|
initUserDiv(user2, document.getElementById("user2"));
|
||||||
user2.generateKeys();
|
|
||||||
|
var user3 = new DemoUser();
|
||||||
|
initUserDiv(user3, document.getElementById("user3"));
|
||||||
|
|
||||||
|
var user4 = new DemoUser();
|
||||||
|
initUserDiv(user4, document.getElementById("user4"));
|
||||||
|
|
||||||
user1.addPeer(user2.remoteOps);
|
user1.addPeer(user2.remoteOps);
|
||||||
|
user1.addPeer(user3.remoteOps);
|
||||||
|
user1.addPeer(user4.remoteOps);
|
||||||
|
|
||||||
user2.addPeer(user1.remoteOps);
|
user2.addPeer(user1.remoteOps);
|
||||||
|
user2.addPeer(user3.remoteOps);
|
||||||
|
user2.addPeer(user4.remoteOps);
|
||||||
|
|
||||||
|
user3.addPeer(user1.remoteOps);
|
||||||
|
user3.addPeer(user2.remoteOps);
|
||||||
|
user3.addPeer(user4.remoteOps);
|
||||||
|
|
||||||
|
user4.addPeer(user1.remoteOps);
|
||||||
|
user4.addPeer(user2.remoteOps);
|
||||||
|
user4.addPeer(user3.remoteOps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", startDemo, false);
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
Olm.init().then(function() {
|
||||||
|
startDemo();
|
||||||
|
});
|
||||||
|
}, false);
|
||||||
|
|
|
@ -2,7 +2,13 @@
|
||||||
<head>
|
<head>
|
||||||
<script src="../olm.js"></script>
|
<script src="../olm.js"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", function (event) {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
Olm.init().then(function() {
|
||||||
|
demo();
|
||||||
|
});
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
function demo() {
|
||||||
function progress(who, message) {
|
function progress(who, message) {
|
||||||
var message_element = document.createElement("pre");
|
var message_element = document.createElement("pre");
|
||||||
var progress = document.getElementById(who + "_progress");
|
var progress = document.getElementById(who + "_progress");
|
||||||
|
@ -109,7 +115,7 @@ document.addEventListener("DOMContentLoaded", function (event) {
|
||||||
glue_encrypt("bob", "alice", b_session);
|
glue_encrypt("bob", "alice", b_session);
|
||||||
glue_decrypt("alice", a_session);
|
glue_decrypt("alice", a_session);
|
||||||
});
|
});
|
||||||
}, false);
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<body>
|
<body>
|
||||||
|
|
142
javascript/index.d.ts
vendored
Normal file
142
javascript/index.d.ts
vendored
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export as namespace Olm;
|
||||||
|
|
||||||
|
declare class Account {
|
||||||
|
constructor();
|
||||||
|
free(): void;
|
||||||
|
create(): void;
|
||||||
|
identity_keys(): string;
|
||||||
|
sign(message: string | Uint8Array): string;
|
||||||
|
one_time_keys(): string;
|
||||||
|
mark_keys_as_published(): void;
|
||||||
|
max_number_of_one_time_keys(): number;
|
||||||
|
generate_one_time_keys(number_of_keys: number): void;
|
||||||
|
remove_one_time_keys(session: Session): void;
|
||||||
|
generate_fallback_key(): void;
|
||||||
|
fallback_key(): string;
|
||||||
|
unpublished_fallback_key(): string;
|
||||||
|
forget_old_fallback_key(): void;
|
||||||
|
pickle(key: string | Uint8Array): string;
|
||||||
|
unpickle(key: string | Uint8Array, pickle: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare class Session {
|
||||||
|
constructor();
|
||||||
|
free(): void;
|
||||||
|
pickle(key: string | Uint8Array): string;
|
||||||
|
unpickle(key: string | Uint8Array, pickle: string): void;
|
||||||
|
create_outbound(
|
||||||
|
account: Account, their_identity_key: string, their_one_time_key: string,
|
||||||
|
): void;
|
||||||
|
create_inbound(account: Account, one_time_key_message: string): void;
|
||||||
|
create_inbound_from(
|
||||||
|
account: Account, identity_key: string, one_time_key_message: string,
|
||||||
|
): void;
|
||||||
|
session_id(): string;
|
||||||
|
has_received_message(): boolean;
|
||||||
|
matches_inbound(one_time_key_message: string): boolean;
|
||||||
|
matches_inbound_from(identity_key: string, one_time_key_message: string): boolean;
|
||||||
|
encrypt(plaintext: string): {
|
||||||
|
type: 0 | 1; // 0: PreKey, 1: Message
|
||||||
|
body: string;
|
||||||
|
};
|
||||||
|
decrypt(message_type: number, message: string): string;
|
||||||
|
describe(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare class Utility {
|
||||||
|
constructor();
|
||||||
|
free(): void;
|
||||||
|
sha256(input: string | Uint8Array): string;
|
||||||
|
ed25519_verify(key: string, message: string | Uint8Array, signature: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare class InboundGroupSession {
|
||||||
|
constructor();
|
||||||
|
free(): void;
|
||||||
|
pickle(key: string | Uint8Array): string;
|
||||||
|
unpickle(key: string | Uint8Array, pickle: string): void;
|
||||||
|
create(session_key: string): string;
|
||||||
|
import_session(session_key: string): string;
|
||||||
|
decrypt(message: string): {
|
||||||
|
message_index: number;
|
||||||
|
plaintext: string;
|
||||||
|
};
|
||||||
|
session_id(): string;
|
||||||
|
first_known_index(): number;
|
||||||
|
export_session(message_index: number): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare class OutboundGroupSession {
|
||||||
|
constructor();
|
||||||
|
free(): void;
|
||||||
|
pickle(key: string | Uint8Array): string;
|
||||||
|
unpickle(key: string | Uint8Array, pickle: string): void;
|
||||||
|
create(): void;
|
||||||
|
encrypt(plaintext: string): string;
|
||||||
|
session_id(): string;
|
||||||
|
session_key(): string;
|
||||||
|
message_index(): number;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare class PkEncryption {
|
||||||
|
constructor();
|
||||||
|
free(): void;
|
||||||
|
set_recipient_key(key: string): void;
|
||||||
|
encrypt(plaintext: string): {
|
||||||
|
ciphertext: string;
|
||||||
|
mac: string;
|
||||||
|
ephemeral: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
declare class PkDecryption {
|
||||||
|
constructor();
|
||||||
|
free(): void;
|
||||||
|
init_with_private_key(key: Uint8Array): string;
|
||||||
|
generate_key(): string;
|
||||||
|
get_private_key(): Uint8Array;
|
||||||
|
pickle(key: string | Uint8Array): string;
|
||||||
|
unpickle(key: string | Uint8Array, pickle: string): string;
|
||||||
|
decrypt(ephemeral_key: string, mac: string, ciphertext: string): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare class PkSigning {
|
||||||
|
constructor();
|
||||||
|
free(): void;
|
||||||
|
init_with_seed(seed: Uint8Array): string;
|
||||||
|
generate_seed(): Uint8Array;
|
||||||
|
sign(message: string): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare class SAS {
|
||||||
|
constructor();
|
||||||
|
free(): void;
|
||||||
|
get_pubkey(): string;
|
||||||
|
set_their_key(their_key: string): void;
|
||||||
|
generate_bytes(info: string, length: number): Uint8Array;
|
||||||
|
calculate_mac(input: string, info: string): string;
|
||||||
|
calculate_mac_fixed_base64(input: string, info: string): string;
|
||||||
|
calculate_mac_long_kdf(input: string, info: string): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function init(opts?: object): Promise<void>;
|
||||||
|
|
||||||
|
export function get_library_version(): [number, number, number];
|
||||||
|
|
||||||
|
export const PRIVATE_KEY_LENGTH: number;
|
|
@ -1,3 +1,4 @@
|
||||||
|
/** @constructor */
|
||||||
function InboundGroupSession() {
|
function InboundGroupSession() {
|
||||||
var size = Module['_olm_inbound_group_session_size']();
|
var size = Module['_olm_inbound_group_session_size']();
|
||||||
this.buf = malloc(size);
|
this.buf = malloc(size);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/** @constructor */
|
||||||
function OutboundGroupSession() {
|
function OutboundGroupSession() {
|
||||||
var size = Module['_olm_outbound_group_session_size']();
|
var size = Module['_olm_outbound_group_session_size']();
|
||||||
this.buf = malloc(size);
|
this.buf = malloc(size);
|
||||||
|
@ -67,9 +68,14 @@ OutboundGroupSession.prototype['create'] = restore_stack(function() {
|
||||||
Module['_olm_init_outbound_group_session_random_length']
|
Module['_olm_init_outbound_group_session_random_length']
|
||||||
)(this.ptr);
|
)(this.ptr);
|
||||||
var random = random_stack(random_length);
|
var random = random_stack(random_length);
|
||||||
|
try {
|
||||||
outbound_group_session_method(Module['_olm_init_outbound_group_session'])(
|
outbound_group_session_method(Module['_olm_init_outbound_group_session'])(
|
||||||
this.ptr, random, random_length
|
this.ptr, random, random_length
|
||||||
);
|
);
|
||||||
|
} finally {
|
||||||
|
// clear the random buffer
|
||||||
|
bzero(random, random_length);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
OutboundGroupSession.prototype['encrypt'] = function(plaintext) {
|
OutboundGroupSession.prototype['encrypt'] = function(plaintext) {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue