Compare commits
2 commits
master
...
rav/inter_
Author | SHA1 | Date | |
---|---|---|---|
|
ad7ffd4cfa | ||
|
23a3e32b8d |
548 changed files with 4380 additions and 69567 deletions
|
@ -1,27 +0,0 @@
|
||||||
version: 2
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
docker:
|
|
||||||
- image: trzeci/emscripten
|
|
||||||
|
|
||||||
working_directory: ~/repo
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- run:
|
|
||||||
name: Native Compile
|
|
||||||
command: make
|
|
||||||
- run:
|
|
||||||
name: Native Tests
|
|
||||||
command: make test
|
|
||||||
- run:
|
|
||||||
name: JS Compile
|
|
||||||
command: make js
|
|
||||||
- run:
|
|
||||||
name: Install JS Deps
|
|
||||||
working_directory: ~/repo/javascript
|
|
||||||
command: npm install
|
|
||||||
- run:
|
|
||||||
name: JS Tests
|
|
||||||
working_directory: ~/repo/javascript
|
|
||||||
command: npm run test
|
|
|
@ -1,14 +0,0 @@
|
||||||
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
|
|
35
.gitignore
vendored
35
.gitignore
vendored
|
@ -6,38 +6,3 @@
|
||||||
/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
|
|
||||||
build/
|
|
||||||
.build/
|
|
||||||
DerivedData/
|
|
||||||
*.pbxuser
|
|
||||||
!default.pbxuser
|
|
||||||
*.mode1v3
|
|
||||||
!default.mode1v3
|
|
||||||
*.mode2v3
|
|
||||||
!default.mode2v3
|
|
||||||
*.perspectivev3
|
|
||||||
!default.perspectivev3
|
|
||||||
xcuserdata/
|
|
||||||
*.moved-aside
|
|
||||||
*.xcuserstate
|
|
||||||
*.hmap
|
|
||||||
*.ipa
|
|
||||||
*.dSYM.zip
|
|
||||||
*.dSYM
|
|
||||||
Pods/
|
|
||||||
*.xcworkspace
|
|
||||||
|
|
||||||
# JetBrains tools
|
|
||||||
.idea/
|
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
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
|
|
392
CHANGELOG.rst
392
CHANGELOG.rst
|
@ -1,376 +1,5 @@
|
||||||
Changes in `3.2.16 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.16>`_
|
Changes in `2.0.0 <http://matrix.org/git/olm/commit/?h=2.0.0>`_
|
||||||
===========================================================================
|
===============================================================
|
||||||
|
|
||||||
This release includes the following changes since 3.2.15:
|
|
||||||
|
|
||||||
* Fix and modernize the Python packaging (thanks to Alfred Wingate)
|
|
||||||
|
|
||||||
Changes in `3.2.15 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.15>`_
|
|
||||||
===========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 3.2.14:
|
|
||||||
|
|
||||||
* Improvements to Python packaging
|
|
||||||
* No longer depend on ``future`` since Python 2 is no longer supported.
|
|
||||||
* Improve compatibility with tox 4.
|
|
||||||
* Add support for making standalone sdist.
|
|
||||||
* Improvements to Nix flake (Thanks to Jon Ringer)
|
|
||||||
* Improve structure.
|
|
||||||
* Enable Darwin builds.
|
|
||||||
* Typescript type fix.
|
|
||||||
|
|
||||||
Changes in `3.2.14 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.14>`_
|
|
||||||
===========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 3.2.13:
|
|
||||||
|
|
||||||
* TypeScript type improvements.
|
|
||||||
* Improvements to Python packaging
|
|
||||||
* Documentation improvements.
|
|
||||||
|
|
||||||
Changes in `3.2.13 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.13>`_
|
|
||||||
===========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 3.2.12:
|
|
||||||
|
|
||||||
* Fix compilation with newer versions of emscripten.
|
|
||||||
* The npm package is compiled with emscripten 3.1.17 to fix compatibility with
|
|
||||||
node 18.
|
|
||||||
* Add py.typed to Python wheels.
|
|
||||||
* Some documentation fixes and updates.
|
|
||||||
* Improve the pkgconfig file.
|
|
||||||
|
|
||||||
Changes in `3.2.12 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.12>`_
|
|
||||||
===========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 3.2.11:
|
|
||||||
|
|
||||||
* Expose olm_sas_calculate_mac_fixed_base64 in the bindings.
|
|
||||||
* Allow memory to grow in wasm. Thanks to benkuly for the suggestion.
|
|
||||||
* Fix Python type hints.
|
|
||||||
* Some Python build fixes.
|
|
||||||
* Initial work on a Nix flake for building and testing.
|
|
||||||
|
|
||||||
Changes in `3.2.11 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.11>`_
|
|
||||||
===========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 3.2.10:
|
|
||||||
|
|
||||||
* Fix building documentation. Thanks to Jonas Smedegaard. The documents
|
|
||||||
written in Markdown are now converted to HTML using Pandoc.
|
|
||||||
* Add methods for getting unpublished fallback key in Objective-C binding.
|
|
||||||
* Add public pickle/unpickle methods to Java binding.
|
|
||||||
* Add wrapper for olm_session_describe to Java binding. Thanks to Alex Baker.
|
|
||||||
|
|
||||||
Changes in `3.2.10 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.10>`_
|
|
||||||
===========================================================================
|
|
||||||
|
|
||||||
This release includes no change since 3.2.9, but is created to be able to
|
|
||||||
publish again the Android library on MavenCentral.
|
|
||||||
|
|
||||||
Changes in `3.2.9 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.9>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 3.2.8:
|
|
||||||
|
|
||||||
* Switch C++ tests to use doctest. Thanks to Nicolas Werner.
|
|
||||||
* Switch JavaScript tests to use jasmine instead of deprecated jasmine-node.
|
|
||||||
* Add session describe function to Python binding. Thanks to Tulir Asokan.
|
|
||||||
|
|
||||||
Changes in `3.2.8 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.8>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 3.2.7:
|
|
||||||
|
|
||||||
* Improve handling of olm_session_describe when the buffer is too small.
|
|
||||||
* Ensure random arrays are blanked in JavaScript bindings.
|
|
||||||
|
|
||||||
Changes in `3.2.7 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.7>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 3.2.6:
|
|
||||||
|
|
||||||
* Fix installation with the Makefile.
|
|
||||||
* Fix exporting again, so we only export olm symbols.
|
|
||||||
* Fix WASM build. Thanks to Benjamin Kampmann.
|
|
||||||
* Add more functions for fallback keys.
|
|
||||||
|
|
||||||
Changes in `3.2.6 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.6>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 3.2.5:
|
|
||||||
|
|
||||||
* Fix building on various platforms when using CMake. Building from the
|
|
||||||
Makefile still assumes that it is using gcc.
|
|
||||||
|
|
||||||
Changes in `3.2.5 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.5>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 3.2.4:
|
|
||||||
|
|
||||||
* Add functions for getting error codes rather than error strings. Thanks to
|
|
||||||
Nicolas Werner for the suggestion.
|
|
||||||
* Only export olm symbols. Thanks to Mohammed Sadiq for the suggestion.
|
|
||||||
* Improve error handling in unpickle functions.
|
|
||||||
* Add support for fallback keys to the Objective-C and Android bindings.
|
|
||||||
|
|
||||||
Changes in `3.2.4 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.4>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 3.2.3:
|
|
||||||
|
|
||||||
* Android build fixes.
|
|
||||||
|
|
||||||
Changes in `3.2.3 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.3>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 3.2.2:
|
|
||||||
|
|
||||||
* Add some checks for invalid input and ensure all fields are initialized.
|
|
||||||
* Include LibreJS license tags. Thanks to Johannes Marbach for the suggestion.
|
|
||||||
* Support for Swift Package Manager. Thanks to Johannes Marbach.
|
|
||||||
|
|
||||||
Changes in `3.2.2 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.2>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 3.2.1:
|
|
||||||
|
|
||||||
* Fixes in the TypeScript definition file.
|
|
||||||
* CMake build fixes. Thanks to Gorgurov Alexey.
|
|
||||||
* Change the JavaScript package name to ``@matrix-org/olm``. Note that
|
|
||||||
this means that packages will need to change their ``require`` or
|
|
||||||
``import`` statements to use this new name.
|
|
||||||
* Include file checksums in the JavaScript package.
|
|
||||||
* Fix length calculation in fallback key json. Thanks to Tobias Furuholm.
|
|
||||||
* Add a new function to calculate the correct base64 encoding for SAS.
|
|
||||||
(Currently only available in the C API.)
|
|
||||||
* Add the ability to specify a pickle key in the Objective-C binding.
|
|
||||||
* Add pkg-config file on Unix-like systems.
|
|
||||||
|
|
||||||
Changes in `3.2.1 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.1>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 3.2.0:
|
|
||||||
|
|
||||||
* Fixes in the TypeScript definition file.
|
|
||||||
|
|
||||||
Changes in `3.2.0 <https://gitlab.matrix.org/matrix-org/olm/tags/3.2.0>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 3.1.5:
|
|
||||||
|
|
||||||
* Add support for fallback keys (MSC2732).
|
|
||||||
* Allow some arguments in the JavaScript bindings to be either Uint8Array or
|
|
||||||
strings.
|
|
||||||
* Fixes to the TypeScript definition file.
|
|
||||||
* Improvements to the JavaScript group demo. Thanks to Saúl Ibarra Corretgé.
|
|
||||||
* Ensure that the other party's public key has been set in SAS module. Thanks
|
|
||||||
to Saúl Ibarra Corretgé.
|
|
||||||
* Fix building with newer versions of emscripten, and simplify makefile. Thanks
|
|
||||||
to Lukas Lihotzki.
|
|
||||||
* Reduce pollution of the global namespace in the Javascript binding. Thanks to
|
|
||||||
Lukas Lihotzki.
|
|
||||||
|
|
||||||
Changes in `3.1.5 <https://gitlab.matrix.org/matrix-org/olm/tags/3.1.5>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 3.1.4:
|
|
||||||
|
|
||||||
* Build improvements:
|
|
||||||
|
|
||||||
* Fix CMake handling when installing in a non-standard location. Thanks to
|
|
||||||
Alexey Rusakov.
|
|
||||||
* Add support in the Makefile for creating a WASM-ready archive. Thanks to
|
|
||||||
stoically.
|
|
||||||
* Improve support for LLVM is Makefile. Thanks to caywin25 for reporting.
|
|
||||||
|
|
||||||
* Add a TypeScript definition file.
|
|
||||||
* Some documentation and example fixes.
|
|
||||||
* Add list of bindings to the README.
|
|
||||||
|
|
||||||
Changes in `3.1.4 <https://gitlab.matrix.org/matrix-org/olm/tags/3.1.4>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 3.1.3:
|
|
||||||
|
|
||||||
* Build improvements:
|
|
||||||
* Install headers in the system-configured include directory with CMake.
|
|
||||||
* Overwrite symbolic links when installing with make.
|
|
||||||
* Improve compatibility with more emscripten versions.
|
|
||||||
* Don't use hypothesis in Python unit tests.
|
|
||||||
* Some documentation improvements.
|
|
||||||
|
|
||||||
Changes in `3.1.3 <https://gitlab.matrix.org/matrix-org/olm/tags/3.1.3>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release fixes unicode issues in the Python bindings, and adds some
|
|
||||||
clarifications to the documentation.
|
|
||||||
|
|
||||||
Changes in `3.1.2 <https://gitlab.matrix.org/matrix-org/olm/tags/3.1.2>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release updates the Android bindings to use a newer Android SDK version.
|
|
||||||
|
|
||||||
Changes in `3.1.1 <https://gitlab.matrix.org/matrix-org/olm/tags/3.1.1>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release fixes various build issues:
|
|
||||||
|
|
||||||
* Include the SAS files and tests in the CMake files.
|
|
||||||
* Address some build issues on Windows.
|
|
||||||
|
|
||||||
Changes in `3.1.0 <https://gitlab.matrix.org/matrix-org/olm/tags/3.1.0>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 3.0.0:
|
|
||||||
|
|
||||||
* Add functions to support Short Authentication String key verification. The
|
|
||||||
new functions are in the ``sas.h`` header file. The Android, iOS, JavaScript
|
|
||||||
and Python bindings also include corresponding functions.
|
|
||||||
* Add functions to perform public key signing. These are meant for use with
|
|
||||||
cross-signing. The new functions are ``olm_pk_signing_size``,
|
|
||||||
``olm_pk_signing``, ``olm_pk_signing_last_error``, ``olm_clear_pk_signing``,
|
|
||||||
``olm_pk_signing_key_from_seed``, ``olm_pk_signing_seed_length``,
|
|
||||||
``olm_pk_signing_public_key_length``, ``olm_pk_signature_length``, and
|
|
||||||
``olm_pk_sign``. Signatures generated by ``olm_pk_sign`` can be verified
|
|
||||||
using ``olm_ed25519_verify``. The Android, iOS, JavaScript and Python
|
|
||||||
bindings also include corresponding functions.
|
|
||||||
* Fix compilation under some compilers.
|
|
||||||
|
|
||||||
JavaScript wrapper:
|
|
||||||
|
|
||||||
* Improved compatibility with newer versions of Emscripten, and dropped support
|
|
||||||
for some older versions of Emscripten.
|
|
||||||
|
|
||||||
Python wrapper:
|
|
||||||
|
|
||||||
* Build fixes.
|
|
||||||
* Add bindings for the public key encryption/decryption functions from olm 2.3.0.
|
|
||||||
|
|
||||||
Changes in `3.0.0 <https://gitlab.matrix.org/matrix-org/olm/tags/3.0.0>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes to 2.3.0:
|
|
||||||
|
|
||||||
* Support for building using cmake. Thanks to Konstantinos Sideris.
|
|
||||||
* Add more functions for managing private keys in the public key decryption
|
|
||||||
functionality. These are meant for use with server-side encrypted key
|
|
||||||
backups. The new functions are ``olm_pk_private_key_length``,
|
|
||||||
``olm_pk_key_from_private``, and ``olm_pk_get_private_key``.
|
|
||||||
* ``olm_pk_generate_key`` and ``olm_pk_generate_key_random_length`` are
|
|
||||||
deprecated: to generate a random key, use ``olm_pk_key_from_private``
|
|
||||||
with random bytes as the private key.
|
|
||||||
|
|
||||||
Python wrapper:
|
|
||||||
|
|
||||||
* BREAKING CHANGE: This release introduces a new API for the Python wrapper,
|
|
||||||
thanks to Damir Jelić. The new API should be much easier to use for Python
|
|
||||||
developers. However, this means that existing code will need to be rewritten
|
|
||||||
to use the new API.
|
|
||||||
|
|
||||||
JavaScript wrapper:
|
|
||||||
|
|
||||||
* BREAKING CHANGE: Olm now uses WebAssembly which means it needs
|
|
||||||
to load the wasm file asynchronously, and therefore needs to be
|
|
||||||
started up asynchronously. The imported module now has an init()
|
|
||||||
method which returns a promise. The library cannot be used until
|
|
||||||
this promise resolves. It will reject if the library fails to start.
|
|
||||||
* Using ``olm/olm.js`` will use the WebAssembly version of the library. For
|
|
||||||
environments that do not support WebAssembly, use ``olm/olm_legacy.js``.
|
|
||||||
|
|
||||||
Objective-C wrapper:
|
|
||||||
|
|
||||||
* Add support for the public key encryption/decryption functionality.
|
|
||||||
|
|
||||||
Changes in `2.3.0 <https://gitlab.matrix.org/matrix-org/olm/tags/2.3.0>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 2.2.2:
|
|
||||||
|
|
||||||
* Support building on Windows. Thanks to Marcel Radzio.
|
|
||||||
* Avoid C99 inside C++ code. Thanks to Alexey Rusakov.
|
|
||||||
* Support building as a static library. Thanks to Andreas Zwinkau.
|
|
||||||
|
|
||||||
New functionality:
|
|
||||||
|
|
||||||
* Add a number of methods for public key encryption and decryption. This
|
|
||||||
functionality is meant for use with allowing virus scanning of encrypted
|
|
||||||
attachments, server-side encrypted key backups, and possibly other uses. The
|
|
||||||
methods are listed in the ``olm/pk.h`` header file. Corresponding wrappers
|
|
||||||
are available in the JavaScript and Android wrappers. Objective-C and Python
|
|
||||||
wrappers will be available in a future release.
|
|
||||||
|
|
||||||
Android wrapper:
|
|
||||||
|
|
||||||
* Update build tool dependencies
|
|
||||||
* Apply some hardening flags and fix some compilation and run-time issues.
|
|
||||||
Thanks in part to Arnaud Fontaine.
|
|
||||||
|
|
||||||
Objective-C wrapper:
|
|
||||||
|
|
||||||
* Update project file
|
|
||||||
* Fix compiler warnings
|
|
||||||
|
|
||||||
Python wrapper:
|
|
||||||
|
|
||||||
* Add binding for ``olm_remove_one_time_keys``. Thanks to Wilfried Klaebe.
|
|
||||||
* Add utility module for ``ed25519_verify``. Thanks to Alexander Maznev.
|
|
||||||
* Improve portability. Thanks to Jan Jancar.
|
|
||||||
|
|
||||||
Changes in `2.2.2 <https://gitlab.matrix.org/matrix-org/olm/tags/2.2.2>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
Objective-C wrapper:
|
|
||||||
|
|
||||||
* Fixed type of ``messageIndex`` argument in
|
|
||||||
``exportSessionAtMessageIndex``. Thanks to Greg Hughes.
|
|
||||||
|
|
||||||
Changes in `2.2.1 <https://gitlab.matrix.org/matrix-org/olm/tags/2.2.1>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
The only change in this release is a fix to the build scripts for the
|
|
||||||
Objective-C wrapper which made it impossible to release the 2.2.0 CocoaPod.
|
|
||||||
|
|
||||||
Changes in `2.2.0 <https://gitlab.matrix.org/matrix-org/olm/tags/2.2.0>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 2.1.0:
|
|
||||||
|
|
||||||
* Add Java wrappers to allow use under Android.
|
|
||||||
|
|
||||||
New functionality:
|
|
||||||
|
|
||||||
* Add a number of methods allowing InboundGroupSessions to be exported and
|
|
||||||
imported. These are: ``olm_inbound_group_session_first_known_index``,
|
|
||||||
``olm_export_inbound_group_session_length``,
|
|
||||||
``olm_export_inbound_group_session``, ``olm_import_inbound_group_session``
|
|
||||||
and ``olm_inbound_group_session_is_verified``. Corresponding wrappers are
|
|
||||||
available in the Javascript, Python, Objective-C and Android wrappers.
|
|
||||||
|
|
||||||
Objective-C wrapper:
|
|
||||||
|
|
||||||
* Fix a number of issues with the build scripts which prevented it being used
|
|
||||||
for macOS/Swift projects. Thanks to Avery Pierce.
|
|
||||||
|
|
||||||
Changes in `2.1.0 <https://gitlab.matrix.org/matrix-org/olm/tags/2.1.0>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 2.0.0:
|
|
||||||
|
|
||||||
* Add OLMKit, the Objective-C wrapper. Thanks to Chris Ballinger for the
|
|
||||||
initial work on this.
|
|
||||||
|
|
||||||
Javascript wrapper:
|
|
||||||
|
|
||||||
* Handle exceptions during loading better (don't leave a half-initialised
|
|
||||||
state).
|
|
||||||
* Allow applications to tune emscripten options (such as the amount of heap).
|
|
||||||
* Allocate memory for encrypted/decrypted messages on the empscripten heap,
|
|
||||||
rather than the stack, allowing more efficient memory use.
|
|
||||||
|
|
||||||
|
|
||||||
Changes in `2.0.0 <https://gitlab.matrix.org/matrix-org/olm/tags/2.0.0>`_
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release includes the following changes since 1.3.0:
|
This release includes the following changes since 1.3.0:
|
||||||
|
|
||||||
|
@ -387,9 +16,8 @@ This release includes the following changes since 1.3.0:
|
||||||
* Add an ``install-headers`` target to the Makefile (and run it when installing
|
* Add an ``install-headers`` target to the Makefile (and run it when installing
|
||||||
the library). (Credit to Emmanuel Gil Peyrot).
|
the library). (Credit to Emmanuel Gil Peyrot).
|
||||||
|
|
||||||
|
Changes in `1.3.0 <http://matrix.org/git/olm/commit/?h=1.3.0>`_
|
||||||
Changes in `1.3.0 <https://gitlab.matrix.org/matrix-org/olm/tags/1.3.0>`_
|
===============================================================
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This release updates the group session identifier to avoid collisions.
|
This release updates the group session identifier to avoid collisions.
|
||||||
Group sessions are now identified by their ed25519 public key.
|
Group sessions are now identified by their ed25519 public key.
|
||||||
|
@ -404,8 +32,8 @@ ed25519 key. No attempt was made to preserve backwards-compatibility.
|
||||||
Attempting to send session_keys between old and new versions will give
|
Attempting to send session_keys between old and new versions will give
|
||||||
``OLM_BAD_SESSION_KEY``.
|
``OLM_BAD_SESSION_KEY``.
|
||||||
|
|
||||||
Changes in `1.2.0 <https://gitlab.matrix.org/matrix-org/olm/tags/1.2.0>`_
|
Changes in `1.2.0 <http://matrix.org/git/olm/commit/?h=1.2.0>`_
|
||||||
=========================================================================
|
===============================================================
|
||||||
|
|
||||||
This release updates the implementation of group session communications, to
|
This release updates the implementation of group session communications, to
|
||||||
include Ed25519 signatures on group messages, to ensure that participants in
|
include Ed25519 signatures on group messages, to ensure that participants in
|
||||||
|
@ -426,8 +54,8 @@ release, aimed at making the codebase more consistent, and to help with the
|
||||||
implementation of the group message signatures.
|
implementation of the group message signatures.
|
||||||
|
|
||||||
|
|
||||||
Changes in `1.1.0 <https://gitlab.matrix.org/matrix-org/olm/tags/1.1.0>`_
|
Changes in `1.1.0 <http://matrix.org/git/olm/commit/?h=1.1.0>`_
|
||||||
=========================================================================
|
===============================================================
|
||||||
|
|
||||||
This release includes a fix to a bug which caused Ed25519 keypairs to be
|
This release includes a fix to a bug which caused Ed25519 keypairs to be
|
||||||
generated and used insecurely. Any Ed25519 keys generated by libolm 1.0.0
|
generated and used insecurely. Any Ed25519 keys generated by libolm 1.0.0
|
||||||
|
@ -438,8 +66,8 @@ existing OlmAccounts should in any case be considered compromised (as above),
|
||||||
the library refuses to load them, returning OLM_BAD_LEGACY_ACCOUNT_PICKLE.
|
the library refuses to load them, returning OLM_BAD_LEGACY_ACCOUNT_PICKLE.
|
||||||
|
|
||||||
|
|
||||||
Changes in `1.0.0 <https://gitlab.matrix.org/matrix-org/olm/tags/1.0.0>`_
|
Changes in `1.0.0 <http://matrix.org/git/olm/commit/?h=1.0.0>`_
|
||||||
=========================================================================
|
===============================================================
|
||||||
|
|
||||||
This release includes a fix to a bug which had the potential to leak sensitive
|
This release includes a fix to a bug which had the potential to leak sensitive
|
||||||
data to the application: see
|
data to the application: see
|
||||||
|
|
142
CMakeLists.txt
142
CMakeLists.txt
|
@ -1,142 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 3.4)
|
|
||||||
|
|
||||||
project(olm VERSION 3.2.16 LANGUAGES CXX C)
|
|
||||||
|
|
||||||
option(OLM_TESTS "Build tests" ON)
|
|
||||||
option(BUILD_SHARED_LIBS "Build as a shared library" ON)
|
|
||||||
|
|
||||||
add_definitions(-DOLMLIB_VERSION_MAJOR=${PROJECT_VERSION_MAJOR})
|
|
||||||
add_definitions(-DOLMLIB_VERSION_MINOR=${PROJECT_VERSION_MINOR})
|
|
||||||
add_definitions(-DOLMLIB_VERSION_PATCH=${PROJECT_VERSION_PATCH})
|
|
||||||
|
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
set(CMAKE_C_STANDARD 99)
|
|
||||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
|
||||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
|
||||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE)
|
|
||||||
set(CMAKE_BUILD_TYPE Release)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(CMAKE_C_VISIBILITY_PRESET hidden)
|
|
||||||
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
|
||||||
set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
|
|
||||||
|
|
||||||
add_library(olm
|
|
||||||
src/account.cpp
|
|
||||||
src/base64.cpp
|
|
||||||
src/cipher.cpp
|
|
||||||
src/crypto.cpp
|
|
||||||
src/memory.cpp
|
|
||||||
src/message.cpp
|
|
||||||
src/pickle.cpp
|
|
||||||
src/ratchet.cpp
|
|
||||||
src/session.cpp
|
|
||||||
src/utility.cpp
|
|
||||||
src/pk.cpp
|
|
||||||
src/sas.c
|
|
||||||
|
|
||||||
src/ed25519.c
|
|
||||||
src/error.c
|
|
||||||
src/inbound_group_session.c
|
|
||||||
src/megolm.c
|
|
||||||
src/olm.cpp
|
|
||||||
src/outbound_group_session.c
|
|
||||||
src/pickle_encoding.c
|
|
||||||
|
|
||||||
lib/crypto-algorithms/aes.c
|
|
||||||
lib/crypto-algorithms/sha256.c
|
|
||||||
lib/curve25519-donna/curve25519-donna.c)
|
|
||||||
add_library(Olm::Olm ALIAS olm)
|
|
||||||
|
|
||||||
# restrict the exported symbols
|
|
||||||
include(GenerateExportHeader)
|
|
||||||
generate_export_header(olm
|
|
||||||
EXPORT_FILE_NAME ${CMAKE_CURRENT_SOURCE_DIR}/include/olm/olm_export.h)
|
|
||||||
|
|
||||||
target_include_directories(olm
|
|
||||||
PUBLIC
|
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
|
||||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
|
||||||
PRIVATE
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/lib)
|
|
||||||
|
|
||||||
set_target_properties(olm PROPERTIES
|
|
||||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
|
||||||
VERSION ${PROJECT_VERSION})
|
|
||||||
|
|
||||||
set_target_properties(olm PROPERTIES
|
|
||||||
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
||||||
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
|
||||||
|
|
||||||
# Make a pkg-config file
|
|
||||||
configure_file(${PROJECT_NAME}.pc.in ${PROJECT_NAME}.pc @ONLY NEWLINE_STYLE UNIX)
|
|
||||||
|
|
||||||
#
|
|
||||||
# Installation
|
|
||||||
#
|
|
||||||
set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/Olm)
|
|
||||||
install(TARGETS olm
|
|
||||||
EXPORT olm-targets
|
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
|
||||||
)
|
|
||||||
|
|
||||||
# The exported target will be named Olm.
|
|
||||||
set_target_properties(olm PROPERTIES EXPORT_NAME Olm)
|
|
||||||
install(FILES
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/olm/olm.h
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/olm/olm_export.h
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/olm/outbound_group_session.h
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/olm/inbound_group_session.h
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/olm/pk.h
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/olm/sas.h
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/olm/error.h
|
|
||||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/olm)
|
|
||||||
|
|
||||||
if (UNIX AND NOT APPLE)
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc
|
|
||||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
|
|
||||||
)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# Export the targets to a script.
|
|
||||||
install(EXPORT olm-targets
|
|
||||||
FILE OlmTargets.cmake
|
|
||||||
NAMESPACE Olm::
|
|
||||||
DESTINATION ${INSTALL_CONFIGDIR})
|
|
||||||
|
|
||||||
# Create a ConfigVersion.cmake file.
|
|
||||||
include(CMakePackageConfigHelpers)
|
|
||||||
write_basic_package_version_file(
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/OlmConfigVersion.cmake
|
|
||||||
VERSION ${PROJECT_VERSION}
|
|
||||||
COMPATIBILITY SameMajorVersion)
|
|
||||||
|
|
||||||
configure_package_config_file(
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/cmake/OlmConfig.cmake.in
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/OlmConfig.cmake
|
|
||||||
INSTALL_DESTINATION ${INSTALL_CONFIGDIR})
|
|
||||||
|
|
||||||
#Install the config & configversion.
|
|
||||||
install(FILES
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/OlmConfig.cmake
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/OlmConfigVersion.cmake
|
|
||||||
DESTINATION ${INSTALL_CONFIGDIR})
|
|
||||||
|
|
||||||
# Register package in user's package registry
|
|
||||||
export(EXPORT olm-targets
|
|
||||||
FILE ${CMAKE_CURRENT_BINARY_DIR}/OlmTargets.cmake
|
|
||||||
NAMESPACE Olm::)
|
|
||||||
export(PACKAGE Olm)
|
|
||||||
|
|
||||||
if (OLM_TESTS)
|
|
||||||
add_subdirectory(tests)
|
|
||||||
endif()
|
|
|
@ -1,67 +0,0 @@
|
||||||
# Contributing code to libolm
|
|
||||||
|
|
||||||
To contribute code to this library, the preferred way is to clone the git
|
|
||||||
repository, create a git patch series (for example via ``git
|
|
||||||
format-patch --stdout origin/master``), and send this by email to
|
|
||||||
``olm@matrix.org``.
|
|
||||||
|
|
||||||
Naturally, you must be willing to license your contributions under the same
|
|
||||||
license as the project itself - in this case, Apache Software License v2 (see
|
|
||||||
[LICENSE](LICENSE)).
|
|
||||||
|
|
||||||
## Sign off
|
|
||||||
|
|
||||||
In order to have a concrete record that your contribution is intentional and
|
|
||||||
you agree to license it under the same terms as the project's license, we've
|
|
||||||
adopted the same lightweight approach that the
|
|
||||||
[Linux Kernel](https://www.kernel.org/doc/html/latest/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin),
|
|
||||||
[Docker](https://github.com/docker/docker/blob/master/CONTRIBUTING.md),
|
|
||||||
and many other projects use: the DCO ([Developer Certificate of Origin](http://developercertificate.org/)).
|
|
||||||
This is a simple declaration that you wrote the contribution or otherwise have
|
|
||||||
the right to contribute it to Matrix::
|
|
||||||
|
|
||||||
Developer Certificate of Origin
|
|
||||||
Version 1.1
|
|
||||||
|
|
||||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
|
||||||
660 York Street, Suite 102,
|
|
||||||
San Francisco, CA 94110 USA
|
|
||||||
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies of this
|
|
||||||
license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
Developer's Certificate of Origin 1.1
|
|
||||||
|
|
||||||
By making a contribution to this project, I certify that:
|
|
||||||
|
|
||||||
(a) The contribution was created in whole or in part by me and I
|
|
||||||
have the right to submit it under the open source license
|
|
||||||
indicated in the file; or
|
|
||||||
|
|
||||||
(b) The contribution is based upon previous work that, to the best
|
|
||||||
of my knowledge, is covered under an appropriate open source
|
|
||||||
license and I have the right under that license to submit that
|
|
||||||
work with modifications, whether created in whole or in part
|
|
||||||
by me, under the same open source license (unless I am
|
|
||||||
permitted to submit under a different license), as indicated
|
|
||||||
in the file; or
|
|
||||||
|
|
||||||
(c) The contribution was provided directly to me by some other
|
|
||||||
person who certified (a), (b) or (c) and I have not modified
|
|
||||||
it.
|
|
||||||
|
|
||||||
(d) I understand and agree that this project and the contribution
|
|
||||||
are public and that a record of the contribution (including all
|
|
||||||
personal information I submit with it, including my sign-off) is
|
|
||||||
maintained indefinitely and may be redistributed consistent with
|
|
||||||
this project or the open source license(s) involved.
|
|
||||||
|
|
||||||
If you agree to this for your contribution, then all that's needed is to
|
|
||||||
include the line in your commits or covering email::
|
|
||||||
|
|
||||||
Signed-off-by: Your Name <your@email.example.org>
|
|
||||||
|
|
||||||
...using your real name; unfortunately pseudonyms and anonymous contributions
|
|
||||||
can't be accepted. Git makes this trivial - just use the -s flag when you do
|
|
||||||
``git commit``, having first set ``user.name`` and ``user.email`` git configs
|
|
||||||
(which you should have done anyway :)
|
|
328
Makefile
328
Makefile
|
@ -1,79 +1,48 @@
|
||||||
#!/usr/bin/make -f
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
include common.mk
|
MAJOR := 2
|
||||||
|
MINOR := 0
|
||||||
|
PATCH := 0
|
||||||
VERSION := $(MAJOR).$(MINOR).$(PATCH)
|
VERSION := $(MAJOR).$(MINOR).$(PATCH)
|
||||||
PREFIX ?= /usr/local
|
PREFIX ?= /usr/local
|
||||||
BUILD_DIR := build
|
BUILD_DIR := build
|
||||||
RELEASE_OPTIMIZE_FLAGS ?= -O3
|
RELEASE_OPTIMIZE_FLAGS ?= -g -O3
|
||||||
DEBUG_OPTIMIZE_FLAGS ?= -g -O0 -U_FORTIFY_SOURCE
|
DEBUG_OPTIMIZE_FLAGS ?= -g -O0
|
||||||
JS_OPTIMIZE_FLAGS ?= -O3
|
JS_OPTIMIZE_FLAGS ?= -O3
|
||||||
FUZZER_OPTIMIZE_FLAGS ?= -O3
|
FUZZING_OPTIMIZE_FLAGS ?= -O3
|
||||||
|
CC = gcc
|
||||||
EMCC = emcc
|
EMCC = emcc
|
||||||
EMAR = emar
|
AFL_CC = afl-gcc
|
||||||
AR = ar
|
AFL_CXX = afl-g++
|
||||||
|
|
||||||
UNAME := $(shell uname)
|
RELEASE_TARGET := $(BUILD_DIR)/libolm.so.$(VERSION)
|
||||||
ifeq ($(UNAME),Darwin)
|
DEBUG_TARGET := $(BUILD_DIR)/libolm_debug.so.$(VERSION)
|
||||||
SO := dylib
|
JS_TARGET := javascript/olm.js
|
||||||
OLM_LDFLAGS :=
|
|
||||||
else
|
|
||||||
SO := so
|
|
||||||
OLM_LDFLAGS := -Wl,-soname,libolm.so.$(MAJOR) \
|
|
||||||
-Wl,--version-script,version_script.ver
|
|
||||||
endif
|
|
||||||
|
|
||||||
RELEASE_TARGET := $(BUILD_DIR)/libolm.$(SO).$(VERSION)
|
|
||||||
STATIC_RELEASE_TARGET := $(BUILD_DIR)/libolm.a
|
|
||||||
DEBUG_TARGET := $(BUILD_DIR)/libolm_debug.$(SO).$(VERSION)
|
|
||||||
JS_WASM_TARGET := javascript/olm.js
|
|
||||||
JS_ASMJS_TARGET := javascript/olm_legacy.js
|
|
||||||
WASM_TARGET := $(BUILD_DIR)/wasm/libolm.a
|
|
||||||
|
|
||||||
JS_EXPORTED_FUNCTIONS := javascript/exported_functions.json
|
JS_EXPORTED_FUNCTIONS := javascript/exported_functions.json
|
||||||
JS_EXPORTED_RUNTIME_METHODS := [ALLOC_STACK,writeAsciiToMemory,intArrayFromString,UTF8ToString,stringToUTF8]
|
|
||||||
JS_EXTERNS := javascript/externs.js
|
|
||||||
|
|
||||||
PUBLIC_HEADERS := include/olm/olm.h include/olm/outbound_group_session.h include/olm/inbound_group_session.h include/olm/pk.h include/olm/sas.h include/olm/error.h include/olm/olm_export.h
|
PUBLIC_HEADERS := include/olm/olm.h include/olm/outbound_group_session.h include/olm/inbound_group_session.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 fuzzing/fuzzers/fuzz_*.cpp) $(wildcard fuzzing/fuzzers/fuzz_*.c)
|
FUZZER_SOURCES := $(wildcard fuzzers/fuzz_*.cpp) $(wildcard 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_ASAN_OBJECTS := $(addprefix $(BUILD_DIR)/fuzzers/objects/,$(addprefix asan_,$(OBJECTS)))
|
FUZZER_BINARIES := $(addprefix $(BUILD_DIR)/,$(basename $(FUZZER_SOURCES)))
|
||||||
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.
|
|
||||||
# They are injected inside the modularised code and
|
|
||||||
# processed by the optimiser.
|
|
||||||
JS_PRE := $(wildcard javascript/*pre.js)
|
JS_PRE := $(wildcard javascript/*pre.js)
|
||||||
JS_POST := javascript/olm_outbound_group_session.js \
|
JS_POST := javascript/olm_outbound_group_session.js \
|
||||||
javascript/olm_inbound_group_session.js \
|
javascript/olm_inbound_group_session.js \
|
||||||
javascript/olm_pk.js \
|
|
||||||
javascript/olm_sas.js \
|
|
||||||
javascript/olm_post.js
|
javascript/olm_post.js
|
||||||
|
|
||||||
# The prefix & suffix are just added onto the start & end
|
|
||||||
# of what comes out emcc, so are outside of the modularised
|
|
||||||
# code and not seen by the opimiser.
|
|
||||||
JS_PREFIX := javascript/olm_prefix.js
|
|
||||||
JS_SUFFIX := javascript/olm_suffix.js
|
|
||||||
|
|
||||||
DOCS := tracing/README.html \
|
DOCS := tracing/README.html \
|
||||||
docs/megolm.html \
|
docs/megolm.html \
|
||||||
docs/olm.html \
|
docs/olm.html \
|
||||||
|
@ -86,98 +55,50 @@ 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
|
CFLAGS += -Wall -Werror -std=c99 -fPIC
|
||||||
CXXFLAGS += -Wall -Werror -std=c++11
|
CXXFLAGS += -Wall -Werror -std=c++11 -fPIC
|
||||||
LDFLAGS += -Wall -Werror
|
LDFLAGS += -Wall -Werror
|
||||||
|
|
||||||
CFLAGS_NATIVE = -fPIC
|
EMCCFLAGS = --closure 1 --memory-init-file 0 -s NO_FILESYSTEM=1 -s INVOKE_RUN=0
|
||||||
CXXFLAGS_NATIVE = -fPIC
|
# NO_BROWSER is kept for compatibility with emscripten 1.35.24, but is no
|
||||||
|
# longer needed.
|
||||||
|
EMCCFLAGS += -s NO_BROWSER=1
|
||||||
|
|
||||||
EMCCFLAGS = --closure 1 --memory-init-file 0 -s NO_FILESYSTEM=1 -s INVOKE_RUN=0 -s MODULARIZE=1 -Wno-error=closure
|
EMCC.c = $(EMCC) $(CFLAGS) $(CPPFLAGS) -c
|
||||||
|
EMCC.cc = $(EMCC) $(CXXFLAGS) $(CPPFLAGS) -c
|
||||||
# Olm generally doesn't need a lot of memory to encrypt / decrypt its usual
|
|
||||||
# payloads (ie. Matrix messages), but we do need about 128K of heap to encrypt
|
|
||||||
# a 64K event (enough to store the ciphertext and the plaintext, bearing in
|
|
||||||
# mind that the plaintext can only be 48K because base64). We also have about
|
|
||||||
# 36K of statics. So let's have 256K of memory.
|
|
||||||
# (This can't be changed by the app with wasm since it's baked into the wasm).
|
|
||||||
# (emscripten also mandates at least 16MB of memory for asm.js now, so
|
|
||||||
# we don't use this for the legacy build.)
|
|
||||||
EMCCFLAGS_WASM += -s TOTAL_STACK=65536 -s TOTAL_MEMORY=262144 -s ALLOW_MEMORY_GROWTH
|
|
||||||
|
|
||||||
EMCCFLAGS_ASMJS += -s WASM=0
|
|
||||||
|
|
||||||
EMCC.c = $(EMCC) $(CFLAGS) $(CPPFLAGS) -c -DNDEBUG -DOLM_STATIC_DEFINE=1
|
|
||||||
EMCC.cc = $(EMCC) $(CXXFLAGS) $(CPPFLAGS) -c -DNDEBUG -DOLM_STATIC_DEFINE=1
|
|
||||||
EMCC_LINK = $(EMCC) $(LDFLAGS) $(EMCCFLAGS)
|
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) $(CFLAGS_NATIVE)
|
$(RELEASE_OBJECTS): CFLAGS += $(RELEASE_OPTIMIZE_FLAGS)
|
||||||
$(RELEASE_OBJECTS): CXXFLAGS += $(RELEASE_OPTIMIZE_FLAGS) $(CXXFLAGS_NATIVE)
|
$(RELEASE_OBJECTS): CXXFLAGS += $(RELEASE_OPTIMIZE_FLAGS)
|
||||||
$(RELEASE_TARGET): LDFLAGS += $(RELEASE_OPTIMIZE_FLAGS)
|
$(RELEASE_TARGET): LDFLAGS += $(RELEASE_OPTIMIZE_FLAGS)
|
||||||
|
|
||||||
$(DEBUG_OBJECTS): CFLAGS += $(DEBUG_OPTIMIZE_FLAGS) $(CFLAGS_NATIVE)
|
$(DEBUG_OBJECTS): CFLAGS += $(DEBUG_OPTIMIZE_FLAGS)
|
||||||
$(DEBUG_OBJECTS): CXXFLAGS += $(DEBUG_OPTIMIZE_FLAGS) $(CXXFLAGS_NATIVE)
|
$(DEBUG_OBJECTS): CXXFLAGS += $(DEBUG_OPTIMIZE_FLAGS)
|
||||||
$(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) -D OLM_FUZZING=1
|
$(FUZZER_OBJECTS): CFLAGS += $(FUZZER_OPTIMIZE_FLAGS)
|
||||||
$(FUZZER_OBJECTS): CXXFLAGS += $(FUZZER_OPTIMIZE_FLAGS) -D OLM_FUZZING=1
|
$(FUZZER_OBJECTS): CXXFLAGS += $(FUZZER_OPTIMIZE_FLAGS)
|
||||||
$(FUZZER_DEBUG_OBJECTS): CFLAGS += $(DEBUG_OPTIMIZE_FLAGS) $(CFLAGS_NATIVE) -D OLM_FUZZING=1
|
$(FUZZER_BINARIES): CPPFLAGS += -Ifuzzers/include
|
||||||
$(FUZZER_DEBUG_OBJECTS): CXXFLAGS += $(DEBUG_OPTIMIZE_FLAGS) $(CXXFLAGS_NATIVE) -D OLM_FUZZING=1
|
$(FUZZER_BINARIES): LDFLAGS += $(FUZZER_OPTIMIZE_FLAGS) -L$(BUILD_DIR)
|
||||||
$(FUZZER_ASAN_OBJECTS): CFLAGS += $(FUZZER_OPTIMIZE_FLAGS) -D OLM_FUZZING=1
|
$(FUZZER_DEBUG_BINARIES): CPPFLAGS += -Ifuzzers/include
|
||||||
$(FUZZER_ASAN_OBJECTS): CXXFLAGS += $(FUZZER_OPTIMIZE_FLAGS) -D OLM_FUZZING=1
|
$(FUZZER_DEBUG_BINARIES): LDFLAGS += $(DEBUG_OPTIMIZE_FLAGS)
|
||||||
$(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)
|
||||||
$(JS_WASM_TARGET): LDFLAGS += $(JS_OPTIMIZE_FLAGS)
|
$(JS_TARGET): LDFLAGS += $(JS_OPTIMIZE_FLAGS)
|
||||||
$(JS_ASMJS_TARGET): LDFLAGS += $(JS_OPTIMIZE_FLAGS)
|
|
||||||
|
|
||||||
### Fix to make mkdir work on windows and linux
|
|
||||||
ifeq ($(shell echo "check_quotes"),"check_quotes")
|
|
||||||
WINDOWS := yes
|
|
||||||
else
|
|
||||||
WINDOWS := no
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(WINDOWS),yes)
|
|
||||||
mkdir = mkdir $(subst /,\,$(1)) > nul 2>&1 || (exit 0)
|
|
||||||
else
|
|
||||||
mkdir = mkdir -p $(1)
|
|
||||||
endif
|
|
||||||
|
|
||||||
### top-level targets
|
### top-level targets
|
||||||
|
|
||||||
|
@ -185,69 +106,31 @@ 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) \
|
-Wl,-soname,libolm.so.$(MAJOR) \
|
||||||
|
-Wl,--version-script,version_script.ver \
|
||||||
$(OUTPUT_OPTION) $(RELEASE_OBJECTS)
|
$(OUTPUT_OPTION) $(RELEASE_OBJECTS)
|
||||||
ln -sf libolm.$(SO).$(VERSION) $(BUILD_DIR)/libolm.$(SO).$(MAJOR)
|
ln -sf libolm.so.$(VERSION) $(BUILD_DIR)/libolm.so.$(MAJOR)
|
||||||
ln -sf libolm.$(SO).$(VERSION) $(BUILD_DIR)/libolm.$(SO)
|
|
||||||
|
|
||||||
debug: $(DEBUG_TARGET)
|
debug: $(DEBUG_TARGET)
|
||||||
.PHONY: debug
|
.PHONY: debug
|
||||||
|
|
||||||
$(DEBUG_TARGET): $(DEBUG_OBJECTS)
|
$(DEBUG_TARGET): $(DEBUG_OBJECTS)
|
||||||
$(CXX) $(LDFLAGS) --shared -fPIC \
|
$(CXX) $(LDFLAGS) --shared -fPIC \
|
||||||
$(OLM_LDFLAGS) \
|
-Wl,-soname,libolm_debug.so.$(MAJOR) \
|
||||||
|
-Wl,--version-script,version_script.ver \
|
||||||
$(OUTPUT_OPTION) $(DEBUG_OBJECTS)
|
$(OUTPUT_OPTION) $(DEBUG_OBJECTS)
|
||||||
ln -sf libolm_debug.$(SO).$(VERSION) $(BUILD_DIR)/libolm_debug.$(SO).$(MAJOR)
|
ln -sf libolm_debug.so.$(VERSION) $(BUILD_DIR)/libolm_debug.so.$(MAJOR)
|
||||||
|
|
||||||
static: $(STATIC_RELEASE_TARGET)
|
js: $(JS_TARGET)
|
||||||
.PHONY: static
|
|
||||||
|
|
||||||
$(STATIC_RELEASE_TARGET): $(RELEASE_OBJECTS)
|
|
||||||
$(AR) rcs $@ $^
|
|
||||||
|
|
||||||
js: $(JS_WASM_TARGET) $(JS_ASMJS_TARGET)
|
|
||||||
.PHONY: js
|
.PHONY: js
|
||||||
|
|
||||||
wasm: $(WASM_TARGET)
|
$(JS_TARGET): $(JS_OBJECTS) $(JS_PRE) $(JS_POST) $(JS_EXPORTED_FUNCTIONS)
|
||||||
.PHONY: wasm
|
$(EMCC_LINK) \
|
||||||
|
|
||||||
$(WASM_TARGET): $(WASM_OBJECTS)
|
|
||||||
$(EMAR) rcs $@ $^
|
|
||||||
|
|
||||||
javascript/olm_prefix.js: javascript/olm_prefix.js.in Makefile common.mk
|
|
||||||
sed s/@VERSION@/$(VERSION)/ javascript/olm_prefix.js.in > $@
|
|
||||||
|
|
||||||
# Note that the output file we give to emcc determines the name of the
|
|
||||||
# wasm file baked into the js, hence messing around outputting to olm.js
|
|
||||||
# and then renaming it.
|
|
||||||
$(JS_WASM_TARGET): $(JS_OBJECTS) $(JS_PRE) $(JS_POST) $(JS_EXPORTED_FUNCTIONS) $(JS_PREFIX) $(JS_SUFFIX)
|
|
||||||
EMCC_CLOSURE_ARGS="--externs $(CURDIR)/$(JS_EXTERNS)" $(EMCC_LINK) \
|
|
||||||
$(EMCCFLAGS_WASM) \
|
|
||||||
$(foreach f,$(JS_PRE),--pre-js $(f)) \
|
$(foreach f,$(JS_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 "EXPORTED_RUNTIME_METHODS=$(JS_EXPORTED_RUNTIME_METHODS)" \
|
$(JS_OBJECTS) -o $@
|
||||||
-o $@ $(JS_OBJECTS)
|
|
||||||
|
|
||||||
$(JS_ASMJS_TARGET): $(JS_OBJECTS) $(JS_PRE) $(JS_POST) $(JS_EXPORTED_FUNCTIONS) $(JS_PREFIX) $(JS_SUFFIX)
|
|
||||||
EMCC_CLOSURE_ARGS="--externs $(CURDIR)/$(JS_EXTERNS)" $(EMCC_LINK) \
|
|
||||||
$(EMCCFLAGS_ASMJS) \
|
|
||||||
$(foreach f,$(JS_PRE),--pre-js $(f)) \
|
|
||||||
$(foreach f,$(JS_POST),--post-js $(f)) \
|
|
||||||
$(foreach f,$(JS_PREFIX),--extern-pre-js $(f)) \
|
|
||||||
$(foreach f,$(JS_SUFFIX),--extern-post-js $(f)) \
|
|
||||||
-s "EXPORTED_FUNCTIONS=@$(JS_EXPORTED_FUNCTIONS)" \
|
|
||||||
-s "EXPORTED_RUNTIME_METHODS=$(JS_EXPORTED_RUNTIME_METHODS)" \
|
|
||||||
-o $@ $(JS_OBJECTS)
|
|
||||||
|
|
||||||
build_tests: $(TEST_BINARIES)
|
build_tests: $(TEST_BINARIES)
|
||||||
|
|
||||||
|
@ -257,39 +140,33 @@ test: build_tests
|
||||||
$$i || exit $$?; \
|
$$i || exit $$?; \
|
||||||
done
|
done
|
||||||
|
|
||||||
test_mem: build_tests
|
fuzzers: $(FUZZER_BINARIES) $(FUZZER_DEBUG_BINARIES)
|
||||||
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)
|
||||||
./exports.py $^ > $@.tmp
|
perl -MJSON -ne '$$f{"_$$1"}=1 if /(olm_[^( ]*)\(/; END { @f=sort keys %f; print encode_json \@f }' $^ > $@.tmp
|
||||||
mv $@.tmp $@
|
mv $@.tmp $@
|
||||||
|
|
||||||
all: test js lib debug doc
|
all: test js lib debug doc
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
|
|
||||||
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 || mkdir -p $(DESTDIR)$(PREFIX)/include/olm
|
||||||
install $(PUBLIC_HEADERS) $(DESTDIR)$(PREFIX)/include/olm/
|
install -Dm644 $(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 || mkdir -p $(DESTDIR)$(PREFIX)/lib
|
||||||
install $(DEBUG_TARGET) $(DESTDIR)$(PREFIX)/lib/libolm_debug.$(SO).$(VERSION)
|
install -Dm755 $(DEBUG_TARGET) $(DESTDIR)$(PREFIX)/lib/libolm_debug.so.$(VERSION)
|
||||||
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.$(MAJOR)
|
||||||
ln -sf libolm_debug.$(SO).$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm_debug.$(SO)
|
ln -s 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 || mkdir -p $(DESTDIR)$(PREFIX)/lib
|
||||||
install $(RELEASE_TARGET) $(DESTDIR)$(PREFIX)/lib/libolm.$(SO).$(VERSION)
|
install -Dm755 $(RELEASE_TARGET) $(DESTDIR)$(PREFIX)/lib/libolm.so.$(VERSION)
|
||||||
ln -sf libolm.$(SO).$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm.$(SO).$(MAJOR)
|
ln -s libolm.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm.so.$(MAJOR)
|
||||||
ln -sf libolm.$(SO).$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm.$(SO)
|
ln -s libolm.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm.so
|
||||||
.PHONY: install
|
.PHONY: install
|
||||||
|
|
||||||
clean:;
|
clean:;
|
||||||
|
@ -301,108 +178,60 @@ doc: $(DOCS)
|
||||||
|
|
||||||
### rules for building objects
|
### rules for building objects
|
||||||
$(BUILD_DIR)/release/%.o: %.c
|
$(BUILD_DIR)/release/%.o: %.c
|
||||||
$(call mkdir,$(dir $@))
|
mkdir -p $(dir $@)
|
||||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
$(BUILD_DIR)/release/%.o: %.cpp
|
$(BUILD_DIR)/release/%.o: %.cpp
|
||||||
$(call mkdir,$(dir $@))
|
mkdir -p $(dir $@)
|
||||||
$(COMPILE.cc) $(OUTPUT_OPTION) $<
|
$(COMPILE.cc) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
$(BUILD_DIR)/debug/%.o: %.c
|
$(BUILD_DIR)/debug/%.o: %.c
|
||||||
$(call mkdir,$(dir $@))
|
mkdir -p $(dir $@)
|
||||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
$(BUILD_DIR)/debug/%.o: %.cpp
|
$(BUILD_DIR)/debug/%.o: %.cpp
|
||||||
$(call mkdir,$(dir $@))
|
mkdir -p $(dir $@)
|
||||||
$(COMPILE.cc) $(OUTPUT_OPTION) $<
|
$(COMPILE.cc) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
$(BUILD_DIR)/javascript/%.o: %.c
|
$(BUILD_DIR)/javascript/%.o: %.c
|
||||||
$(call mkdir,$(dir $@))
|
mkdir -p $(dir $@)
|
||||||
$(EMCC.c) $(OUTPUT_OPTION) $<
|
$(EMCC.c) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
$(BUILD_DIR)/javascript/%.o: %.cpp
|
$(BUILD_DIR)/javascript/%.o: %.cpp
|
||||||
$(call mkdir,$(dir $@))
|
mkdir -p $(dir $@)
|
||||||
$(EMCC.cc) $(OUTPUT_OPTION) $<
|
|
||||||
|
|
||||||
$(BUILD_DIR)/wasm/%.o: %.c
|
|
||||||
$(call mkdir,$(dir $@))
|
|
||||||
$(EMCC.c) $(OUTPUT_OPTION) $<
|
|
||||||
|
|
||||||
$(BUILD_DIR)/wasm/%.o: %.cpp
|
|
||||||
$(call mkdir,$(dir $@))
|
|
||||||
$(EMCC.cc) $(OUTPUT_OPTION) $<
|
$(EMCC.cc) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
$(BUILD_DIR)/tests/%: tests/%.c $(DEBUG_OBJECTS)
|
$(BUILD_DIR)/tests/%: tests/%.c $(DEBUG_OBJECTS)
|
||||||
$(call mkdir,$(dir $@))
|
mkdir -p $(dir $@)
|
||||||
$(LINK.c) -o $@ $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS)
|
$(LINK.c) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
|
||||||
|
|
||||||
$(BUILD_DIR)/tests/%: tests/%.cpp $(DEBUG_OBJECTS)
|
$(BUILD_DIR)/tests/%: tests/%.cpp $(DEBUG_OBJECTS)
|
||||||
$(call mkdir,$(dir $@))
|
mkdir -p $(dir $@)
|
||||||
$(LINK.cc) -o $@ $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS)
|
$(LINK.cc) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
|
||||||
|
|
||||||
$(BUILD_DIR)/fuzzers/objects/%.o: %.c
|
$(BUILD_DIR)/fuzzers/objects/%.o: %.c
|
||||||
$(call mkdir,$(dir $@))
|
mkdir -p $(dir $@)
|
||||||
$(AFL.c) $(OUTPUT_OPTION) $<
|
$(AFL.c) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
$(BUILD_DIR)/fuzzers/objects/%.o: %.cpp
|
$(BUILD_DIR)/fuzzers/objects/%.o: %.cpp
|
||||||
$(call mkdir,$(dir $@))
|
mkdir -p $(dir $@)
|
||||||
$(AFL.cc) $(OUTPUT_OPTION) $<
|
$(AFL.cc) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
$(BUILD_DIR)/fuzzers/objects/asan_%.o: %.c
|
$(BUILD_DIR)/fuzzers/fuzz_%: fuzzers/fuzz_%.c $(FUZZER_OBJECTS)
|
||||||
$(call mkdir,$(dir $@))
|
$(AFL_LINK.c) $< $(FUZZER_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
|
||||||
$(AFL_ASAN.c) $(OUTPUT_OPTION) $<
|
|
||||||
|
|
||||||
$(BUILD_DIR)/fuzzers/objects/asan_%.o: %.cpp
|
$(BUILD_DIR)/fuzzers/fuzz_%: fuzzers/fuzz_%.cpp $(FUZZER_OBJECTS)
|
||||||
$(call mkdir,$(dir $@))
|
$(AFL_LINK.cc) $< $(FUZZER_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
|
||||||
$(AFL_ASAN.cc) $(OUTPUT_OPTION) $<
|
|
||||||
|
|
||||||
$(BUILD_DIR)/fuzzers/objects/msan_%.o: %.c
|
$(BUILD_DIR)/fuzzers/debug_%: fuzzers/fuzz_%.c $(DEBUG_OBJECTS)
|
||||||
$(call mkdir,$(dir $@))
|
$(LINK.c) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
|
||||||
$(AFL_MSAN.c) $(OUTPUT_OPTION) $<
|
|
||||||
|
|
||||||
$(BUILD_DIR)/fuzzers/objects/msan_%.o: %.cpp
|
$(BUILD_DIR)/fuzzers/debug_%: fuzzers/fuzz_%.cpp $(DEBUG_OBJECTS)
|
||||||
$(call mkdir,$(dir $@))
|
$(LINK.cc) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@
|
||||||
$(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)
|
||||||
|
@ -410,10 +239,5 @@ $(BUILD_DIR)/fuzzers/fuzz_%_msan: fuzzing/fuzzers/fuzz_%.cpp $(FUZZER_MSAN_OBJEC
|
||||||
-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)
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
Pod::Spec.new do |s|
|
|
||||||
|
|
||||||
# The libolm version
|
|
||||||
MAJOR = 3
|
|
||||||
MINOR = 2
|
|
||||||
PATCH = 16
|
|
||||||
|
|
||||||
s.name = "OLMKit"
|
|
||||||
s.version = "#{MAJOR}.#{MINOR}.#{PATCH}"
|
|
||||||
s.summary = "An Objective-C wrapper of olm (http://matrix.org/git/olm)"
|
|
||||||
|
|
||||||
s.description = <<-DESC
|
|
||||||
olm is an implementation of the Double Ratchet cryptographic ratchet in C++
|
|
||||||
DESC
|
|
||||||
|
|
||||||
s.homepage = "https://gitlab.matrix.org/matrix-org/olm"
|
|
||||||
|
|
||||||
s.license = { :type => "Apache License, Version 2.0", :file => "LICENSE" }
|
|
||||||
|
|
||||||
s.authors = { "Chris Ballinger" => "chrisballinger@gmail.com",
|
|
||||||
"matrix.org" => "support@matrix.org" }
|
|
||||||
|
|
||||||
s.ios.deployment_target = "6.0"
|
|
||||||
s.osx.deployment_target = "10.9"
|
|
||||||
|
|
||||||
# Expose the Objective-C wrapper API of libolm
|
|
||||||
s.public_header_files = "xcode/OLMKit/*.h"
|
|
||||||
|
|
||||||
s.source = {
|
|
||||||
:git => "https://gitlab.matrix.org/matrix-org/olm.git",
|
|
||||||
:tag => s.version.to_s
|
|
||||||
}
|
|
||||||
|
|
||||||
s.source_files = "xcode/OLMKit/*.{h,m}", "include/**/*.{h,hh}", "src/*.{c,cpp}", "lib/crypto-algorithms/sha256.c", "lib/crypto-algorithms/aes.c", "lib/curve25519-donna/curve25519-donna.c"
|
|
||||||
s.private_header_files = "xcode/OLMKit/*_Private.h"
|
|
||||||
|
|
||||||
# Those files (including .c) are included by ed25519.c. We do not want to compile them twice
|
|
||||||
s.preserve_paths = "lib/ed25519/**/*.{h,c}"
|
|
||||||
|
|
||||||
s.library = "c++"
|
|
||||||
|
|
||||||
|
|
||||||
# Use the same compiler options for C and C++ as olm/Makefile
|
|
||||||
|
|
||||||
s.compiler_flags = "-g -O3 -DOLMLIB_VERSION_MAJOR=#{MAJOR} -DOLMLIB_VERSION_MINOR=#{MINOR} -DOLMLIB_VERSION_PATCH=#{PATCH}"
|
|
||||||
|
|
||||||
# For headers search paths, manage first the normal installation. Then, use paths used
|
|
||||||
# when the pod is local
|
|
||||||
s.xcconfig = {
|
|
||||||
'USER_HEADER_SEARCH_PATHS' =>"${PODS_ROOT}/OLMKit/include ${PODS_ROOT}/OLMKit/lib #{File.join(File.dirname(__FILE__), 'include')} #{File.join(File.dirname(__FILE__), 'lib')}"
|
|
||||||
}
|
|
||||||
|
|
||||||
s.subspec 'olmc' do |olmc|
|
|
||||||
olmc.source_files = "src/*.{c}", "lib/curve25519-donna.h", "lib/crypto-algorithms/sha256.{h,c}", "lib/crypto-algorithms/aes.{h,c}", "lib/curve25519-donna/curve25519-donna.c"
|
|
||||||
olmc.compiler_flags = ' -std=c99 -fPIC'
|
|
||||||
end
|
|
||||||
|
|
||||||
s.subspec 'olmcpp' do |olmcpp|
|
|
||||||
olmcpp.source_files = "src/*.{cpp}"
|
|
||||||
olmcpp.compiler_flags = ' -std=c++11 -fPIC'
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
|
@ -1,52 +0,0 @@
|
||||||
// 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
328
README.md
|
@ -1,328 +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.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.
|
|
114
README.rst
Normal file
114
README.rst
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
Olm
|
||||||
|
===
|
||||||
|
|
||||||
|
An implementation of the Double Ratchet cryptographic ratchet described by
|
||||||
|
https://github.com/trevp/double_ratchet/wiki, written in C and C++11 and
|
||||||
|
exposed as a C API.
|
||||||
|
|
||||||
|
The specification of the Olm ratchet can be found in ``docs/olm.rst`` or
|
||||||
|
https://matrix.org/docs/spec/olm.html.
|
||||||
|
|
||||||
|
This library also includes an implementation of the Megolm cryptographic
|
||||||
|
ratchet, as specified in ``docs/megolm.rst`` or
|
||||||
|
https://matrix.org/docs/spec/megolm.html.
|
||||||
|
|
||||||
|
Building
|
||||||
|
--------
|
||||||
|
|
||||||
|
To build olm as a shared library run:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
make
|
||||||
|
|
||||||
|
To run the tests run:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
make test
|
||||||
|
|
||||||
|
To build the javascript bindings, install emscripten from http://kripken.github.io/emscripten-site/ and then run:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
make js
|
||||||
|
|
||||||
|
Release process
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
# Bump version numbers in ``Makefile`` and ``javascript/package.json``
|
||||||
|
# Prepare changelog
|
||||||
|
git commit
|
||||||
|
make clean
|
||||||
|
make test
|
||||||
|
make js
|
||||||
|
npm pack javascript
|
||||||
|
VERSION=x.y.z
|
||||||
|
scp olm-$VERSION.tgz packages@ldc-prd-matrix-001:/sites/matrix/packages/npm/olm/
|
||||||
|
git tag $VERSION -s
|
||||||
|
git push --tags
|
||||||
|
|
||||||
|
It's probably sensible to do the above on a release branch (``release-vx.y.z``
|
||||||
|
by convention), and merge back to master once complete.
|
||||||
|
|
||||||
|
|
||||||
|
Design
|
||||||
|
------
|
||||||
|
|
||||||
|
Olm is designed to be easy port to different platforms and to be easy
|
||||||
|
to write bindings for.
|
||||||
|
|
||||||
|
It was originally implemented in C++, with a plain-C layer providing the public
|
||||||
|
API. As development has progressed, it has become clear that C++ gives little
|
||||||
|
advantage, and new functionality is being added in C, with C++ parts being
|
||||||
|
rewritten as the need ariases.
|
||||||
|
|
||||||
|
Error Handling
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
All C functions in the API for olm return ``olm_error()`` on error.
|
||||||
|
This makes it easy to check for error conditions within the language bindings.
|
||||||
|
|
||||||
|
Random Numbers
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Olm doesn't generate random numbers itself. Instead the caller must
|
||||||
|
provide the random data. This makes it easier to port the library to different
|
||||||
|
platforms since the caller can use whatever cryptographic random number
|
||||||
|
generator their platform provides.
|
||||||
|
|
||||||
|
Memory
|
||||||
|
~~~~~~
|
||||||
|
|
||||||
|
Olm avoids calling malloc or allocating memory on the heap itself.
|
||||||
|
Instead the library calculates how much memory will be needed to hold the
|
||||||
|
output and the caller supplies a buffer of the appropriate size.
|
||||||
|
|
||||||
|
Output Encoding
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Binary output is encoded as base64 so that languages that prefer unicode
|
||||||
|
strings will find it easier to handle the output.
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Olm uses pure C implementations of the cryptographic primitives used by
|
||||||
|
the ratchet. While this decreases the performance it makes it much easier
|
||||||
|
to compile the library for different architectures.
|
||||||
|
|
||||||
|
What's an olm?
|
||||||
|
--------------
|
||||||
|
|
||||||
|
It's a really cool species of European troglodytic salamander.
|
||||||
|
http://www.postojnska-jama.eu/en/come-and-visit-us/vivarium-proteus/
|
||||||
|
|
||||||
|
Legal Notice
|
||||||
|
------------
|
||||||
|
|
||||||
|
The software may be subject to the U.S. export control laws and regulations
|
||||||
|
and by downloading the software the user certifies that he/she/it is
|
||||||
|
authorized to do so in accordance with those export control laws and
|
||||||
|
regulations.
|
|
@ -1,29 +0,0 @@
|
||||||
# Cross-compile for Windows (64-bit) using Mingw-w64
|
|
||||||
|
|
||||||
# Build using:
|
|
||||||
# cmake . -Bbuild -DCMAKE_TOOLCHAIN_FILE=Windows64.cmake
|
|
||||||
# cmake --build build
|
|
||||||
|
|
||||||
# from @ticho:cyberdi.sk
|
|
||||||
# https://paste.debian.net/1201338/
|
|
||||||
|
|
||||||
# the name of the target operating system
|
|
||||||
SET(CMAKE_SYSTEM_NAME Windows)
|
|
||||||
|
|
||||||
# which compilers to use for C and C++
|
|
||||||
SET(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc-posix)
|
|
||||||
SET(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++-posix)
|
|
||||||
SET(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
|
|
||||||
|
|
||||||
# here is the target environment located
|
|
||||||
SET(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)
|
|
||||||
|
|
||||||
# adjust the default behaviour of the FIND_XXX() commands:
|
|
||||||
# search headers and libraries in the target environment, search
|
|
||||||
# programs in the host environment
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
|
||||||
|
|
||||||
# static-link against the standard libraries
|
|
||||||
set(CMAKE_CXX_STANDARD_LIBRARIES "-static-libgcc -static-libstdc++")
|
|
22
android/.gitignore
vendored
22
android/.gitignore
vendored
|
@ -1,22 +0,0 @@
|
||||||
#IDEs
|
|
||||||
/.idea
|
|
||||||
*.iml
|
|
||||||
|
|
||||||
#OS
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
#builds
|
|
||||||
/build
|
|
||||||
*.apk
|
|
||||||
|
|
||||||
.gradle
|
|
||||||
/local.properties
|
|
||||||
|
|
||||||
# Native build
|
|
||||||
/olm-sdk/src/main/obj
|
|
||||||
/olm-sdk/doc
|
|
||||||
/olm-sdk/src/main/libs/**/*.so
|
|
||||||
|
|
||||||
# Test
|
|
||||||
/olm-sdk/src/main/libs/**/gdbserver
|
|
||||||
/olm-sdk/src/main/libs/**/gdb.setup
|
|
|
@ -1,46 +0,0 @@
|
||||||
# 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
|
|
|
@ -1,25 +0,0 @@
|
||||||
OlmLibSdk
|
|
||||||
=========
|
|
||||||
|
|
||||||
OlmLibSdk exposes an android wrapper to libolm.
|
|
||||||
|
|
||||||
Installation
|
|
||||||
------------
|
|
||||||
|
|
||||||
Android Olm library is released on MavenCentral.
|
|
||||||
|
|
||||||
Add this dependency to your project:
|
|
||||||
|
|
||||||
```groovy
|
|
||||||
implementation "org.matrix.android:olm:3.2.8"
|
|
||||||
```
|
|
||||||
|
|
||||||
Latest version: ![Latest version](https://img.shields.io/maven-central/v/org.matrix.android/olm)
|
|
||||||
|
|
||||||
Development
|
|
||||||
-----------
|
|
||||||
import the project from the ``android/`` path.
|
|
||||||
|
|
||||||
The project contains some JNI files and some Java wrapper files.
|
|
||||||
|
|
||||||
The project contains some tests under AndroidTests package.
|
|
|
@ -1,38 +0,0 @@
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
|
||||||
|
|
||||||
buildscript {
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
google()
|
|
||||||
}
|
|
||||||
dependencies {
|
|
||||||
// Release notes of Android Gradle Plugin (AGP):
|
|
||||||
// https://developer.android.com/studio/releases/gradle-plugin
|
|
||||||
classpath 'com.android.tools.build:gradle:7.0.4'
|
|
||||||
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.18.0'
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
|
||||||
// in the individual module build.gradle files
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allprojects {
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
google()
|
|
||||||
}
|
|
||||||
|
|
||||||
plugins.withId("com.vanniktech.maven.publish.base") {
|
|
||||||
group = project.getProperties().getOrDefault("GROUP", "0.0.0")
|
|
||||||
version = project.getProperties().getOrDefault("VERSION_NAME", "name")
|
|
||||||
|
|
||||||
mavenPublishing {
|
|
||||||
publishToMavenCentral("S01")
|
|
||||||
pomFromGradleProperties()
|
|
||||||
signAllPublications()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task clean(type: Delete) {
|
|
||||||
delete rootProject.buildDir
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
## Project-wide Gradle settings.
|
|
||||||
#
|
|
||||||
# For more details on how to configure your build environment visit
|
|
||||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
|
||||||
#
|
|
||||||
# Specifies the JVM arguments used for the daemon process.
|
|
||||||
# The setting is particularly useful for tweaking memory settings.
|
|
||||||
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
|
||||||
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
|
||||||
#
|
|
||||||
# When configured, Gradle will run in incubating parallel mode.
|
|
||||||
# This option should only be used with decoupled projects. More details, visit
|
|
||||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
|
||||||
# org.gradle.parallel=true
|
|
||||||
#Wed Oct 05 11:49:34 CEST 2016
|
|
||||||
|
|
||||||
#systemProp.https.proxyPort=8080
|
|
||||||
#systemProp.http.proxyHost=batproxy
|
|
||||||
#systemProp.https.proxyHost=batproxy
|
|
||||||
#systemProp.http.proxyPort=8080
|
|
||||||
|
|
||||||
android.useAndroidX=true
|
|
||||||
org.gradle.configureondemand=false
|
|
||||||
|
|
||||||
# Maven publication
|
|
||||||
# Ref: https://github.com/vanniktech/gradle-maven-publish-plugin
|
|
||||||
GROUP=org.matrix.android
|
|
||||||
POM_ARTIFACT_ID=olm
|
|
||||||
VERSION_NAME=3.2.16
|
|
||||||
|
|
||||||
POM_PACKAGING=aar
|
|
||||||
|
|
||||||
POM_NAME=Olm Android wrapper
|
|
||||||
POM_DESCRIPTION=An Android wrapper to libolm.
|
|
||||||
POM_INCEPTION_YEAR=2021
|
|
||||||
POM_URL=https://gitlab.matrix.org/matrix-org/olm
|
|
||||||
|
|
||||||
POM_LICENSE_NAME=The Apache Software License, Version 2.0
|
|
||||||
POM_LICENCE_URL=https://www.apache.org/licenses/LICENSE-2.0.txt
|
|
||||||
POM_LICENCE_DIST=repo
|
|
||||||
|
|
||||||
POM_SCM_URL=https://gitlab.matrix.org/matrix-org/olm
|
|
||||||
POM_SCM_CONNECTION=scm:git:https://gitlab.matrix.org/matrix-org/olm.git
|
|
||||||
POM_SCM_DEV_CONNECTION=scm:git:ssh://git@gitlab.int.matrix.org:matrix-org/olm.git
|
|
||||||
|
|
||||||
POM_DEVELOPER_ID=matrixdev
|
|
||||||
POM_DEVELOPER_NAME=matrixdev
|
|
||||||
POM_DEVELOPER_URL=https://gitlab.matrix.org/matrix-org
|
|
||||||
POM_DEVELOPER_EMAIL=android@element.io
|
|
BIN
android/gradle/wrapper/gradle-wrapper.jar
vendored
BIN
android/gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
|
@ -1,6 +0,0 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
|
||||||
distributionPath=wrapper/dists
|
|
||||||
distributionSha256Sum=c9490e938b221daf0094982288e4038deed954a3f12fb54cbf270ddf4e37d879
|
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
|
160
android/gradlew
vendored
160
android/gradlew
vendored
|
@ -1,160 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
##
|
|
||||||
## Gradle start up script for UN*X
|
|
||||||
##
|
|
||||||
##############################################################################
|
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
|
||||||
DEFAULT_JVM_OPTS=""
|
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
|
||||||
APP_BASE_NAME=`basename "$0"`
|
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
|
||||||
MAX_FD="maximum"
|
|
||||||
|
|
||||||
warn ( ) {
|
|
||||||
echo "$*"
|
|
||||||
}
|
|
||||||
|
|
||||||
die ( ) {
|
|
||||||
echo
|
|
||||||
echo "$*"
|
|
||||||
echo
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
|
||||||
cygwin=false
|
|
||||||
msys=false
|
|
||||||
darwin=false
|
|
||||||
case "`uname`" in
|
|
||||||
CYGWIN* )
|
|
||||||
cygwin=true
|
|
||||||
;;
|
|
||||||
Darwin* )
|
|
||||||
darwin=true
|
|
||||||
;;
|
|
||||||
MINGW* )
|
|
||||||
msys=true
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
|
||||||
# Resolve links: $0 may be a link
|
|
||||||
PRG="$0"
|
|
||||||
# Need this for relative symlinks.
|
|
||||||
while [ -h "$PRG" ] ; do
|
|
||||||
ls=`ls -ld "$PRG"`
|
|
||||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
|
||||||
if expr "$link" : '/.*' > /dev/null; then
|
|
||||||
PRG="$link"
|
|
||||||
else
|
|
||||||
PRG=`dirname "$PRG"`"/$link"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
SAVED="`pwd`"
|
|
||||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
|
||||||
APP_HOME="`pwd -P`"
|
|
||||||
cd "$SAVED" >/dev/null
|
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
|
||||||
if [ -n "$JAVA_HOME" ] ; then
|
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
|
||||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
|
||||||
else
|
|
||||||
JAVACMD="$JAVA_HOME/bin/java"
|
|
||||||
fi
|
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
|
||||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
location of your Java installation."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
JAVACMD="java"
|
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
location of your Java installation."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
|
||||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
|
||||||
MAX_FD_LIMIT=`ulimit -H -n`
|
|
||||||
if [ $? -eq 0 ] ; then
|
|
||||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
|
||||||
MAX_FD="$MAX_FD_LIMIT"
|
|
||||||
fi
|
|
||||||
ulimit -n $MAX_FD
|
|
||||||
if [ $? -ne 0 ] ; then
|
|
||||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Darwin, add options to specify how the application appears in the dock
|
|
||||||
if $darwin; then
|
|
||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Cygwin, switch paths to Windows format before running java
|
|
||||||
if $cygwin ; then
|
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
|
||||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
|
||||||
SEP=""
|
|
||||||
for dir in $ROOTDIRSRAW ; do
|
|
||||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
|
||||||
SEP="|"
|
|
||||||
done
|
|
||||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
|
||||||
# Add a user-defined pattern to the cygpath arguments
|
|
||||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
|
||||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
|
||||||
fi
|
|
||||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
|
||||||
i=0
|
|
||||||
for arg in "$@" ; do
|
|
||||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
|
||||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
|
||||||
|
|
||||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
|
||||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
|
||||||
else
|
|
||||||
eval `echo args$i`="\"$arg\""
|
|
||||||
fi
|
|
||||||
i=$((i+1))
|
|
||||||
done
|
|
||||||
case $i in
|
|
||||||
(0) set -- ;;
|
|
||||||
(1) set -- "$args0" ;;
|
|
||||||
(2) set -- "$args0" "$args1" ;;
|
|
||||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
|
||||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
|
||||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
|
||||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
|
||||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
|
||||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
|
||||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
|
||||||
function splitJvmOpts() {
|
|
||||||
JVM_OPTS=("$@")
|
|
||||||
}
|
|
||||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
|
||||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
|
||||||
|
|
||||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
|
90
android/gradlew.bat
vendored
90
android/gradlew.bat
vendored
|
@ -1,90 +0,0 @@
|
||||||
@if "%DEBUG%" == "" @echo off
|
|
||||||
@rem ##########################################################################
|
|
||||||
@rem
|
|
||||||
@rem Gradle startup script for Windows
|
|
||||||
@rem
|
|
||||||
@rem ##########################################################################
|
|
||||||
|
|
||||||
@rem Set local scope for the variables with windows NT shell
|
|
||||||
if "%OS%"=="Windows_NT" setlocal
|
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
|
||||||
set DEFAULT_JVM_OPTS=
|
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
|
||||||
set APP_BASE_NAME=%~n0
|
|
||||||
set APP_HOME=%DIRNAME%
|
|
||||||
|
|
||||||
@rem Find java.exe
|
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
|
||||||
|
|
||||||
set JAVA_EXE=java.exe
|
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
|
||||||
if "%ERRORLEVEL%" == "0" goto init
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
|
||||||
echo.
|
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
echo location of your Java installation.
|
|
||||||
|
|
||||||
goto fail
|
|
||||||
|
|
||||||
:findJavaFromJavaHome
|
|
||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
|
||||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto init
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
|
||||||
echo.
|
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
echo location of your Java installation.
|
|
||||||
|
|
||||||
goto fail
|
|
||||||
|
|
||||||
:init
|
|
||||||
@rem Get command-line arguments, handling Windowz variants
|
|
||||||
|
|
||||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
|
||||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
|
||||||
|
|
||||||
:win9xME_args
|
|
||||||
@rem Slurp the command line arguments.
|
|
||||||
set CMD_LINE_ARGS=
|
|
||||||
set _SKIP=2
|
|
||||||
|
|
||||||
:win9xME_args_slurp
|
|
||||||
if "x%~1" == "x" goto execute
|
|
||||||
|
|
||||||
set CMD_LINE_ARGS=%*
|
|
||||||
goto execute
|
|
||||||
|
|
||||||
:4NT_args
|
|
||||||
@rem Get arguments from the 4NT Shell from JP Software
|
|
||||||
set CMD_LINE_ARGS=%$
|
|
||||||
|
|
||||||
:execute
|
|
||||||
@rem Setup the command line
|
|
||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
|
||||||
|
|
||||||
:end
|
|
||||||
@rem End local scope for the variables with windows NT shell
|
|
||||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
|
||||||
|
|
||||||
:fail
|
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
|
||||||
rem the _cmd.exe /c_ return code!
|
|
||||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
|
||||||
exit /b 1
|
|
||||||
|
|
||||||
:mainEnd
|
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
|
||||||
|
|
||||||
:omega
|
|
|
@ -1,167 +0,0 @@
|
||||||
import org.apache.tools.ant.taskdefs.condition.Os
|
|
||||||
import com.vanniktech.maven.publish.AndroidLibrary
|
|
||||||
import com.vanniktech.maven.publish.JavadocJar
|
|
||||||
|
|
||||||
apply plugin: 'com.android.library'
|
|
||||||
apply plugin: "com.vanniktech.maven.publish.base"
|
|
||||||
|
|
||||||
android {
|
|
||||||
compileSdk 31
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
minSdk 14
|
|
||||||
targetSdk 31
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
||||||
|
|
||||||
buildConfigField "String", "OLM_VERSION", "\"${project.getProperties().getOrDefault("VERSION_NAME", "0.0.0")}\""
|
|
||||||
|
|
||||||
// The following argument makes the Android Test Orchestrator run its
|
|
||||||
// "pm clear" command after each test invocation. This command ensures
|
|
||||||
// that the app's state is completely cleared between tests.
|
|
||||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
|
||||||
|
|
||||||
buildConfigField "String", "OLM_VERSION", "\"${project.getProperties().getOrDefault("VERSION_NAME", "0.0.0")}\""
|
|
||||||
}
|
|
||||||
buildTypes {
|
|
||||||
debug {
|
|
||||||
resValue "string", "git_olm_revision", "\"${gitRevision()}\""
|
|
||||||
resValue "string", "git_olm_revision_unix_date", "\"${gitRevisionUnixDate()}\""
|
|
||||||
resValue "string", "git_olm_revision_date", "\"${gitRevisionDate()}\""
|
|
||||||
}
|
|
||||||
|
|
||||||
release {
|
|
||||||
resValue "string", "git_olm_revision", "\"${gitRevision()}\""
|
|
||||||
resValue "string", "git_olm_revision_unix_date", "\"${gitRevisionUnixDate()}\""
|
|
||||||
resValue "string", "git_olm_revision_date", "\"${gitRevisionDate()}\""
|
|
||||||
|
|
||||||
minifyEnabled false
|
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sourceSets.main {
|
|
||||||
jniLibs.srcDir 'src/main/libs'
|
|
||||||
jni.srcDirs = []
|
|
||||||
}
|
|
||||||
|
|
||||||
task buildJavaDoc(type: Javadoc) {
|
|
||||||
source = android.sourceSets.main.java.srcDirs
|
|
||||||
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
|
|
||||||
destinationDir = file("./doc/")
|
|
||||||
options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PRIVATE
|
|
||||||
failOnError false
|
|
||||||
}
|
|
||||||
|
|
||||||
task ndkBuildNativeRelease(type: Exec, description: 'NDK building..') {
|
|
||||||
println 'ndkBuildNativeRelease starts..'
|
|
||||||
workingDir file('src/main')
|
|
||||||
commandLine getNdkBuildCmd(), 'NDK_DEBUG=0'
|
|
||||||
}
|
|
||||||
|
|
||||||
task ndkBuildNativeDebug(type: Exec, description: 'NDK building..') {
|
|
||||||
println 'ndkBuildNativeDebug starts..'
|
|
||||||
workingDir file('src/main')
|
|
||||||
commandLine getNdkBuildCmd(), 'NDK_DEBUG=1'
|
|
||||||
}
|
|
||||||
|
|
||||||
task cleanNative(type: Exec, description: 'Clean NDK build') {
|
|
||||||
workingDir file('src/main')
|
|
||||||
commandLine getNdkBuildCmd(), 'clean'
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType(JavaCompile) {
|
|
||||||
compileTask ->
|
|
||||||
if (compileTask.name.startsWith('compileDebugJava')) {
|
|
||||||
println 'test compile: Debug'
|
|
||||||
compileTask.dependsOn ndkBuildNativeDebug
|
|
||||||
} else if (compileTask.name.startsWith('compileReleaseJava')) {
|
|
||||||
println 'test compile: Release'
|
|
||||||
compileTask.dependsOn ndkBuildNativeRelease
|
|
||||||
}
|
|
||||||
compileTask.dependsOn buildJavaDoc
|
|
||||||
}
|
|
||||||
|
|
||||||
task androidJavadocs(type: Javadoc) {
|
|
||||||
source = android.sourceSets.main.java.srcDirs
|
|
||||||
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
|
|
||||||
android.libraryVariants.all { variant ->
|
|
||||||
if (variant.name == 'release') {
|
|
||||||
owner.classpath += variant.javaCompileProvider.get().classpath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exclude '**/R.html', '**/R.*.html', '**/index.html'
|
|
||||||
}
|
|
||||||
|
|
||||||
task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
|
|
||||||
archiveClassifier.set('javadoc')
|
|
||||||
from androidJavadocs.destinationDir
|
|
||||||
}
|
|
||||||
|
|
||||||
task androidSourcesJar(type: Jar) {
|
|
||||||
archiveClassifier.set('sources')
|
|
||||||
from android.sourceSets.main.java.srcDirs
|
|
||||||
}
|
|
||||||
|
|
||||||
clean.dependsOn cleanNative
|
|
||||||
|
|
||||||
|
|
||||||
libraryVariants.all { variant ->
|
|
||||||
variant.outputs.each { output ->
|
|
||||||
def outputFile = output.outputFileName
|
|
||||||
if (outputFile != null && outputFile.endsWith('.aar')) {
|
|
||||||
output.outputFileName = outputFile.replace(".aar", "-${version}.aar")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility JavaVersion.VERSION_11
|
|
||||||
targetCompatibility JavaVersion.VERSION_11
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def getNdkFolder() {
|
|
||||||
Properties properties = new Properties()
|
|
||||||
properties.load(project.rootProject.file('local.properties').newDataInputStream())
|
|
||||||
def ndkFolder = properties.getProperty('ndk.dir', null)
|
|
||||||
if (ndkFolder == null)
|
|
||||||
throw new GradleException("NDK location missing. Define it with ndk.dir in the local.properties file")
|
|
||||||
|
|
||||||
return ndkFolder
|
|
||||||
}
|
|
||||||
|
|
||||||
def getNdkBuildCmd() {
|
|
||||||
def ndkBuildCmd = getNdkFolder() + "/ndk-build"
|
|
||||||
if (Os.isFamily(Os.FAMILY_WINDOWS))
|
|
||||||
ndkBuildCmd += ".cmd"
|
|
||||||
|
|
||||||
return ndkBuildCmd
|
|
||||||
}
|
|
||||||
|
|
||||||
def gitRevision() {
|
|
||||||
def cmd = "git rev-parse --short HEAD"
|
|
||||||
return cmd.execute().text.trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
def gitRevisionUnixDate() {
|
|
||||||
def cmd = "git show -s --format=%ct HEAD^{commit}"
|
|
||||||
return cmd.execute().text.trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
def gitRevisionDate() {
|
|
||||||
def cmd = "git show -s --format=%ci HEAD^{commit}"
|
|
||||||
return cmd.execute().text.trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
testImplementation 'junit:junit:4.13.2'
|
|
||||||
androidTestImplementation 'junit:junit:4.13.2'
|
|
||||||
|
|
||||||
androidTestImplementation 'androidx.test:core:1.4.0'
|
|
||||||
androidTestImplementation 'androidx.test:runner:1.4.0'
|
|
||||||
androidTestImplementation 'androidx.test:rules:1.4.0'
|
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
|
||||||
}
|
|
||||||
|
|
||||||
mavenPublishing {
|
|
||||||
configure(new AndroidLibrary(new JavadocJar.Empty(), false))
|
|
||||||
}
|
|
|
@ -1,506 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.olm;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
||||||
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.AfterClass;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
import org.junit.FixMethodOrder;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.MethodSorters;
|
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertNotEquals;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
|
||||||
public class OlmAccountTest {
|
|
||||||
private static final String LOG_TAG = "OlmAccountTest";
|
|
||||||
private static final int GENERATION_ONE_TIME_KEYS_NUMBER = 50;
|
|
||||||
|
|
||||||
private static OlmAccount mOlmAccount;
|
|
||||||
private static OlmManager mOlmManager;
|
|
||||||
private boolean mIsAccountCreated;
|
|
||||||
private final String FILE_NAME = "SerialTestFile";
|
|
||||||
|
|
||||||
@BeforeClass
|
|
||||||
public static void setUpClass() {
|
|
||||||
// load native lib
|
|
||||||
mOlmManager = new OlmManager();
|
|
||||||
|
|
||||||
String olmLibVersion = mOlmManager.getOlmLibVersion();
|
|
||||||
assertNotNull(olmLibVersion);
|
|
||||||
String olmSdkVersion = mOlmManager.getDetailedVersion(ApplicationProvider.getApplicationContext());
|
|
||||||
assertNotNull(olmLibVersion);
|
|
||||||
Log.d(LOG_TAG, "## setUpClass(): Versions - Android Olm SDK = " + olmSdkVersion + " Olm lib =" + olmLibVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterClass
|
|
||||||
public static void tearDownClass() {
|
|
||||||
// TBD
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
if (mIsAccountCreated) {
|
|
||||||
assertNotNull(mOlmAccount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void tearDown() {
|
|
||||||
// TBD
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic test: creation and release.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void test01CreateReleaseAccount() {
|
|
||||||
try {
|
|
||||||
mOlmAccount = new OlmAccount();
|
|
||||||
} catch (OlmException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail("OlmAccount failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
assertNotNull(mOlmAccount);
|
|
||||||
|
|
||||||
mOlmAccount.releaseAccount();
|
|
||||||
assertEquals(0, mOlmAccount.getOlmAccountId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test02CreateAccount() {
|
|
||||||
try {
|
|
||||||
mOlmAccount = new OlmAccount();
|
|
||||||
} catch (OlmException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail("OlmAccount failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
assertNotNull(mOlmAccount);
|
|
||||||
mIsAccountCreated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test04GetOlmAccountId() {
|
|
||||||
long olmNativeInstance = mOlmAccount.getOlmAccountId();
|
|
||||||
Log.d(LOG_TAG, "## testGetOlmAccountId olmNativeInstance=" + olmNativeInstance);
|
|
||||||
assertTrue(0 != olmNativeInstance);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if {@link OlmAccount#identityKeys()} returns a JSON object
|
|
||||||
* that contains the following keys: {@link OlmAccount#JSON_KEY_FINGER_PRINT_KEY}
|
|
||||||
* and {@link OlmAccount#JSON_KEY_IDENTITY_KEY}
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void test05IdentityKeys() {
|
|
||||||
Map<String, String> identityKeys = null;
|
|
||||||
try {
|
|
||||||
identityKeys = mOlmAccount.identityKeys();
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("identityKeys failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
assertNotNull(identityKeys);
|
|
||||||
Log.d(LOG_TAG, "## testIdentityKeys Keys=" + identityKeys);
|
|
||||||
|
|
||||||
// is JSON_KEY_FINGER_PRINT_KEY present?
|
|
||||||
String fingerPrintKey = TestHelper.getFingerprintKey(identityKeys);
|
|
||||||
assertFalse("fingerprint key missing", TextUtils.isEmpty(fingerPrintKey));
|
|
||||||
|
|
||||||
// is JSON_KEY_IDENTITY_KEY present?
|
|
||||||
String identityKey = TestHelper.getIdentityKey(identityKeys);
|
|
||||||
assertFalse("identity key missing", TextUtils.isEmpty(identityKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
//****************************************************
|
|
||||||
//***************** ONE TIME KEYS TESTS **************
|
|
||||||
//****************************************************
|
|
||||||
@Test
|
|
||||||
public void test06MaxOneTimeKeys() {
|
|
||||||
long maxOneTimeKeys = mOlmAccount.maxOneTimeKeys();
|
|
||||||
Log.d(LOG_TAG, "## testMaxOneTimeKeys(): maxOneTimeKeys=" + maxOneTimeKeys);
|
|
||||||
|
|
||||||
assertTrue(maxOneTimeKeys > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test one time keys generation.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void test07GenerateOneTimeKeys() {
|
|
||||||
String error = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
mOlmAccount.generateOneTimeKeys(GENERATION_ONE_TIME_KEYS_NUMBER);
|
|
||||||
} catch (Exception e) {
|
|
||||||
error = e.getMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNull(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test the generated amount of one time keys = GENERATION_ONE_TIME_KEYS_NUMBER.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void test08OneTimeKeysJsonFormat() {
|
|
||||||
int oneTimeKeysCount = 0;
|
|
||||||
Map<String, Map<String, String>> oneTimeKeysJson = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
oneTimeKeysJson = mOlmAccount.oneTimeKeys();
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
assertNotNull(oneTimeKeysJson);
|
|
||||||
|
|
||||||
try {
|
|
||||||
Map<String, String> map = oneTimeKeysJson.get(OlmAccount.JSON_KEY_ONE_TIME_KEY);
|
|
||||||
assertNotNull(OlmAccount.JSON_KEY_ONE_TIME_KEY + " object is missing", map);
|
|
||||||
|
|
||||||
// test the count of the generated one time keys:
|
|
||||||
oneTimeKeysCount = map.size();
|
|
||||||
|
|
||||||
assertEquals("Expected count=" + GENERATION_ONE_TIME_KEYS_NUMBER + " found=" + oneTimeKeysCount, GENERATION_ONE_TIME_KEYS_NUMBER, oneTimeKeysCount);
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("Exception MSg=" + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test10RemoveOneTimeKeysForSession() {
|
|
||||||
OlmSession olmSession = null;
|
|
||||||
try {
|
|
||||||
olmSession = new OlmSession();
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail("Exception Msg=" + e.getMessage());
|
|
||||||
}
|
|
||||||
long sessionId = olmSession.getOlmSessionId();
|
|
||||||
assertTrue(0 != sessionId);
|
|
||||||
|
|
||||||
String errorMessage = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
mOlmAccount.removeOneTimeKeys(olmSession);
|
|
||||||
} catch (Exception e) {
|
|
||||||
errorMessage = e.getMessage();
|
|
||||||
}
|
|
||||||
assertNotNull(errorMessage);
|
|
||||||
|
|
||||||
olmSession.releaseSession();
|
|
||||||
sessionId = olmSession.getOlmSessionId();
|
|
||||||
assertEquals(0, sessionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test11MarkOneTimeKeysAsPublished() {
|
|
||||||
try {
|
|
||||||
mOlmAccount.markOneTimeKeysAsPublished();
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test12SignMessage() {
|
|
||||||
String clearMsg = "String to be signed by olm";
|
|
||||||
String signedMsg = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
signedMsg = mOlmAccount.signMessage(clearMsg);
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNotNull(signedMsg);
|
|
||||||
// additional tests are performed in test01VerifyEd25519Signing()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ********************************************************
|
|
||||||
// ************* SERIALIZATION TEST ***********************
|
|
||||||
// ********************************************************
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test13Serialization() {
|
|
||||||
FileOutputStream fileOutput;
|
|
||||||
ObjectOutputStream objectOutput;
|
|
||||||
OlmAccount accountRef = null;
|
|
||||||
OlmAccount accountDeserial;
|
|
||||||
|
|
||||||
try {
|
|
||||||
accountRef = new OlmAccount();
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
accountRef.generateOneTimeKeys(GENERATION_ONE_TIME_KEYS_NUMBER);
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
// get keys references
|
|
||||||
Map<String, String> identityKeysRef = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
identityKeysRef = accountRef.identityKeys();
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("identityKeys failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, Map<String, String>> oneTimeKeysRef = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
oneTimeKeysRef = accountRef.oneTimeKeys();
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNotNull(identityKeysRef);
|
|
||||||
assertNotNull(oneTimeKeysRef);
|
|
||||||
|
|
||||||
try {
|
|
||||||
Context context = ApplicationProvider.getApplicationContext();
|
|
||||||
//context.getFilesDir();
|
|
||||||
fileOutput = context.openFileOutput(FILE_NAME, Context.MODE_PRIVATE);
|
|
||||||
|
|
||||||
// serialize account
|
|
||||||
objectOutput = new ObjectOutputStream(fileOutput);
|
|
||||||
objectOutput.writeObject(accountRef);
|
|
||||||
objectOutput.flush();
|
|
||||||
objectOutput.close();
|
|
||||||
|
|
||||||
// deserialize account
|
|
||||||
FileInputStream fileInput = context.openFileInput(FILE_NAME);
|
|
||||||
ObjectInputStream objectInput = new ObjectInputStream(fileInput);
|
|
||||||
accountDeserial = (OlmAccount) objectInput.readObject();
|
|
||||||
objectInput.close();
|
|
||||||
assertNotNull(accountDeserial);
|
|
||||||
|
|
||||||
// get de-serialized keys
|
|
||||||
Map<String, String> identityKeysDeserial = accountDeserial.identityKeys();
|
|
||||||
Map<String, Map<String, String>> oneTimeKeysDeserial = accountDeserial.oneTimeKeys();
|
|
||||||
assertNotNull(identityKeysDeserial);
|
|
||||||
assertNotNull(oneTimeKeysDeserial);
|
|
||||||
|
|
||||||
// compare identity keys
|
|
||||||
assertEquals(identityKeysDeserial.toString(), identityKeysRef.toString());
|
|
||||||
|
|
||||||
// compare onetime keys
|
|
||||||
assertEquals(oneTimeKeysDeserial.toString(), oneTimeKeysRef.toString());
|
|
||||||
|
|
||||||
accountRef.releaseAccount();
|
|
||||||
accountDeserial.releaseAccount();
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
Log.e(LOG_TAG, "## test13Serialization(): Exception FileNotFoundException Msg==" + e.getMessage());
|
|
||||||
fail("test13Serialization failed " + e.getMessage());
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
Log.e(LOG_TAG, "## test13Serialization(): Exception ClassNotFoundException Msg==" + e.getMessage());
|
|
||||||
fail("test13Serialization failed " + e.getMessage());
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(LOG_TAG, "## test13Serialization(): Exception IOException Msg==" + e.getMessage());
|
|
||||||
fail("test13Serialization failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
/*catch (OlmException e) {
|
|
||||||
Log.e(LOG_TAG, "## test13Serialization(): Exception OlmException Msg==" + e.getMessage());
|
|
||||||
}*/ catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## test13Serialization(): Exception Msg==" + e.getMessage());
|
|
||||||
fail("test13Serialization failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ****************************************************
|
|
||||||
// *************** SANITY CHECK TESTS *****************
|
|
||||||
// ****************************************************
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test14GenerateOneTimeKeysError() {
|
|
||||||
// keys number = 0 => no error
|
|
||||||
|
|
||||||
String errorMessage = null;
|
|
||||||
try {
|
|
||||||
mOlmAccount.generateOneTimeKeys(0);
|
|
||||||
} catch (Exception e) {
|
|
||||||
errorMessage = e.getMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNull(errorMessage);
|
|
||||||
|
|
||||||
// keys number = negative value
|
|
||||||
errorMessage = null;
|
|
||||||
try {
|
|
||||||
mOlmAccount.generateOneTimeKeys(-50);
|
|
||||||
} catch (Exception e) {
|
|
||||||
errorMessage = e.getMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNotNull(errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test15RemoveOneTimeKeysForSessionError() {
|
|
||||||
OlmAccount olmAccount = null;
|
|
||||||
try {
|
|
||||||
olmAccount = new OlmAccount();
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
olmAccount.removeOneTimeKeys(null);
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
olmAccount.releaseAccount();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test16SignMessageError() {
|
|
||||||
OlmAccount olmAccount = null;
|
|
||||||
try {
|
|
||||||
olmAccount = new OlmAccount();
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
String signedMsg = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
signedMsg = olmAccount.signMessage(null);
|
|
||||||
} catch (Exception e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNull(signedMsg);
|
|
||||||
|
|
||||||
olmAccount.releaseAccount();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create multiple accounts and check that identity keys are still different.
|
|
||||||
* This test validates random series are provide enough random values.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void test17MultipleAccountCreation() {
|
|
||||||
try {
|
|
||||||
OlmAccount account1 = new OlmAccount();
|
|
||||||
OlmAccount account2 = new OlmAccount();
|
|
||||||
OlmAccount account3 = new OlmAccount();
|
|
||||||
OlmAccount account4 = new OlmAccount();
|
|
||||||
OlmAccount account5 = new OlmAccount();
|
|
||||||
OlmAccount account6 = new OlmAccount();
|
|
||||||
OlmAccount account7 = new OlmAccount();
|
|
||||||
OlmAccount account8 = new OlmAccount();
|
|
||||||
OlmAccount account9 = new OlmAccount();
|
|
||||||
OlmAccount account10 = new OlmAccount();
|
|
||||||
|
|
||||||
Map<String, String> identityKeys1 = account1.identityKeys();
|
|
||||||
Map<String, String> identityKeys2 = account2.identityKeys();
|
|
||||||
Map<String, String> identityKeys3 = account3.identityKeys();
|
|
||||||
Map<String, String> identityKeys4 = account4.identityKeys();
|
|
||||||
Map<String, String> identityKeys5 = account5.identityKeys();
|
|
||||||
Map<String, String> identityKeys6 = account6.identityKeys();
|
|
||||||
Map<String, String> identityKeys7 = account7.identityKeys();
|
|
||||||
Map<String, String> identityKeys8 = account8.identityKeys();
|
|
||||||
Map<String, String> identityKeys9 = account9.identityKeys();
|
|
||||||
Map<String, String> identityKeys10 = account10.identityKeys();
|
|
||||||
|
|
||||||
String identityKey1 = TestHelper.getIdentityKey(identityKeys1);
|
|
||||||
String identityKey2 = TestHelper.getIdentityKey(identityKeys2);
|
|
||||||
assertNotEquals(identityKey1, identityKey2);
|
|
||||||
|
|
||||||
String identityKey3 = TestHelper.getIdentityKey(identityKeys3);
|
|
||||||
assertNotEquals(identityKey2, identityKey3);
|
|
||||||
|
|
||||||
String identityKey4 = TestHelper.getIdentityKey(identityKeys4);
|
|
||||||
assertNotEquals(identityKey3, identityKey4);
|
|
||||||
|
|
||||||
String identityKey5 = TestHelper.getIdentityKey(identityKeys5);
|
|
||||||
assertNotEquals(identityKey4, identityKey5);
|
|
||||||
|
|
||||||
String identityKey6 = TestHelper.getIdentityKey(identityKeys6);
|
|
||||||
assertNotEquals(identityKey5, identityKey6);
|
|
||||||
|
|
||||||
String identityKey7 = TestHelper.getIdentityKey(identityKeys7);
|
|
||||||
assertNotEquals(identityKey6, identityKey7);
|
|
||||||
|
|
||||||
String identityKey8 = TestHelper.getIdentityKey(identityKeys8);
|
|
||||||
assertNotEquals(identityKey7, identityKey8);
|
|
||||||
|
|
||||||
String identityKey9 = TestHelper.getIdentityKey(identityKeys9);
|
|
||||||
assertNotEquals(identityKey8, identityKey9);
|
|
||||||
|
|
||||||
String identityKey10 = TestHelper.getIdentityKey(identityKeys10);
|
|
||||||
assertNotEquals(identityKey9, identityKey10);
|
|
||||||
|
|
||||||
account1.releaseAccount();
|
|
||||||
account2.releaseAccount();
|
|
||||||
account3.releaseAccount();
|
|
||||||
account4.releaseAccount();
|
|
||||||
account5.releaseAccount();
|
|
||||||
account6.releaseAccount();
|
|
||||||
account7.releaseAccount();
|
|
||||||
account8.releaseAccount();
|
|
||||||
account9.releaseAccount();
|
|
||||||
account10.releaseAccount();
|
|
||||||
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test18GenerateFallbackKey() {
|
|
||||||
try {
|
|
||||||
OlmAccount account1 = new OlmAccount();
|
|
||||||
account1.generateFallbackKey();
|
|
||||||
Map<String, Map<String, String>> fallbackKeyMap = account1.fallbackKey();
|
|
||||||
|
|
||||||
assertNotNull(fallbackKeyMap);
|
|
||||||
|
|
||||||
assertEquals(1, fallbackKeyMap.size());
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,630 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.olm;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
||||||
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
import org.junit.FixMethodOrder;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.MethodSorters;
|
|
||||||
|
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertNotEquals;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
|
||||||
public class OlmGroupSessionTest {
|
|
||||||
private static final String LOG_TAG = "OlmSessionTest";
|
|
||||||
private final String FILE_NAME_SERIAL_OUT_SESSION = "SerialOutGroupSession";
|
|
||||||
private final String FILE_NAME_SERIAL_IN_SESSION = "SerialInGroupSession";
|
|
||||||
|
|
||||||
private static OlmManager mOlmManager;
|
|
||||||
private static OlmOutboundGroupSession mAliceOutboundGroupSession;
|
|
||||||
private static String mAliceSessionIdentifier;
|
|
||||||
private static long mAliceMessageIndex;
|
|
||||||
private static final String CLEAR_MESSAGE1 = "Hello!";
|
|
||||||
private static String mAliceToBobMessage;
|
|
||||||
private static OlmInboundGroupSession mBobInboundGroupSession;
|
|
||||||
private static String mAliceOutboundSessionKey;
|
|
||||||
private static String mBobSessionIdentifier;
|
|
||||||
private static String mBobDecryptedMessage;
|
|
||||||
|
|
||||||
@BeforeClass
|
|
||||||
public static void setUpClass(){
|
|
||||||
// load native lib
|
|
||||||
mOlmManager = new OlmManager();
|
|
||||||
|
|
||||||
String version = mOlmManager.getOlmLibVersion();
|
|
||||||
assertNotNull(version);
|
|
||||||
Log.d(LOG_TAG, "## setUpClass(): lib version="+version);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic test:
|
|
||||||
* - alice creates an outbound group session
|
|
||||||
* - bob creates an inbound group session with alice's outbound session key
|
|
||||||
* - alice encrypts a message with its session
|
|
||||||
* - bob decrypts the encrypted message with its session
|
|
||||||
* - decrypted message is identical to original alice message
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void test01CreateOutboundSession() {
|
|
||||||
// alice creates OUTBOUND GROUP SESSION
|
|
||||||
try {
|
|
||||||
mAliceOutboundGroupSession = new OlmOutboundGroupSession();
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test02GetOutboundGroupSessionIdentifier() {
|
|
||||||
// test session ID
|
|
||||||
mAliceSessionIdentifier = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
mAliceSessionIdentifier = mAliceOutboundGroupSession.sessionIdentifier();
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNotNull(mAliceSessionIdentifier);
|
|
||||||
assertTrue(mAliceSessionIdentifier.length() > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test03GetOutboundGroupSessionKey() {
|
|
||||||
// test session Key
|
|
||||||
mAliceOutboundSessionKey = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
mAliceOutboundSessionKey = mAliceOutboundGroupSession.sessionKey();
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
assertNotNull(mAliceOutboundSessionKey);
|
|
||||||
assertTrue(mAliceOutboundSessionKey.length() > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test04GetOutboundGroupMessageIndex() {
|
|
||||||
// test message index before any encryption
|
|
||||||
mAliceMessageIndex = mAliceOutboundGroupSession.messageIndex();
|
|
||||||
assertEquals(0, mAliceMessageIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test05OutboundGroupEncryptMessage() {
|
|
||||||
// alice encrypts a message to bob
|
|
||||||
try {
|
|
||||||
mAliceToBobMessage = mAliceOutboundGroupSession.encryptMessage(CLEAR_MESSAGE1);
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("Exception in bob encryptMessage, Exception code=" + e.getMessage());
|
|
||||||
}
|
|
||||||
assertFalse(TextUtils.isEmpty(mAliceToBobMessage));
|
|
||||||
|
|
||||||
// test message index after encryption is incremented
|
|
||||||
mAliceMessageIndex = mAliceOutboundGroupSession.messageIndex();
|
|
||||||
assertEquals(1, mAliceMessageIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test06CreateInboundGroupSession() {
|
|
||||||
// bob creates INBOUND GROUP SESSION with alice outbound key
|
|
||||||
try {
|
|
||||||
mBobInboundGroupSession = new OlmInboundGroupSession(mAliceOutboundSessionKey);
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail("Exception in bob OlmInboundGroupSession, Exception code=" + e.getExceptionCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test08GetInboundGroupSessionIdentifier() {
|
|
||||||
// check both session identifiers are equals
|
|
||||||
mBobSessionIdentifier = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
mBobSessionIdentifier = mBobInboundGroupSession.sessionIdentifier();
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
assertFalse(TextUtils.isEmpty(mBobSessionIdentifier));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test09SessionIdentifiersAreIdentical() {
|
|
||||||
// check both session identifiers are equals: alice vs bob
|
|
||||||
assertEquals(mAliceSessionIdentifier, mBobSessionIdentifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test10InboundDecryptMessage() {
|
|
||||||
mBobDecryptedMessage = null;
|
|
||||||
OlmInboundGroupSession.DecryptMessageResult result = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
result = mBobInboundGroupSession.decryptMessage(mAliceToBobMessage);
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
// test decrypted message
|
|
||||||
mBobDecryptedMessage = result.mDecryptedMessage;
|
|
||||||
assertFalse(TextUtils.isEmpty(mBobDecryptedMessage));
|
|
||||||
assertEquals(0, result.mIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test11InboundDecryptedMessageIdentical() {
|
|
||||||
// test decrypted message
|
|
||||||
assertEquals(mBobDecryptedMessage, CLEAR_MESSAGE1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test12ReleaseOutboundSession() {
|
|
||||||
// release group sessions
|
|
||||||
mAliceOutboundGroupSession.releaseSession();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test13ReleaseInboundSession() {
|
|
||||||
// release group sessions
|
|
||||||
mBobInboundGroupSession.releaseSession();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test14CheckUnreleaseedCount() {
|
|
||||||
assertTrue(mAliceOutboundGroupSession.isReleased());
|
|
||||||
assertTrue(mBobInboundGroupSession.isReleased());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test15SerializeOutboundSession() {
|
|
||||||
OlmOutboundGroupSession outboundGroupSessionRef=null;
|
|
||||||
OlmOutboundGroupSession outboundGroupSessionSerial;
|
|
||||||
|
|
||||||
// create one OUTBOUND GROUP SESSION
|
|
||||||
try {
|
|
||||||
outboundGroupSessionRef = new OlmOutboundGroupSession();
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode());
|
|
||||||
}
|
|
||||||
assertNotNull(outboundGroupSessionRef);
|
|
||||||
|
|
||||||
|
|
||||||
// serialize alice session
|
|
||||||
Context context = ApplicationProvider.getApplicationContext();
|
|
||||||
try {
|
|
||||||
FileOutputStream fileOutput = context.openFileOutput(FILE_NAME_SERIAL_OUT_SESSION, Context.MODE_PRIVATE);
|
|
||||||
ObjectOutputStream objectOutput = new ObjectOutputStream(fileOutput);
|
|
||||||
objectOutput.writeObject(outboundGroupSessionRef);
|
|
||||||
objectOutput.flush();
|
|
||||||
objectOutput.close();
|
|
||||||
|
|
||||||
// deserialize session
|
|
||||||
FileInputStream fileInput = context.openFileInput(FILE_NAME_SERIAL_OUT_SESSION);
|
|
||||||
ObjectInputStream objectInput = new ObjectInputStream(fileInput);
|
|
||||||
outboundGroupSessionSerial = (OlmOutboundGroupSession) objectInput.readObject();
|
|
||||||
assertNotNull(outboundGroupSessionSerial);
|
|
||||||
objectInput.close();
|
|
||||||
|
|
||||||
// get sessions keys
|
|
||||||
String sessionKeyRef = outboundGroupSessionRef.sessionKey();
|
|
||||||
String sessionKeySerial = outboundGroupSessionSerial.sessionKey();
|
|
||||||
assertFalse(TextUtils.isEmpty(sessionKeyRef));
|
|
||||||
assertFalse(TextUtils.isEmpty(sessionKeySerial));
|
|
||||||
|
|
||||||
// session keys comparison
|
|
||||||
assertEquals(sessionKeyRef, sessionKeySerial);
|
|
||||||
|
|
||||||
// get sessions IDs
|
|
||||||
String sessionIdRef = outboundGroupSessionRef.sessionIdentifier();
|
|
||||||
String sessionIdSerial = outboundGroupSessionSerial.sessionIdentifier();
|
|
||||||
assertFalse(TextUtils.isEmpty(sessionIdRef));
|
|
||||||
assertFalse(TextUtils.isEmpty(sessionIdSerial));
|
|
||||||
|
|
||||||
// session IDs comparison
|
|
||||||
assertEquals(sessionIdRef, sessionIdSerial);
|
|
||||||
|
|
||||||
outboundGroupSessionRef.releaseSession();
|
|
||||||
outboundGroupSessionSerial.releaseSession();
|
|
||||||
|
|
||||||
assertTrue(outboundGroupSessionRef.isReleased());
|
|
||||||
assertTrue(outboundGroupSessionSerial.isReleased());
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception FileNotFoundException Msg=="+e.getMessage());
|
|
||||||
fail(e.getMessage());
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception ClassNotFoundException Msg==" + e.getMessage());
|
|
||||||
fail(e.getMessage());
|
|
||||||
} catch (OlmException e) {
|
|
||||||
Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception OlmException Msg==" + e.getMessage());
|
|
||||||
fail(e.getMessage());
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception IOException Msg==" + e.getMessage());
|
|
||||||
fail(e.getMessage());
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception Msg==" + e.getMessage());
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test16SerializeInboundSession() {
|
|
||||||
OlmOutboundGroupSession aliceOutboundGroupSession=null;
|
|
||||||
OlmInboundGroupSession bobInboundGroupSessionRef=null;
|
|
||||||
OlmInboundGroupSession bobInboundGroupSessionSerial;
|
|
||||||
|
|
||||||
// alice creates OUTBOUND GROUP SESSION
|
|
||||||
try {
|
|
||||||
aliceOutboundGroupSession = new OlmOutboundGroupSession();
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode());
|
|
||||||
}
|
|
||||||
assertNotNull(aliceOutboundGroupSession);
|
|
||||||
|
|
||||||
// get the session key from the outbound group session
|
|
||||||
String sessionKeyRef = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
sessionKeyRef = aliceOutboundGroupSession.sessionKey();
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
assertNotNull(sessionKeyRef);
|
|
||||||
|
|
||||||
// bob creates INBOUND GROUP SESSION
|
|
||||||
try {
|
|
||||||
bobInboundGroupSessionRef = new OlmInboundGroupSession(sessionKeyRef);
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail("Exception in OlmInboundGroupSession, Exception code=" + e.getExceptionCode());
|
|
||||||
}
|
|
||||||
assertNotNull(bobInboundGroupSessionRef);
|
|
||||||
|
|
||||||
// serialize alice session
|
|
||||||
Context context = ApplicationProvider.getApplicationContext();
|
|
||||||
try {
|
|
||||||
FileOutputStream fileOutput = context.openFileOutput(FILE_NAME_SERIAL_IN_SESSION, Context.MODE_PRIVATE);
|
|
||||||
ObjectOutputStream objectOutput = new ObjectOutputStream(fileOutput);
|
|
||||||
objectOutput.writeObject(bobInboundGroupSessionRef);
|
|
||||||
objectOutput.flush();
|
|
||||||
objectOutput.close();
|
|
||||||
|
|
||||||
// deserialize session
|
|
||||||
FileInputStream fileInput = context.openFileInput(FILE_NAME_SERIAL_IN_SESSION);
|
|
||||||
ObjectInputStream objectInput = new ObjectInputStream(fileInput);
|
|
||||||
bobInboundGroupSessionSerial = (OlmInboundGroupSession)objectInput.readObject();
|
|
||||||
assertNotNull(bobInboundGroupSessionSerial);
|
|
||||||
objectInput.close();
|
|
||||||
|
|
||||||
// get sessions IDs
|
|
||||||
String aliceSessionId = aliceOutboundGroupSession.sessionIdentifier();
|
|
||||||
String sessionIdRef = bobInboundGroupSessionRef.sessionIdentifier();
|
|
||||||
String sessionIdSerial = bobInboundGroupSessionSerial.sessionIdentifier();
|
|
||||||
assertFalse(TextUtils.isEmpty(aliceSessionId));
|
|
||||||
assertFalse(TextUtils.isEmpty(sessionIdRef));
|
|
||||||
assertFalse(TextUtils.isEmpty(sessionIdSerial));
|
|
||||||
|
|
||||||
// session IDs comparison
|
|
||||||
assertEquals(aliceSessionId, sessionIdSerial);
|
|
||||||
assertEquals(sessionIdRef, sessionIdSerial);
|
|
||||||
|
|
||||||
aliceOutboundGroupSession.releaseSession();
|
|
||||||
bobInboundGroupSessionRef.releaseSession();
|
|
||||||
bobInboundGroupSessionSerial.releaseSession();
|
|
||||||
|
|
||||||
assertTrue(aliceOutboundGroupSession.isReleased());
|
|
||||||
assertTrue(bobInboundGroupSessionRef.isReleased());
|
|
||||||
assertTrue(bobInboundGroupSessionSerial.isReleased());
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception FileNotFoundException Msg=="+e.getMessage());
|
|
||||||
fail(e.getMessage());
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception ClassNotFoundException Msg==" + e.getMessage());
|
|
||||||
fail(e.getMessage());
|
|
||||||
} catch (OlmException e) {
|
|
||||||
Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception OlmException Msg==" + e.getMessage());
|
|
||||||
fail(e.getMessage());
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception IOException Msg==" + e.getMessage());
|
|
||||||
fail(e.getMessage());
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception Msg==" + e.getMessage());
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create multiple outbound group sessions and check that session Keys are different.
|
|
||||||
* This test validates random series are provide enough random values.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void test17MultipleOutboundSession() {
|
|
||||||
OlmOutboundGroupSession outboundGroupSession1;
|
|
||||||
OlmOutboundGroupSession outboundGroupSession2;
|
|
||||||
OlmOutboundGroupSession outboundGroupSession3;
|
|
||||||
OlmOutboundGroupSession outboundGroupSession4;
|
|
||||||
OlmOutboundGroupSession outboundGroupSession5;
|
|
||||||
OlmOutboundGroupSession outboundGroupSession6;
|
|
||||||
OlmOutboundGroupSession outboundGroupSession7;
|
|
||||||
OlmOutboundGroupSession outboundGroupSession8;
|
|
||||||
|
|
||||||
try {
|
|
||||||
outboundGroupSession1 = new OlmOutboundGroupSession();
|
|
||||||
outboundGroupSession2 = new OlmOutboundGroupSession();
|
|
||||||
outboundGroupSession3 = new OlmOutboundGroupSession();
|
|
||||||
outboundGroupSession4 = new OlmOutboundGroupSession();
|
|
||||||
outboundGroupSession5 = new OlmOutboundGroupSession();
|
|
||||||
outboundGroupSession6 = new OlmOutboundGroupSession();
|
|
||||||
outboundGroupSession7 = new OlmOutboundGroupSession();
|
|
||||||
outboundGroupSession8 = new OlmOutboundGroupSession();
|
|
||||||
|
|
||||||
// get the session key from the outbound group sessions
|
|
||||||
String sessionKey1 = outboundGroupSession1.sessionKey();
|
|
||||||
String sessionKey2 = outboundGroupSession2.sessionKey();
|
|
||||||
assertNotEquals(sessionKey1, sessionKey2);
|
|
||||||
|
|
||||||
String sessionKey3 = outboundGroupSession3.sessionKey();
|
|
||||||
assertNotEquals(sessionKey2, sessionKey3);
|
|
||||||
|
|
||||||
String sessionKey4 = outboundGroupSession4.sessionKey();
|
|
||||||
assertNotEquals(sessionKey3, sessionKey4);
|
|
||||||
|
|
||||||
String sessionKey5 = outboundGroupSession5.sessionKey();
|
|
||||||
assertNotEquals(sessionKey4, sessionKey5);
|
|
||||||
|
|
||||||
String sessionKey6 = outboundGroupSession6.sessionKey();
|
|
||||||
assertNotEquals(sessionKey5, sessionKey6);
|
|
||||||
|
|
||||||
String sessionKey7 = outboundGroupSession7.sessionKey();
|
|
||||||
assertNotEquals(sessionKey6, sessionKey7);
|
|
||||||
|
|
||||||
String sessionKey8 = outboundGroupSession8.sessionKey();
|
|
||||||
assertNotEquals(sessionKey7, sessionKey8);
|
|
||||||
|
|
||||||
// get the session IDs from the outbound group sessions
|
|
||||||
String sessionId1 = outboundGroupSession1.sessionIdentifier();
|
|
||||||
String sessionId2 = outboundGroupSession2.sessionIdentifier();
|
|
||||||
assertNotEquals(sessionId1, sessionId2);
|
|
||||||
|
|
||||||
String sessionId3 = outboundGroupSession3.sessionKey();
|
|
||||||
assertNotEquals(sessionId2, sessionId3);
|
|
||||||
|
|
||||||
String sessionId4 = outboundGroupSession4.sessionKey();
|
|
||||||
assertNotEquals(sessionId3, sessionId4);
|
|
||||||
|
|
||||||
String sessionId5 = outboundGroupSession5.sessionKey();
|
|
||||||
assertNotEquals(sessionId4, sessionId5);
|
|
||||||
|
|
||||||
String sessionId6 = outboundGroupSession6.sessionKey();
|
|
||||||
assertNotEquals(sessionId5, sessionId6);
|
|
||||||
|
|
||||||
String sessionId7 = outboundGroupSession7.sessionKey();
|
|
||||||
assertNotEquals(sessionId6, sessionId7);
|
|
||||||
|
|
||||||
String sessionId8 = outboundGroupSession8.sessionKey();
|
|
||||||
assertNotEquals(sessionId7, sessionId8);
|
|
||||||
|
|
||||||
outboundGroupSession1.releaseSession();
|
|
||||||
outboundGroupSession2.releaseSession();
|
|
||||||
outboundGroupSession3.releaseSession();
|
|
||||||
outboundGroupSession4.releaseSession();
|
|
||||||
outboundGroupSession5.releaseSession();
|
|
||||||
outboundGroupSession6.releaseSession();
|
|
||||||
outboundGroupSession7.releaseSession();
|
|
||||||
outboundGroupSession8.releaseSession();
|
|
||||||
|
|
||||||
assertTrue(outboundGroupSession1.isReleased());
|
|
||||||
assertTrue(outboundGroupSession2.isReleased());
|
|
||||||
assertTrue(outboundGroupSession3.isReleased());
|
|
||||||
assertTrue(outboundGroupSession4.isReleased());
|
|
||||||
assertTrue(outboundGroupSession5.isReleased());
|
|
||||||
assertTrue(outboundGroupSession6.isReleased());
|
|
||||||
assertTrue(outboundGroupSession7.isReleased());
|
|
||||||
assertTrue(outboundGroupSession8.isReleased());
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specific test for the following run time error:
|
|
||||||
* "JNI DETECTED ERROR IN APPLICATION: input is not valid Modified UTF-8: illegal start byte 0xf0 in call to NewStringUTF".<br>
|
|
||||||
* When the msg to decrypt contain emojis, depending on the android platform, the NewStringUTF() behaves differently and
|
|
||||||
* can even crash.
|
|
||||||
* This issue is described in details here: https://github.com/eclipsesource/J2V8/issues/142
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void test18TestBadCharacterCrashInDecrypt() {
|
|
||||||
OlmInboundGroupSession bobInboundGroupSession=null;
|
|
||||||
|
|
||||||
// values taken from a "real life" crash case
|
|
||||||
String sessionKeyRef = "AgAAAAycZE6AekIctJWYxd2AWLOY15YmxZODm/WkgbpWkyycp6ytSp/R+wo84jRrzBNWmv6ySLTZ9R0EDOk9VI2eZyQ6Efdwyo1mAvrWvTkZl9yALPdkOIVHywyG65f1SNiLrnsln3hgsT1vUrISGyKtsljoUgQpr3JDPEhD0ilAi63QBjhnGCW252b+7nF+43rb6O6lwm93LaVwe2341Gdp6EkhTUvetALezEqDOtKN00wVqAbq0RQAnUJIowxHbMswg+FyoR1K1oCjnVEoF23O9xlAn5g1XtuBZP3moJlR2lwsBA";
|
|
||||||
String msgToDecryptWithEmoji = "AwgNEpABpjs+tYF+0y8bWtzAgYAC3N55p5cPJEEiGPU1kxIHSY7f2aG5Fj4wmcsXUkhDv0UePj922kgf+Q4dFsPHKq2aVA93n8DJAQ/FRfcM98B9E6sKCZ/PsCF78uBvF12Aaq9D3pUHBopdd7llUfVq29d5y6ZwX5VDoqV2utsATkKjXYV9CbfZuvvBMQ30ZLjEtyUUBJDY9K4FxEFcULytA/IkVnATTG9ERuLF/yB6ukSFR+iUWRYAmtuOuU0k9BvaqezbGqNoK5Grlkes+dYX6/0yUObumcw9/iAI";
|
|
||||||
|
|
||||||
// bob creates INBOUND GROUP SESSION
|
|
||||||
try {
|
|
||||||
bobInboundGroupSession = new OlmInboundGroupSession(sessionKeyRef);
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail("Exception in test18TestBadCharacterCrashInDecrypt, Exception code=" + e.getExceptionCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
OlmInboundGroupSession.DecryptMessageResult result = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
result = bobInboundGroupSession.decryptMessage(msgToDecryptWithEmoji);
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("Exception in test18TestBadCharacterCrashInDecrypt, Exception code=" + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNotNull(result.mDecryptedMessage);
|
|
||||||
assertEquals(13, result.mIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specific test to check an error message is returned by decryptMessage() API.<br>
|
|
||||||
* A corrupted encrypted message is passed, and a INVALID_BASE64 is
|
|
||||||
* espexted.
|
|
||||||
**/
|
|
||||||
@Test
|
|
||||||
public void test19TestErrorMessageReturnedInDecrypt() {
|
|
||||||
OlmInboundGroupSession bobInboundGroupSession=null;
|
|
||||||
final String EXPECTED_ERROR_MESSAGE= "INVALID_BASE64";
|
|
||||||
|
|
||||||
String sessionKeyRef = "AgAAAAycZE6AekIctJWYxd2AWLOY15YmxZODm/WkgbpWkyycp6ytSp/R+wo84jRrzBNWmv6ySLTZ9R0EDOk9VI2eZyQ6Efdwyo1mAvrWvTkZl9yALPdkOIVHywyG65f1SNiLrnsln3hgsT1vUrISGyKtsljoUgQpr3JDPEhD0ilAi63QBjhnGCW252b+7nF+43rb6O6lwm93LaVwe2341Gdp6EkhTUvetALezEqDOtKN00wVqAbq0RQAnUJIowxHbMswg+FyoR1K1oCjnVEoF23O9xlAn5g1XtuBZP3moJlR2lwsBA";
|
|
||||||
String corruptedEncryptedMsg = "AwgANYTHINGf87ge45ge7gr*/rg5ganything4gr41rrgr4re55tanythingmcsXUkhDv0UePj922kgf+";
|
|
||||||
|
|
||||||
// valid INBOUND GROUP SESSION
|
|
||||||
try {
|
|
||||||
bobInboundGroupSession = new OlmInboundGroupSession(sessionKeyRef);
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail("Exception in test19TestErrorMessageReturnedInDecrypt, Exception code=" + e.getExceptionCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
String exceptionMessage = null;
|
|
||||||
try {
|
|
||||||
bobInboundGroupSession.decryptMessage(corruptedEncryptedMsg);
|
|
||||||
} catch (OlmException e) {
|
|
||||||
exceptionMessage = e.getMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(EXPECTED_ERROR_MESSAGE, exceptionMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test the import/export functions.<br>
|
|
||||||
**/
|
|
||||||
@Test
|
|
||||||
public void test20TestInboundGroupSessionImportExport() {
|
|
||||||
|
|
||||||
String sessionKey = "AgAAAAAwMTIzNDU2Nzg5QUJERUYwMTIzNDU2Nzg5QUJDREVGMDEyMzQ1Njc4OUFCREVGM" +
|
|
||||||
"DEyMzQ1Njc4OUFCQ0RFRjAxMjM0NTY3ODlBQkRFRjAxMjM0NTY3ODlBQkNERUYwMTIzND" +
|
|
||||||
"U2Nzg5QUJERUYwMTIzNDU2Nzg5QUJDREVGMDEyMw0bdg1BDq4Px/slBow06q8n/B9WBfw" +
|
|
||||||
"WYyNOB8DlUmXGGwrFmaSb9bR/eY8xgERrxmP07hFmD9uqA2p8PMHdnV5ysmgufE6oLZ5+" +
|
|
||||||
"8/mWQOW3VVTnDIlnwd8oHUYRuk8TCQ";
|
|
||||||
|
|
||||||
String message = "AwgAEhAcbh6UpbByoyZxufQ+h2B+8XHMjhR69G8F4+qjMaFlnIXusJZX3r8LnRORG9T3D" +
|
|
||||||
"XFdbVuvIWrLyRfm4i8QRbe8VPwGRFG57B1CtmxanuP8bHtnnYqlwPsD";
|
|
||||||
|
|
||||||
|
|
||||||
OlmInboundGroupSession inboundGroupSession = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
inboundGroupSession = new OlmInboundGroupSession(sessionKey);
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("OlmInboundGroupSession failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isVerified = false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
isVerified = inboundGroupSession.isVerified();
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("isVerified failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
assertTrue(isVerified);
|
|
||||||
|
|
||||||
OlmInboundGroupSession.DecryptMessageResult result = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
result = inboundGroupSession.decryptMessage(message);
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("decryptMessage failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
assertTrue(TextUtils.equals(result.mDecryptedMessage, "Message"));
|
|
||||||
assertEquals(0, result.mIndex);
|
|
||||||
|
|
||||||
String export = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
export = inboundGroupSession.export(0);
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("export failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
assertFalse(TextUtils.isEmpty(export));
|
|
||||||
|
|
||||||
long index = -1;
|
|
||||||
try {
|
|
||||||
index = inboundGroupSession.getFirstKnownIndex();
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("getFirstKnownIndex failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
assertTrue(index >=0);
|
|
||||||
|
|
||||||
inboundGroupSession.releaseSession();
|
|
||||||
inboundGroupSession = null;
|
|
||||||
|
|
||||||
OlmInboundGroupSession inboundGroupSession2 = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
inboundGroupSession2 = inboundGroupSession.importSession(export);
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("OlmInboundGroupSession failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
isVerified = inboundGroupSession2.isVerified();
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("isVerified failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
assertFalse(isVerified);
|
|
||||||
|
|
||||||
result = null;
|
|
||||||
try {
|
|
||||||
result = inboundGroupSession2.decryptMessage(message);
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("decryptMessage failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
assertTrue(TextUtils.equals(result.mDecryptedMessage, "Message"));
|
|
||||||
assertEquals(0, result.mIndex);
|
|
||||||
|
|
||||||
try {
|
|
||||||
isVerified = inboundGroupSession2.isVerified();
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("isVerified failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
assertTrue(isVerified);
|
|
||||||
inboundGroupSession2.releaseSession();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,202 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2018,2019 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.olm;
|
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
||||||
|
|
||||||
import org.junit.FixMethodOrder;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.MethodSorters;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
|
||||||
public class OlmPkTest {
|
|
||||||
private static final String LOG_TAG = "OlmPkEncryptionTest";
|
|
||||||
|
|
||||||
private static OlmPkEncryption mOlmPkEncryption;
|
|
||||||
private static OlmPkDecryption mOlmPkDecryption;
|
|
||||||
private static OlmPkSigning mOlmPkSigning;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test01EncryptAndDecrypt() {
|
|
||||||
try {
|
|
||||||
mOlmPkEncryption = new OlmPkEncryption();
|
|
||||||
} catch (OlmException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail("OlmPkEncryption failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
mOlmPkDecryption = new OlmPkDecryption();
|
|
||||||
} catch (OlmException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail("OlmPkEncryption failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNotNull(mOlmPkEncryption);
|
|
||||||
assertNotNull(mOlmPkDecryption);
|
|
||||||
|
|
||||||
String key = null;
|
|
||||||
try {
|
|
||||||
key = mOlmPkDecryption.generateKey();
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail("Exception in generateKey, Exception code=" + e.getExceptionCode());
|
|
||||||
}
|
|
||||||
Log.d(LOG_TAG, "Ephemeral Key: " + key);
|
|
||||||
try {
|
|
||||||
mOlmPkEncryption.setRecipientKey(key);
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail("Exception in setRecipientKey, Exception code=" + e.getExceptionCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
String clearMessage = "Public key test";
|
|
||||||
OlmPkMessage message = null;
|
|
||||||
try {
|
|
||||||
message = mOlmPkEncryption.encrypt(clearMessage);
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail("Exception in encrypt, Exception code=" + e.getExceptionCode());
|
|
||||||
}
|
|
||||||
Log.d(LOG_TAG, "message: " + message.mCipherText + " " + message.mMac + " " + message.mEphemeralKey);
|
|
||||||
|
|
||||||
String decryptedMessage = null;
|
|
||||||
try {
|
|
||||||
decryptedMessage = mOlmPkDecryption.decrypt(message);
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail("Exception in decrypt, Exception code=" + e.getExceptionCode());
|
|
||||||
}
|
|
||||||
assertEquals(clearMessage, decryptedMessage);
|
|
||||||
|
|
||||||
mOlmPkEncryption.releaseEncryption();
|
|
||||||
mOlmPkDecryption.releaseDecryption();
|
|
||||||
assertTrue(mOlmPkEncryption.isReleased());
|
|
||||||
assertTrue(mOlmPkDecryption.isReleased());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test02PrivateKey() {
|
|
||||||
try {
|
|
||||||
mOlmPkDecryption = new OlmPkDecryption();
|
|
||||||
} catch (OlmException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail("OlmPkEncryption failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNotNull(mOlmPkDecryption);
|
|
||||||
|
|
||||||
byte[] privateKey = {
|
|
||||||
(byte) 0x77, (byte) 0x07, (byte) 0x6D, (byte) 0x0A,
|
|
||||||
(byte) 0x73, (byte) 0x18, (byte) 0xA5, (byte) 0x7D,
|
|
||||||
(byte) 0x3C, (byte) 0x16, (byte) 0xC1, (byte) 0x72,
|
|
||||||
(byte) 0x51, (byte) 0xB2, (byte) 0x66, (byte) 0x45,
|
|
||||||
(byte) 0xDF, (byte) 0x4C, (byte) 0x2F, (byte) 0x87,
|
|
||||||
(byte) 0xEB, (byte) 0xC0, (byte) 0x99, (byte) 0x2A,
|
|
||||||
(byte) 0xB1, (byte) 0x77, (byte) 0xFB, (byte) 0xA5,
|
|
||||||
(byte) 0x1D, (byte) 0xB9, (byte) 0x2C, (byte) 0x2A
|
|
||||||
};
|
|
||||||
|
|
||||||
assertEquals(privateKey.length, OlmPkDecryption.privateKeyLength());
|
|
||||||
|
|
||||||
try {
|
|
||||||
mOlmPkDecryption.setPrivateKey(privateKey);
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail("Exception in setPrivateKey, Exception code=" + e.getExceptionCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] privateKeyCopy = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
privateKeyCopy = mOlmPkDecryption.privateKey();
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail("Exception in privateKey, Exception code=" + e.getExceptionCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
assertArrayEquals(privateKey, privateKeyCopy);
|
|
||||||
|
|
||||||
mOlmPkDecryption.releaseDecryption();
|
|
||||||
assertTrue(mOlmPkDecryption.isReleased());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test03Signing() {
|
|
||||||
try {
|
|
||||||
mOlmPkSigning = new OlmPkSigning();
|
|
||||||
} catch (OlmException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail("OlmPkSigning failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNotNull(mOlmPkSigning);
|
|
||||||
|
|
||||||
byte[] seed = null;
|
|
||||||
try {
|
|
||||||
seed = OlmPkSigning.generateSeed();
|
|
||||||
} catch (OlmException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail("generateSeed failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(seed.length, OlmPkSigning.seedLength());
|
|
||||||
|
|
||||||
String pubkey = null;
|
|
||||||
try {
|
|
||||||
pubkey = mOlmPkSigning.initWithSeed(seed);
|
|
||||||
} catch (OlmException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail("initWithSeed failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
String message = "We hold these truths to be self-evident, that all men are created equal, that they are endowed by their Creator with certain unalienable Rights, that among these are Life, Liberty and the pursuit of Happiness.";
|
|
||||||
|
|
||||||
String signature = null;
|
|
||||||
try {
|
|
||||||
signature = mOlmPkSigning.sign(message);
|
|
||||||
} catch (OlmException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail("sign failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
OlmUtility olmUtility = null;
|
|
||||||
try {
|
|
||||||
olmUtility = new OlmUtility();
|
|
||||||
} catch (OlmException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail("olmUtility failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
olmUtility.verifyEd25519Signature(signature, pubkey, message);
|
|
||||||
} catch (OlmException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail("Signature verification failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
mOlmPkSigning.releaseSigning();
|
|
||||||
assertTrue(mOlmPkSigning.isReleased());
|
|
||||||
|
|
||||||
olmUtility.releaseUtility();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2019 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.olm;
|
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
||||||
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
import org.junit.FixMethodOrder;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.MethodSorters;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
|
||||||
public class OlmSasTest {
|
|
||||||
|
|
||||||
private static OlmManager mOlmManager;
|
|
||||||
|
|
||||||
//Enable the native lib
|
|
||||||
@BeforeClass
|
|
||||||
public static void setUpClass() {
|
|
||||||
// load native librandomBytesOfLength
|
|
||||||
mOlmManager = new OlmManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSASCode() {
|
|
||||||
OlmSAS aliceSas = null;
|
|
||||||
OlmSAS bobSas = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
aliceSas = new OlmSAS();
|
|
||||||
bobSas = new OlmSAS();
|
|
||||||
|
|
||||||
String alicePKey = aliceSas.getPublicKey();
|
|
||||||
String bobPKey = bobSas.getPublicKey();
|
|
||||||
|
|
||||||
Log.e(OlmSasTest.class.getSimpleName(), "#### Alice pub Key is " + alicePKey);
|
|
||||||
Log.e(OlmSasTest.class.getSimpleName(), "#### Bob pub Key is " + bobPKey);
|
|
||||||
|
|
||||||
aliceSas.setTheirPublicKey(bobPKey);
|
|
||||||
bobSas.setTheirPublicKey(alicePKey);
|
|
||||||
|
|
||||||
int codeLength = 6;
|
|
||||||
byte[] alice_sas = aliceSas.generateShortCode("SAS", codeLength);
|
|
||||||
byte[] bob_sas = bobSas.generateShortCode("SAS", codeLength);
|
|
||||||
|
|
||||||
Log.e(OlmSasTest.class.getSimpleName(), "#### Alice SAS is " + new String(alice_sas, "UTF-8"));
|
|
||||||
Log.e(OlmSasTest.class.getSimpleName(), "#### Bob SAS is " + new String(bob_sas, "UTF-8"));
|
|
||||||
|
|
||||||
assertEquals(codeLength, alice_sas.length);
|
|
||||||
assertEquals(codeLength, bob_sas.length);
|
|
||||||
assertArrayEquals(alice_sas, bob_sas);
|
|
||||||
|
|
||||||
String aliceMac = aliceSas.calculateMac("Hello world!", "SAS");
|
|
||||||
String bobMac = bobSas.calculateMac("Hello world!", "SAS");
|
|
||||||
|
|
||||||
assertEquals(aliceMac, bobMac);
|
|
||||||
|
|
||||||
Log.e(OlmSasTest.class.getSimpleName(), "#### Alice Mac is " + aliceMac);
|
|
||||||
Log.e(OlmSasTest.class.getSimpleName(), "#### Bob Mac is " + bobMac);
|
|
||||||
|
|
||||||
|
|
||||||
String aliceLongKdfMac = aliceSas.calculateMacLongKdf("Hello world!", "SAS");
|
|
||||||
String bobLongKdfMac = bobSas.calculateMacLongKdf("Hello world!", "SAS");
|
|
||||||
|
|
||||||
assertEquals("Mac should be the same", aliceLongKdfMac, bobLongKdfMac);
|
|
||||||
|
|
||||||
Log.e(OlmSasTest.class.getSimpleName(), "#### Alice lkdf Mac is " + aliceLongKdfMac);
|
|
||||||
Log.e(OlmSasTest.class.getSimpleName(), "#### Bob lkdf Mac is " + bobLongKdfMac);
|
|
||||||
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("OlmSas init failed " + e.getMessage());
|
|
||||||
e.printStackTrace();
|
|
||||||
} finally {
|
|
||||||
if (aliceSas != null) {
|
|
||||||
aliceSas.releaseSas();
|
|
||||||
}
|
|
||||||
if (bobSas != null) {
|
|
||||||
bobSas.releaseSas();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,162 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.olm;
|
|
||||||
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
||||||
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
import org.junit.FixMethodOrder;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.MethodSorters;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
|
||||||
public class OlmUtilityTest {
|
|
||||||
private static final String LOG_TAG = "OlmAccountTest";
|
|
||||||
private static final int GENERATION_ONE_TIME_KEYS_NUMBER = 50;
|
|
||||||
|
|
||||||
private static OlmManager mOlmManager;
|
|
||||||
|
|
||||||
@BeforeClass
|
|
||||||
public static void setUpClass() {
|
|
||||||
// load native lib
|
|
||||||
mOlmManager = new OlmManager();
|
|
||||||
|
|
||||||
String version = mOlmManager.getOlmLibVersion();
|
|
||||||
assertNotNull(version);
|
|
||||||
Log.d(LOG_TAG, "## setUpClass(): lib version=" + version);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test the signing API
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void test01VerifyEd25519Signing() {
|
|
||||||
String fingerPrintKey = null;
|
|
||||||
String errorMsg = null;
|
|
||||||
String message = "{\"algorithms\":[\"m.megolm.v1.aes-sha2\",\"m.olm.v1.curve25519-aes-sha2\"],\"device_id\":\"YMBYCWTWCG\",\"keys\":{\"curve25519:YMBYCWTWCG\":\"KZFa5YUXV2EOdhK8dcGMMHWB67stdgAP4+xwiS69mCU\",\"ed25519:YMBYCWTWCG\":\"0cEgQJJqjgtXUGp4ZXQQmh36RAxwxr8HJw2E9v1gvA0\"},\"user_id\":\"@mxBob14774891254276b253f42-f267-43ec-bad9-767142bfea30:localhost:8480\"}";
|
|
||||||
OlmAccount account = null;
|
|
||||||
|
|
||||||
// create account
|
|
||||||
try {
|
|
||||||
account = new OlmAccount();
|
|
||||||
} catch (OlmException e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
assertNotNull(account);
|
|
||||||
|
|
||||||
// sign message
|
|
||||||
String messageSignature = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
messageSignature = account.signMessage(message);
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail(e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNotNull(messageSignature);
|
|
||||||
|
|
||||||
// get identities key (finger print key)
|
|
||||||
Map<String, String> identityKeys = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
identityKeys = account.identityKeys();
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("identityKeys failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNotNull(identityKeys);
|
|
||||||
fingerPrintKey = TestHelper.getFingerprintKey(identityKeys);
|
|
||||||
assertFalse("fingerprint key missing", TextUtils.isEmpty(fingerPrintKey));
|
|
||||||
|
|
||||||
// instantiate utility object
|
|
||||||
OlmUtility utility = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
utility = new OlmUtility();
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("failed to create OlmUtility");
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify signature
|
|
||||||
errorMsg = null;
|
|
||||||
try {
|
|
||||||
utility.verifyEd25519Signature(messageSignature, fingerPrintKey, message);
|
|
||||||
} catch (Exception e) {
|
|
||||||
errorMsg = e.getMessage();
|
|
||||||
}
|
|
||||||
assertTrue(TextUtils.isEmpty(errorMsg));
|
|
||||||
|
|
||||||
// check a bad signature is detected => errorMsg = BAD_MESSAGE_MAC
|
|
||||||
String badSignature = "Bad signature Bad signature Bad signature..";
|
|
||||||
|
|
||||||
errorMsg = null;
|
|
||||||
try {
|
|
||||||
utility.verifyEd25519Signature(badSignature, fingerPrintKey, message);
|
|
||||||
} catch (Exception e) {
|
|
||||||
errorMsg = e.getMessage();
|
|
||||||
}
|
|
||||||
assertFalse(TextUtils.isEmpty(errorMsg));
|
|
||||||
|
|
||||||
// check bad fingerprint size => errorMsg = INVALID_BASE64
|
|
||||||
String badSizeFingerPrintKey = fingerPrintKey.substring(fingerPrintKey.length() / 2);
|
|
||||||
|
|
||||||
errorMsg = null;
|
|
||||||
try {
|
|
||||||
utility.verifyEd25519Signature(messageSignature, badSizeFingerPrintKey, message);
|
|
||||||
} catch (Exception e) {
|
|
||||||
errorMsg = e.getMessage();
|
|
||||||
}
|
|
||||||
assertFalse(TextUtils.isEmpty(errorMsg));
|
|
||||||
|
|
||||||
utility.releaseUtility();
|
|
||||||
assertTrue(utility.isReleased());
|
|
||||||
|
|
||||||
account.releaseAccount();
|
|
||||||
assertTrue(account.isReleased());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test02sha256() {
|
|
||||||
OlmUtility utility = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
utility = new OlmUtility();
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("OlmUtility creation failed");
|
|
||||||
}
|
|
||||||
String msgToHash = "The quick brown fox jumps over the lazy dog";
|
|
||||||
|
|
||||||
String hashResult = utility.sha256(msgToHash);
|
|
||||||
assertFalse(TextUtils.isEmpty(hashResult));
|
|
||||||
|
|
||||||
utility.releaseUtility();
|
|
||||||
assertTrue(utility.isReleased());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,83 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.olm;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper class providing helper methods used in the Olm Android SDK unit tests.
|
|
||||||
*/
|
|
||||||
public class TestHelper {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the identity key {@link OlmAccount#JSON_KEY_IDENTITY_KEY} from the JSON object.
|
|
||||||
* @param aIdentityKeysMap result of {@link OlmAccount#identityKeys()}
|
|
||||||
* @return identity key string if operation succeed, null otherwise
|
|
||||||
*/
|
|
||||||
static public String getIdentityKey(Map<String, String> aIdentityKeysMap){
|
|
||||||
String idKey = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
idKey = aIdentityKeysMap.get(OlmAccount.JSON_KEY_IDENTITY_KEY);
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("Exception MSg=" + e.getMessage());
|
|
||||||
}
|
|
||||||
return idKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the fingerprint key {@link OlmAccount#JSON_KEY_FINGER_PRINT_KEY} from the JSON object.
|
|
||||||
* @param aIdentityKeysMap result of {@link OlmAccount#identityKeys()}
|
|
||||||
* @return fingerprint key string if operation succeed, null otherwise
|
|
||||||
*/
|
|
||||||
static public String getFingerprintKey(Map<String, String> aIdentityKeysMap) {
|
|
||||||
String fingerprintKey = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
fingerprintKey = aIdentityKeysMap.get(OlmAccount.JSON_KEY_FINGER_PRINT_KEY);
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("Exception MSg=" + e.getMessage());
|
|
||||||
}
|
|
||||||
return fingerprintKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the first one time key from the JSON object.
|
|
||||||
* @param aIdentityKeysMap result of {@link OlmAccount#oneTimeKeys()}
|
|
||||||
* @param aKeyPosition the position of the key to be retrieved
|
|
||||||
* @return one time key string if operation succeed, null otherwise
|
|
||||||
*/
|
|
||||||
static public String getOneTimeKey(Map<String, Map<String, String>> aIdentityKeysMap, int aKeyPosition) {
|
|
||||||
String firstOneTimeKey = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
Map<String, String> generatedKeys = aIdentityKeysMap.get(OlmAccount.JSON_KEY_ONE_TIME_KEY);
|
|
||||||
assertNotNull(OlmAccount.JSON_KEY_ONE_TIME_KEY + " object is missing", generatedKeys);
|
|
||||||
|
|
||||||
firstOneTimeKey = (new ArrayList<>(generatedKeys.values())).get(aKeyPosition - 1);
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("Exception Msg=" + e.getMessage());
|
|
||||||
}
|
|
||||||
return firstOneTimeKey;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
<manifest package="org.matrix.olm" />
|
|
|
@ -1,83 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.olm;
|
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper class dedicated to serialization mechanism (template method pattern).
|
|
||||||
*/
|
|
||||||
abstract class CommonSerializeUtils {
|
|
||||||
private static final String LOG_TAG = "CommonSerializeUtils";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kick off the serialization mechanism.
|
|
||||||
* @param aOutStream output stream for serializing
|
|
||||||
* @throws IOException exception
|
|
||||||
*/
|
|
||||||
protected void serialize(ObjectOutputStream aOutStream) throws IOException {
|
|
||||||
aOutStream.defaultWriteObject();
|
|
||||||
|
|
||||||
// generate serialization key
|
|
||||||
byte[] key = OlmUtility.getRandomKey();
|
|
||||||
|
|
||||||
// compute pickle string
|
|
||||||
StringBuffer errorMsg = new StringBuffer();
|
|
||||||
byte[] pickledData = serialize(key, errorMsg);
|
|
||||||
|
|
||||||
if(null == pickledData) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_SERIALIZATION, String.valueOf(errorMsg));
|
|
||||||
} else {
|
|
||||||
aOutStream.writeObject(new String(key, "UTF-8"));
|
|
||||||
aOutStream.writeObject(new String(pickledData, "UTF-8"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kick off the deserialization mechanism.
|
|
||||||
* @param aInStream input stream
|
|
||||||
* @throws Exception the exception
|
|
||||||
*/
|
|
||||||
protected void deserialize(ObjectInputStream aInStream) throws Exception {
|
|
||||||
aInStream.defaultReadObject();
|
|
||||||
|
|
||||||
String keyAsString = (String)aInStream.readObject();
|
|
||||||
String pickledDataAsString = (String)aInStream.readObject();
|
|
||||||
|
|
||||||
byte[] key;
|
|
||||||
byte[] pickledData;
|
|
||||||
|
|
||||||
try {
|
|
||||||
key = keyAsString.getBytes("UTF-8");
|
|
||||||
pickledData = pickledDataAsString.getBytes("UTF-8");
|
|
||||||
|
|
||||||
deserialize(pickledData, key);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_DESERIALIZATION, e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d(LOG_TAG,"## deserializeObject(): success");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract byte[] serialize(byte[] aKey, StringBuffer aErrorMsg);
|
|
||||||
protected abstract void deserialize(byte[] aSerializedData, byte[] aKey) throws Exception;
|
|
||||||
}
|
|
|
@ -1,515 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2017 OpenMarket Ltd
|
|
||||||
* Copyright 2017 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.olm;
|
|
||||||
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Account class used to create Olm sessions in conjunction with {@link OlmSession} class.<br>
|
|
||||||
* OlmAccount provides APIs to retrieve the Olm keys.
|
|
||||||
*<br><br>Detailed implementation guide is available at <a href="http://matrix.org/docs/guides/e2e_implementation.html">Implementing End-to-End Encryption in Matrix clients</a>.
|
|
||||||
*/
|
|
||||||
public class OlmAccount extends CommonSerializeUtils implements Serializable {
|
|
||||||
private static final long serialVersionUID = 3497486121598434824L;
|
|
||||||
private static final String LOG_TAG = "OlmAccount";
|
|
||||||
|
|
||||||
// JSON keys used in the JSON objects returned by JNI
|
|
||||||
/** As well as the identity key, each device creates a number of Curve25519 key pairs which are
|
|
||||||
also used to establish Olm sessions, but can only be used once. Once again, the private part
|
|
||||||
remains on the device. but the public part is published to the Matrix network **/
|
|
||||||
public static final String JSON_KEY_ONE_TIME_KEY = "curve25519";
|
|
||||||
|
|
||||||
/** Curve25519 identity key is a public-key cryptographic system which can be used to establish a shared
|
|
||||||
secret.<br>In Matrix, each device has a long-lived Curve25519 identity key which is used to establish
|
|
||||||
Olm sessions with that device. The private key should never leave the device, but the
|
|
||||||
public part is signed with the Ed25519 fingerprint key ({@link #JSON_KEY_FINGER_PRINT_KEY}) and published to the network. **/
|
|
||||||
public static final String JSON_KEY_IDENTITY_KEY = "curve25519";
|
|
||||||
|
|
||||||
/** Ed25519 finger print is a public-key cryptographic system for signing messages.<br>In Matrix, each device has
|
|
||||||
an Ed25519 key pair which serves to identify that device. The private the key should
|
|
||||||
never leave the device, but the public part is published to the Matrix network. **/
|
|
||||||
public static final String JSON_KEY_FINGER_PRINT_KEY = "ed25519";
|
|
||||||
|
|
||||||
/** Account Id returned by JNI.
|
|
||||||
* This value identifies uniquely the native account instance.
|
|
||||||
*/
|
|
||||||
private transient long mNativeId;
|
|
||||||
|
|
||||||
public OlmAccount() throws OlmException {
|
|
||||||
try {
|
|
||||||
mNativeId = createNewAccountJni();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_INIT_ACCOUNT_CREATION, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new account and return it to JAVA side.<br>
|
|
||||||
* Since a C prt is returned as a jlong, special care will be taken
|
|
||||||
* to make the cast (OlmAccount* to jlong) platform independent.
|
|
||||||
* @return the initialized OlmAccount* instance or throw an exception if fails
|
|
||||||
**/
|
|
||||||
private native long createNewAccountJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Getter on the account ID.
|
|
||||||
* @return native account ID
|
|
||||||
*/
|
|
||||||
long getOlmAccountId(){
|
|
||||||
return mNativeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release native account and invalid its JAVA reference counter part.<br>
|
|
||||||
* Public API for {@link #releaseAccountJni()}.
|
|
||||||
*/
|
|
||||||
public void releaseAccount() {
|
|
||||||
if (0 != mNativeId) {
|
|
||||||
releaseAccountJni();
|
|
||||||
}
|
|
||||||
mNativeId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the corresponding OLM account native object.<br>
|
|
||||||
* This method must ALWAYS be called when this JAVA instance
|
|
||||||
* is destroyed (ie. garbage collected) to prevent memory leak in native side.
|
|
||||||
* See {@link #createNewAccountJni()}.
|
|
||||||
*/
|
|
||||||
private native void releaseAccountJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true the object resources have been released.<br>
|
|
||||||
* @return true the object resources have been released
|
|
||||||
*/
|
|
||||||
public boolean isReleased() {
|
|
||||||
return (0 == mNativeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the identity keys (identity and fingerprint keys) in a dictionary.<br>
|
|
||||||
* Public API for {@link #identityKeysJni()}.<br>
|
|
||||||
* Ex:<code>
|
|
||||||
* {
|
|
||||||
* "curve25519":"Vam++zZPMqDQM6ANKpO/uAl5ViJSHxV9hd+b0/fwRAg",
|
|
||||||
* "ed25519":"+v8SOlOASFTMrX3MCKBM4iVnYoZ+JIjpNt1fi8Z9O2I"
|
|
||||||
* }</code>
|
|
||||||
* @return identity keys dictionary if operation succeeds, null otherwise
|
|
||||||
* @exception OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public Map<String, String> identityKeys() throws OlmException {
|
|
||||||
JSONObject identityKeysJsonObj = null;
|
|
||||||
|
|
||||||
byte[] identityKeysBuffer;
|
|
||||||
|
|
||||||
try {
|
|
||||||
identityKeysBuffer = identityKeysJni();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## identityKeys(): Failure - " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_IDENTITY_KEYS, e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null != identityKeysBuffer) {
|
|
||||||
try {
|
|
||||||
identityKeysJsonObj = new JSONObject(new String(identityKeysBuffer, "UTF-8"));
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## identityKeys(): Exception - Msg=" + e.getMessage());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.e(LOG_TAG, "## identityKeys(): Failure - identityKeysJni()=null");
|
|
||||||
}
|
|
||||||
|
|
||||||
return OlmUtility.toStringMap(identityKeysJsonObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the public identity keys (Ed25519 fingerprint key and Curve25519 identity key).<br>
|
|
||||||
* Keys are Base64 encoded.
|
|
||||||
* These keys must be published on the server.
|
|
||||||
* @return the identity keys or throw an exception if it fails
|
|
||||||
*/
|
|
||||||
private native byte[] identityKeysJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the largest number of "one time keys" this account can store.
|
|
||||||
* @return the max number of "one time keys", -1 otherwise
|
|
||||||
*/
|
|
||||||
public long maxOneTimeKeys() {
|
|
||||||
return maxOneTimeKeysJni();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the largest number of "one time keys" this account can store.
|
|
||||||
* @return the max number of "one time keys", -1 otherwise
|
|
||||||
*/
|
|
||||||
private native long maxOneTimeKeysJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a number of new one time keys.<br> If total number of keys stored
|
|
||||||
* by this account exceeds {@link #maxOneTimeKeys()}, the old keys are discarded.<br>
|
|
||||||
* The corresponding keys are retrieved by {@link #oneTimeKeys()}.
|
|
||||||
* @param aNumberOfKeys number of keys to generate
|
|
||||||
* @exception OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public void generateOneTimeKeys(int aNumberOfKeys) throws OlmException {
|
|
||||||
try {
|
|
||||||
generateOneTimeKeysJni(aNumberOfKeys);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_GENERATE_ONE_TIME_KEYS, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a number of new one time keys.<br> If total number of keys stored
|
|
||||||
* by this account exceeds {@link #maxOneTimeKeys()}, the old keys are discarded.
|
|
||||||
* An exception is thrown if the operation fails.<br>
|
|
||||||
* @param aNumberOfKeys number of keys to generate
|
|
||||||
*/
|
|
||||||
private native void generateOneTimeKeysJni(int aNumberOfKeys);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the "one time keys" in a dictionary.<br>
|
|
||||||
* The number of "one time keys", is specified by {@link #generateOneTimeKeys(int)}<br>
|
|
||||||
* Ex:<code>
|
|
||||||
* { "curve25519":
|
|
||||||
* {
|
|
||||||
* "AAAABQ":"qefVZd8qvjOpsFzoKSAdfUnJVkIreyxWFlipCHjSQQg",
|
|
||||||
* "AAAABA":"/X8szMU+p+lsTnr56wKjaLgjTMQQkCk8EIWEAilZtQ8",
|
|
||||||
* "AAAAAw":"qxNxxFHzevFntaaPdT0fhhO7tc7pco4+xB/5VRG81hA",
|
|
||||||
* }
|
|
||||||
* }</code><br>
|
|
||||||
* Public API for {@link #oneTimeKeysJni()}.<br>
|
|
||||||
* Note: these keys are to be published on the server.
|
|
||||||
* @return one time keys in string dictionary.
|
|
||||||
* @exception OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public Map<String, Map<String, String>> oneTimeKeys() throws OlmException {
|
|
||||||
JSONObject oneTimeKeysJsonObj = null;
|
|
||||||
byte[] oneTimeKeysBuffer;
|
|
||||||
|
|
||||||
try {
|
|
||||||
oneTimeKeysBuffer = oneTimeKeysJni();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_ONE_TIME_KEYS, e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
if( null != oneTimeKeysBuffer) {
|
|
||||||
try {
|
|
||||||
oneTimeKeysJsonObj = new JSONObject(new String(oneTimeKeysBuffer, "UTF-8"));
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## oneTimeKeys(): Exception - Msg=" + e.getMessage());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.e(LOG_TAG, "## oneTimeKeys(): Failure - identityKeysJni()=null");
|
|
||||||
}
|
|
||||||
|
|
||||||
return OlmUtility.toStringMapMap(oneTimeKeysJsonObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the public parts of the unpublished "one time keys" for the account.<br>
|
|
||||||
* The returned data is a JSON-formatted object with the single property
|
|
||||||
* <code>curve25519</code>, which is itself an object mapping key id to
|
|
||||||
* base64-encoded Curve25519 key.<br>
|
|
||||||
* @return byte array containing the one time keys or throw an exception if it fails
|
|
||||||
*/
|
|
||||||
private native byte[] oneTimeKeysJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the "one time keys" that the session used from the account.
|
|
||||||
* @param aSession session instance
|
|
||||||
* @throws OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public void removeOneTimeKeys(OlmSession aSession) throws OlmException {
|
|
||||||
if (null != aSession) {
|
|
||||||
try {
|
|
||||||
removeOneTimeKeysJni(aSession.getOlmSessionId());
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_REMOVE_ONE_TIME_KEYS, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the "one time keys" that the session used from the account.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aNativeOlmSessionId native session instance identifier
|
|
||||||
*/
|
|
||||||
private native void removeOneTimeKeysJni(long aNativeOlmSessionId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks the current set of "one time keys" as being published.
|
|
||||||
* @exception OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public void markOneTimeKeysAsPublished() throws OlmException {
|
|
||||||
try {
|
|
||||||
markOneTimeKeysAsPublishedJni();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_MARK_ONE_KEYS_AS_PUBLISHED, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks the current set of "one time keys" as being published.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
*/
|
|
||||||
private native void markOneTimeKeysAsPublishedJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign a message with the ed25519 fingerprint key for this account.<br>
|
|
||||||
* The signed message is returned by the method.
|
|
||||||
* @param aMessage message to sign
|
|
||||||
* @return the signed message
|
|
||||||
* @exception OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public String signMessage(String aMessage) throws OlmException {
|
|
||||||
String result = null;
|
|
||||||
|
|
||||||
if (null != aMessage) {
|
|
||||||
byte[] utf8String = null;
|
|
||||||
try {
|
|
||||||
utf8String = aMessage.getBytes("UTF-8");
|
|
||||||
if (null != utf8String) {
|
|
||||||
byte[] signedMessage = signMessageJni(utf8String);
|
|
||||||
|
|
||||||
if (null != signedMessage) {
|
|
||||||
result = new String(signedMessage, "UTF-8");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_SIGN_MESSAGE, e.getMessage());
|
|
||||||
} finally {
|
|
||||||
if (null != utf8String) {
|
|
||||||
Arrays.fill(utf8String, (byte) 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign a message with the ed25519 fingerprint key for this account.<br>
|
|
||||||
* The signed message is returned by the method.
|
|
||||||
* @param aMessage message to sign
|
|
||||||
* @return the signed message
|
|
||||||
*/
|
|
||||||
private native byte[] signMessageJni(byte[] aMessage);
|
|
||||||
|
|
||||||
//==============================================================================================================
|
|
||||||
// Serialization management
|
|
||||||
//==============================================================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kick off the serialization mechanism.
|
|
||||||
* @param aOutStream output stream for serializing
|
|
||||||
* @throws IOException exception
|
|
||||||
*/
|
|
||||||
private void writeObject(ObjectOutputStream aOutStream) throws IOException {
|
|
||||||
serialize(aOutStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kick off the deserialization mechanism.
|
|
||||||
* @param aInStream input stream
|
|
||||||
* @throws Exception exception
|
|
||||||
*/
|
|
||||||
private void readObject(ObjectInputStream aInStream) throws Exception {
|
|
||||||
deserialize(aInStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an account as a bytes buffer.<br>
|
|
||||||
* The account is serialized and encrypted with aKey.
|
|
||||||
* In case of failure, an error human readable
|
|
||||||
* description is provide in aErrorMsg.
|
|
||||||
* @param aKey encryption key
|
|
||||||
* @param aErrorMsg error message description
|
|
||||||
* @return the account as bytes buffer
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected byte[] serialize(byte[] aKey, StringBuffer aErrorMsg) {
|
|
||||||
byte[] pickleRetValue = null;
|
|
||||||
|
|
||||||
// sanity check
|
|
||||||
if(null == aErrorMsg) {
|
|
||||||
Log.e(LOG_TAG,"## serialize(): invalid parameter - aErrorMsg=null");
|
|
||||||
} else if (null == aKey) {
|
|
||||||
aErrorMsg.append("Invalid input parameters in serializeDataWithKey()");
|
|
||||||
} else {
|
|
||||||
aErrorMsg.setLength(0);
|
|
||||||
try {
|
|
||||||
pickleRetValue = serializeJni(aKey);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## serialize() failed " + e.getMessage());
|
|
||||||
aErrorMsg.append(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pickleRetValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize and encrypt account instance.<br>
|
|
||||||
* @param aKeyBuffer key used to encrypt the serialized account data
|
|
||||||
* @return the serialised account as bytes buffer.
|
|
||||||
**/
|
|
||||||
private native byte[] serializeJni(byte[] aKeyBuffer);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads an account from a pickled bytes buffer.<br>
|
|
||||||
* See {@link #serialize(byte[], StringBuffer)}
|
|
||||||
* @param aSerializedData bytes buffer
|
|
||||||
* @param aKey key used to encrypted
|
|
||||||
* @exception Exception the exception
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void deserialize(byte[] aSerializedData, byte[] aKey) throws Exception {
|
|
||||||
String errorMsg = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if ((null == aSerializedData) || (null == aKey)) {
|
|
||||||
Log.e(LOG_TAG, "## deserialize(): invalid input parameters");
|
|
||||||
errorMsg = "invalid input parameters";
|
|
||||||
} else {
|
|
||||||
mNativeId = deserializeJni(aSerializedData, aKey);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## deserialize() failed " + e.getMessage());
|
|
||||||
errorMsg = e.getMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(errorMsg)) {
|
|
||||||
releaseAccount();
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_DESERIALIZATION, errorMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allocate a new account and initialize it with the serialisation data.<br>
|
|
||||||
* @param aSerializedDataBuffer the account serialisation buffer
|
|
||||||
* @param aKeyBuffer the key used to encrypt the serialized account data
|
|
||||||
* @return the deserialized account
|
|
||||||
**/
|
|
||||||
private native long deserializeJni(byte[] aSerializedDataBuffer, byte[] aKeyBuffer);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a pickled account as a bytes buffer.<br>
|
|
||||||
* The account is serialized and encrypted with aKey.
|
|
||||||
* In case of failure, an error human readable
|
|
||||||
* description is provide in aErrorMsg.
|
|
||||||
* @param aKey encryption key
|
|
||||||
* @param aErrorMsg error message description
|
|
||||||
* @return the pickled account as bytes buffer
|
|
||||||
*/
|
|
||||||
public byte[] pickle(byte[] aKey, StringBuffer aErrorMsg) {
|
|
||||||
return serialize(aKey, aErrorMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads an account from a pickled bytes buffer.<br>
|
|
||||||
* See {@link #serialize(byte[], StringBuffer)}
|
|
||||||
* @param aSerializedData bytes buffer
|
|
||||||
* @param aKey key used to encrypted
|
|
||||||
* @exception Exception the exception
|
|
||||||
*/
|
|
||||||
public void unpickle(byte[] aSerializedData, byte[] aKey) throws Exception {
|
|
||||||
deserialize(aSerializedData, aKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a new fallback key.
|
|
||||||
* @throws OlmException exception with a reason.
|
|
||||||
*/
|
|
||||||
public void generateFallbackKey() throws OlmException {
|
|
||||||
try {
|
|
||||||
generateFallbackKeyJni();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_GENERATE_FALLBACK_KEY, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private native void generateFallbackKeyJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the "fallback key" in a dictionary.<br>
|
|
||||||
* Ex:<code>
|
|
||||||
* { "curve25519":
|
|
||||||
* {
|
|
||||||
* "AAAABQ":"qefVZd8qvjOpsFzoKSAdfUnJVkIreyxWFlipCHjSQQg"
|
|
||||||
* }
|
|
||||||
* }</code><br>
|
|
||||||
* Public API for {@link #fallbackKeyJni()}.<br>
|
|
||||||
* Note: the key is to be published on the server.
|
|
||||||
* @return fallback key in string dictionary.
|
|
||||||
* @exception OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public Map<String, Map<String, String>> fallbackKey() throws OlmException {
|
|
||||||
JSONObject fallbackKeyJsonObj = null;
|
|
||||||
byte[] fallbackKeyBuffer;
|
|
||||||
|
|
||||||
try {
|
|
||||||
fallbackKeyBuffer = fallbackKeyJni();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_FALLBACK_KEY, e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
if( null != fallbackKeyBuffer) {
|
|
||||||
try {
|
|
||||||
fallbackKeyJsonObj = new JSONObject(new String(fallbackKeyBuffer, "UTF-8"));
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## fallbackKey(): Exception - Msg=" + e.getMessage());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.e(LOG_TAG, "## fallbackKey(): Failure - identityKeysJni()=null");
|
|
||||||
}
|
|
||||||
|
|
||||||
return OlmUtility.toStringMapMap(fallbackKeyJsonObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
private native byte[] fallbackKeyJni();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Forget about the old fallback key.
|
|
||||||
*
|
|
||||||
* This should be called once you are reasonably certain that you will not
|
|
||||||
* receive any more messages that use the old fallback key (e.g. 5 minutes
|
|
||||||
* after the new fallback key has been published).
|
|
||||||
* @throws OlmException the failure reason
|
|
||||||
**/
|
|
||||||
public void forgetFallbackKey() throws OlmException {
|
|
||||||
try {
|
|
||||||
forgetFallbackKeyJni();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_FORGET_FALLBACK_KEY, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private native void forgetFallbackKeyJni();
|
|
||||||
}
|
|
|
@ -1,112 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2017 OpenMarket Ltd
|
|
||||||
* Copyright 2017-2019 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.olm;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exception class to identify specific Olm SDK exceptions.
|
|
||||||
*/
|
|
||||||
public class OlmException extends IOException {
|
|
||||||
// exception codes
|
|
||||||
|
|
||||||
public static final int EXCEPTION_CODE_INIT_ACCOUNT_CREATION = 10;
|
|
||||||
|
|
||||||
public static final int EXCEPTION_CODE_ACCOUNT_SERIALIZATION = 100;
|
|
||||||
public static final int EXCEPTION_CODE_ACCOUNT_DESERIALIZATION = 101;
|
|
||||||
public static final int EXCEPTION_CODE_ACCOUNT_IDENTITY_KEYS = 102;
|
|
||||||
public static final int EXCEPTION_CODE_ACCOUNT_GENERATE_ONE_TIME_KEYS = 103;
|
|
||||||
public static final int EXCEPTION_CODE_ACCOUNT_ONE_TIME_KEYS = 104;
|
|
||||||
public static final int EXCEPTION_CODE_ACCOUNT_REMOVE_ONE_TIME_KEYS = 105;
|
|
||||||
public static final int EXCEPTION_CODE_ACCOUNT_MARK_ONE_KEYS_AS_PUBLISHED = 106;
|
|
||||||
public static final int EXCEPTION_CODE_ACCOUNT_SIGN_MESSAGE = 107;
|
|
||||||
public static final int EXCEPTION_CODE_ACCOUNT_GENERATE_FALLBACK_KEY = 108;
|
|
||||||
public static final int EXCEPTION_CODE_ACCOUNT_FALLBACK_KEY = 109;
|
|
||||||
public static final int EXCEPTION_CODE_ACCOUNT_FORGET_FALLBACK_KEY = 110;
|
|
||||||
|
|
||||||
public static final int EXCEPTION_CODE_CREATE_INBOUND_GROUP_SESSION = 200;
|
|
||||||
public static final int EXCEPTION_CODE_INIT_INBOUND_GROUP_SESSION = 201;
|
|
||||||
public static final int EXCEPTION_CODE_INBOUND_GROUP_SESSION_IDENTIFIER = 202;
|
|
||||||
public static final int EXCEPTION_CODE_INBOUND_GROUP_SESSION_DECRYPT_SESSION = 203;
|
|
||||||
public static final int EXCEPTION_CODE_INBOUND_GROUP_SESSION_FIRST_KNOWN_INDEX = 204;
|
|
||||||
public static final int EXCEPTION_CODE_INBOUND_GROUP_SESSION_IS_VERIFIED = 205;
|
|
||||||
public static final int EXCEPTION_CODE_INBOUND_GROUP_SESSION_EXPORT = 206;
|
|
||||||
|
|
||||||
public static final int EXCEPTION_CODE_CREATE_OUTBOUND_GROUP_SESSION = 300;
|
|
||||||
public static final int EXCEPTION_CODE_INIT_OUTBOUND_GROUP_SESSION = 301;
|
|
||||||
public static final int EXCEPTION_CODE_OUTBOUND_GROUP_SESSION_IDENTIFIER = 302;
|
|
||||||
public static final int EXCEPTION_CODE_OUTBOUND_GROUP_SESSION_KEY = 303;
|
|
||||||
public static final int EXCEPTION_CODE_OUTBOUND_GROUP_ENCRYPT_MESSAGE = 304;
|
|
||||||
|
|
||||||
public static final int EXCEPTION_CODE_INIT_SESSION_CREATION = 400;
|
|
||||||
public static final int EXCEPTION_CODE_SESSION_INIT_OUTBOUND_SESSION = 401;
|
|
||||||
public static final int EXCEPTION_CODE_SESSION_INIT_INBOUND_SESSION = 402;
|
|
||||||
public static final int EXCEPTION_CODE_SESSION_INIT_INBOUND_SESSION_FROM = 403;
|
|
||||||
public static final int EXCEPTION_CODE_SESSION_ENCRYPT_MESSAGE = 404;
|
|
||||||
public static final int EXCEPTION_CODE_SESSION_DECRYPT_MESSAGE = 405;
|
|
||||||
public static final int EXCEPTION_CODE_SESSION_SESSION_IDENTIFIER = 406;
|
|
||||||
public static final int EXCEPTION_CODE_SESSION_SESSION_DESCRIBE = 407;
|
|
||||||
|
|
||||||
public static final int EXCEPTION_CODE_UTILITY_CREATION = 500;
|
|
||||||
public static final int EXCEPTION_CODE_UTILITY_VERIFY_SIGNATURE = 501;
|
|
||||||
|
|
||||||
public static final int EXCEPTION_CODE_PK_ENCRYPTION_CREATION = 600;
|
|
||||||
public static final int EXCEPTION_CODE_PK_ENCRYPTION_SET_RECIPIENT_KEY = 601;
|
|
||||||
public static final int EXCEPTION_CODE_PK_ENCRYPTION_ENCRYPT = 602;
|
|
||||||
|
|
||||||
public static final int EXCEPTION_CODE_PK_DECRYPTION_CREATION = 700;
|
|
||||||
public static final int EXCEPTION_CODE_PK_DECRYPTION_GENERATE_KEY = 701;
|
|
||||||
public static final int EXCEPTION_CODE_PK_DECRYPTION_DECRYPT = 702;
|
|
||||||
public static final int EXCEPTION_CODE_PK_DECRYPTION_SET_PRIVATE_KEY = 703;
|
|
||||||
public static final int EXCEPTION_CODE_PK_DECRYPTION_PRIVATE_KEY = 704;
|
|
||||||
|
|
||||||
public static final int EXCEPTION_CODE_PK_SIGNING_CREATION = 800;
|
|
||||||
public static final int EXCEPTION_CODE_PK_SIGNING_GENERATE_SEED = 801;
|
|
||||||
public static final int EXCEPTION_CODE_PK_SIGNING_INIT_WITH_SEED = 802;
|
|
||||||
public static final int EXCEPTION_CODE_PK_SIGNING_SIGN = 803;
|
|
||||||
|
|
||||||
public static final int EXCEPTION_CODE_SAS_CREATION = 900;
|
|
||||||
public static final int EXCEPTION_CODE_SAS_ERROR = 901;
|
|
||||||
public static final int EXCEPTION_CODE_SAS_MISSING_THEIR_PKEY = 902;
|
|
||||||
public static final int EXCEPTION_CODE_SAS_GENERATE_SHORT_CODE = 903;
|
|
||||||
|
|
||||||
// exception human readable messages
|
|
||||||
public static final String EXCEPTION_MSG_INVALID_PARAMS_DESERIALIZATION = "invalid de-serialized parameters";
|
|
||||||
|
|
||||||
/** exception code to be taken from: {@link #EXCEPTION_CODE_CREATE_OUTBOUND_GROUP_SESSION}, {@link #EXCEPTION_CODE_CREATE_INBOUND_GROUP_SESSION},
|
|
||||||
* {@link #EXCEPTION_CODE_INIT_OUTBOUND_GROUP_SESSION}, {@link #EXCEPTION_CODE_INIT_INBOUND_GROUP_SESSION}..**/
|
|
||||||
private final int mCode;
|
|
||||||
|
|
||||||
/** Human readable message description **/
|
|
||||||
private final String mMessage;
|
|
||||||
|
|
||||||
public OlmException(int aExceptionCode, String aExceptionMessage) {
|
|
||||||
super();
|
|
||||||
mCode = aExceptionCode;
|
|
||||||
mMessage = aExceptionMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getExceptionCode() {
|
|
||||||
return mCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getMessage() {
|
|
||||||
return mMessage;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,397 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2017 OpenMarket Ltd
|
|
||||||
* Copyright 2017 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.olm;
|
|
||||||
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class used to create an inbound <a href="http://matrix.org/docs/guides/e2e_implementation.html#handling-an-m-room-key-event">Megolm session</a>.<br>
|
|
||||||
* Counter part of the outbound group session {@link OlmOutboundGroupSession}, this class decrypts the messages sent by the outbound side.
|
|
||||||
*
|
|
||||||
* <br><br>Detailed implementation guide is available at <a href="http://matrix.org/docs/guides/e2e_implementation.html">Implementing End-to-End Encryption in Matrix clients</a>.
|
|
||||||
*/
|
|
||||||
public class OlmInboundGroupSession extends CommonSerializeUtils implements Serializable {
|
|
||||||
private static final long serialVersionUID = -772028491251653253L;
|
|
||||||
private static final String LOG_TAG = "OlmInboundGroupSession";
|
|
||||||
|
|
||||||
/** Session Id returned by JNI.<br>
|
|
||||||
* This value uniquely identifies the native inbound group session instance.
|
|
||||||
*/
|
|
||||||
private transient long mNativeId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Result in {@link #decryptMessage(String)}
|
|
||||||
*/
|
|
||||||
public static class DecryptMessageResult {
|
|
||||||
/** decrypt message **/
|
|
||||||
public String mDecryptedMessage;
|
|
||||||
|
|
||||||
/** decrypt index **/
|
|
||||||
public long mIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.<br>
|
|
||||||
* Create and save a new native session instance ID and start a new inbound group session.
|
|
||||||
* The session key parameter is retrieved from an outbound group session.
|
|
||||||
* @param aSessionKey session key
|
|
||||||
* @throws OlmException constructor failure
|
|
||||||
*/
|
|
||||||
public OlmInboundGroupSession(String aSessionKey) throws OlmException {
|
|
||||||
this(aSessionKey, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.<br>
|
|
||||||
* Create and save a new native session instance ID and start a new inbound group session.
|
|
||||||
* The session key parameter is retrieved from an outbound group session.
|
|
||||||
* @param aSessionKey session key
|
|
||||||
* @param isImported true when the session key has been retrieved from a backup
|
|
||||||
* @throws OlmException constructor failure
|
|
||||||
*/
|
|
||||||
private OlmInboundGroupSession(String aSessionKey, boolean isImported) throws OlmException {
|
|
||||||
if (TextUtils.isEmpty(aSessionKey)) {
|
|
||||||
Log.e(LOG_TAG, "## initInboundGroupSession(): invalid session key");
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_INIT_INBOUND_GROUP_SESSION, "invalid session key");
|
|
||||||
} else {
|
|
||||||
byte[] sessionBuffer = null;
|
|
||||||
try {
|
|
||||||
sessionBuffer = aSessionKey.getBytes("UTF-8");
|
|
||||||
mNativeId = createNewSessionJni(aSessionKey.getBytes("UTF-8"), isImported);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_INIT_INBOUND_GROUP_SESSION, e.getMessage());
|
|
||||||
} finally {
|
|
||||||
if (null != sessionBuffer) {
|
|
||||||
Arrays.fill(sessionBuffer, (byte) 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize a new inbound group session and return it to JAVA side.<br>
|
|
||||||
* Since a C prt is returned as a jlong, special care will be taken
|
|
||||||
* to make the cast (OlmInboundGroupSession* to jlong) platform independent.
|
|
||||||
* @param aSessionKeyBuffer session key from an outbound session
|
|
||||||
* @param isImported true when the session key has been retrieved from a backup
|
|
||||||
* @return the initialized OlmInboundGroupSession* instance or throw an exception it fails.
|
|
||||||
**/
|
|
||||||
private native long createNewSessionJni(byte[] aSessionKeyBuffer, boolean isImported);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an OlmInboundGroupSession from its exported session data.
|
|
||||||
* @param exported the exported data
|
|
||||||
* @return the created OlmException
|
|
||||||
* @throws OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public static OlmInboundGroupSession importSession(String exported) throws OlmException {
|
|
||||||
return new OlmInboundGroupSession(exported, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release native session and invalid its JAVA reference counter part.<br>
|
|
||||||
* Public API for {@link #releaseSessionJni()}.
|
|
||||||
*/
|
|
||||||
public void releaseSession(){
|
|
||||||
if (0 != mNativeId) {
|
|
||||||
releaseSessionJni();
|
|
||||||
}
|
|
||||||
mNativeId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the corresponding OLM inbound group session native object.<br>
|
|
||||||
* This method must ALWAYS be called when this JAVA instance
|
|
||||||
* is destroyed (ie. garbage collected) to prevent memory leak in native side.
|
|
||||||
* See {@link #createNewSessionJni(byte[], boolean)}.
|
|
||||||
*/
|
|
||||||
private native void releaseSessionJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true the object resources have been released.<br>
|
|
||||||
* @return true the object resources have been released
|
|
||||||
*/
|
|
||||||
public boolean isReleased() {
|
|
||||||
return (0 == mNativeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the base64-encoded identifier for this inbound group session.
|
|
||||||
* @return the session ID
|
|
||||||
* @throws OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public String sessionIdentifier() throws OlmException {
|
|
||||||
try {
|
|
||||||
return new String(sessionIdentifierJni(), "UTF-8");
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## sessionIdentifier() failed " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_INBOUND_GROUP_SESSION_IDENTIFIER, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a base64-encoded identifier for this inbound group session.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @return the base64-encoded identifier
|
|
||||||
*/
|
|
||||||
private native byte[] sessionIdentifierJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides the first known index.
|
|
||||||
* @return the first known index.
|
|
||||||
* @throws OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public long getFirstKnownIndex() throws OlmException {
|
|
||||||
long index = 0;
|
|
||||||
|
|
||||||
try {
|
|
||||||
index = firstKnownIndexJni();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## getFirstKnownIndex() failed " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_INBOUND_GROUP_SESSION_FIRST_KNOWN_INDEX, e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides the first known index.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @return the first known index.
|
|
||||||
*/
|
|
||||||
private native long firstKnownIndexJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells if the session is verified.
|
|
||||||
* @return true if the session is verified
|
|
||||||
* @throws OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public boolean isVerified() throws OlmException {
|
|
||||||
boolean isVerified;
|
|
||||||
|
|
||||||
try {
|
|
||||||
isVerified = isVerifiedJni();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## isVerified() failed " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_INBOUND_GROUP_SESSION_IS_VERIFIED, e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return isVerified;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells if the session is verified.
|
|
||||||
* @return true if the session is verified
|
|
||||||
*/
|
|
||||||
private native boolean isVerifiedJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export the session from a message index as String.
|
|
||||||
* @param messageIndex the message index
|
|
||||||
* @return the session as String
|
|
||||||
* @throws OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public String export(long messageIndex) throws OlmException {
|
|
||||||
String result = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
byte[] bytesBuffer = exportJni(messageIndex);
|
|
||||||
|
|
||||||
if (null != bytesBuffer) {
|
|
||||||
result = new String(bytesBuffer, "UTF-8");
|
|
||||||
Arrays.fill(bytesBuffer, (byte) 0);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## export() failed " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_INBOUND_GROUP_SESSION_EXPORT, e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exports the session as byte array from a message index
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param messageIndex key used to encrypt the serialized session data
|
|
||||||
* @return the session saved as bytes array
|
|
||||||
*/
|
|
||||||
private native byte[] exportJni(long messageIndex);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypt the message passed in parameter.<br>
|
|
||||||
* In case of error, null is returned and an error message description is provided in aErrorMsg.
|
|
||||||
* @param aEncryptedMsg the message to be decrypted
|
|
||||||
* @return the decrypted message information
|
|
||||||
* @exception OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public DecryptMessageResult decryptMessage(String aEncryptedMsg) throws OlmException {
|
|
||||||
DecryptMessageResult result = new DecryptMessageResult();
|
|
||||||
|
|
||||||
try {
|
|
||||||
byte[] decryptedMessageBuffer = decryptMessageJni(aEncryptedMsg.getBytes("UTF-8"), result);
|
|
||||||
|
|
||||||
if (null != decryptedMessageBuffer) {
|
|
||||||
result.mDecryptedMessage = new String(decryptedMessageBuffer, "UTF-8");
|
|
||||||
Arrays.fill(decryptedMessageBuffer, (byte) 0);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## decryptMessage() failed " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_INBOUND_GROUP_SESSION_DECRYPT_SESSION, e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypt a message.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aEncryptedMsg the encrypted message
|
|
||||||
* @param aDecryptMessageResult the decryptMessage information
|
|
||||||
* @return the decrypted message
|
|
||||||
*/
|
|
||||||
private native byte[] decryptMessageJni(byte[] aEncryptedMsg, DecryptMessageResult aDecryptMessageResult);
|
|
||||||
|
|
||||||
//==============================================================================================================
|
|
||||||
// Serialization management
|
|
||||||
//==============================================================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kick off the serialization mechanism.
|
|
||||||
* @param aOutStream output stream for serializing
|
|
||||||
* @throws IOException exception
|
|
||||||
*/
|
|
||||||
private void writeObject(ObjectOutputStream aOutStream) throws IOException {
|
|
||||||
serialize(aOutStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kick off the deserialization mechanism.
|
|
||||||
* @param aInStream input stream
|
|
||||||
* @throws Exception exception
|
|
||||||
*/
|
|
||||||
private void readObject(ObjectInputStream aInStream) throws Exception {
|
|
||||||
deserialize(aInStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the current inbound group session as a bytes buffer.<br>
|
|
||||||
* The session is serialized and encrypted with aKey.
|
|
||||||
* In case of failure, an error human readable
|
|
||||||
* description is provide in aErrorMsg.
|
|
||||||
* @param aKey encryption key
|
|
||||||
* @param aErrorMsg error message description
|
|
||||||
* @return pickled bytes buffer if operation succeed, null otherwise
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected byte[] serialize(byte[] aKey, StringBuffer aErrorMsg) {
|
|
||||||
byte[] pickleRetValue = null;
|
|
||||||
|
|
||||||
// sanity check
|
|
||||||
if(null == aErrorMsg) {
|
|
||||||
Log.e(LOG_TAG,"## serialize(): invalid parameter - aErrorMsg=null");
|
|
||||||
} else if (null == aKey) {
|
|
||||||
aErrorMsg.append("Invalid input parameters in serialize()");
|
|
||||||
} else {
|
|
||||||
aErrorMsg.setLength(0);
|
|
||||||
try {
|
|
||||||
pickleRetValue = serializeJni(aKey);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## serialize() failed " + e.getMessage());
|
|
||||||
aErrorMsg.append(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pickleRetValue;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* JNI counter part of {@link #serialize(byte[], StringBuffer)}.
|
|
||||||
* @param aKey encryption key
|
|
||||||
* @return the serialized session
|
|
||||||
*/
|
|
||||||
private native byte[] serializeJni(byte[] aKey);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads an account from a pickled base64 string.<br>
|
|
||||||
* See {@link #serialize(byte[], StringBuffer)}
|
|
||||||
* @param aSerializedData pickled account in a bytes buffer
|
|
||||||
* @param aKey key used to encrypted
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void deserialize(byte[] aSerializedData, byte[] aKey) throws Exception {
|
|
||||||
String errorMsg = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if ((null == aSerializedData) || (null == aKey)) {
|
|
||||||
Log.e(LOG_TAG, "## deserialize(): invalid input parameters");
|
|
||||||
errorMsg = "invalid input parameters";
|
|
||||||
} else {
|
|
||||||
mNativeId = deserializeJni(aSerializedData, aKey);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## deserialize() failed " + e.getMessage());
|
|
||||||
errorMsg = e.getMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(errorMsg)) {
|
|
||||||
releaseSession();
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_DESERIALIZATION, errorMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allocate a new session and initialize it with the serialisation data.<br>
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aSerializedData the session serialisation buffer
|
|
||||||
* @param aKey the key used to encrypt the serialized account data
|
|
||||||
* @return the deserialized session
|
|
||||||
**/
|
|
||||||
private native long deserializeJni(byte[] aSerializedData, byte[] aKey);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a pickled inbound group session as a bytes buffer.<br>
|
|
||||||
* The session is serialized and encrypted with aKey.
|
|
||||||
* In case of failure, an error human readable
|
|
||||||
* description is provide in aErrorMsg.
|
|
||||||
* @param aKey encryption key
|
|
||||||
* @param aErrorMsg error message description
|
|
||||||
* @return the pickled inbound group session as bytes buffer
|
|
||||||
*/
|
|
||||||
public byte[] pickle(byte[] aKey, StringBuffer aErrorMsg) {
|
|
||||||
return serialize(aKey, aErrorMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads an inbound group session from a pickled bytes buffer.<br>
|
|
||||||
* See {@link #serialize(byte[], StringBuffer)}
|
|
||||||
* @param aSerializedData bytes buffer
|
|
||||||
* @param aKey key used to encrypted
|
|
||||||
* @exception Exception the exception
|
|
||||||
*/
|
|
||||||
public void unpickle(byte[] aSerializedData, byte[] aKey) throws Exception {
|
|
||||||
deserialize(aSerializedData, aKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.olm;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Olm SDK entry point class.<br> An OlmManager instance must be created at first to enable native library load.
|
|
||||||
* <br><br>Detailed implementation guide is available at <a href="http://matrix.org/docs/guides/e2e_implementation.html">Implementing End-to-End Encryption in Matrix clients</a>.
|
|
||||||
*/
|
|
||||||
public class OlmManager {
|
|
||||||
private static final String LOG_TAG = "OlmManager";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*/
|
|
||||||
public OlmManager() {
|
|
||||||
}
|
|
||||||
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
java.lang.System.loadLibrary("olm");
|
|
||||||
} catch(UnsatisfiedLinkError e) {
|
|
||||||
Log.e(LOG_TAG,"Exception loadLibrary() - Msg="+e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provide the android library version
|
|
||||||
* @return the library version
|
|
||||||
*/
|
|
||||||
public String getVersion() {
|
|
||||||
return BuildConfig.OLM_VERSION;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provide a detailed version.
|
|
||||||
* It contains the android and the native libraries versions.
|
|
||||||
* @param context the context
|
|
||||||
* @return the detailed version
|
|
||||||
*/
|
|
||||||
public String getDetailedVersion(Context context) {
|
|
||||||
String gitVersion = context.getResources().getString(R.string.git_olm_revision);
|
|
||||||
String date = context.getResources().getString(R.string.git_olm_revision_date);
|
|
||||||
return getVersion() + " - olm version (" + getOlmLibVersion() + ") - " + gitVersion + "-" + date;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provide the native OLM lib version.
|
|
||||||
* @return the lib version as a string
|
|
||||||
*/
|
|
||||||
public String getOlmLibVersion(){
|
|
||||||
return getOlmLibVersionJni();
|
|
||||||
}
|
|
||||||
public native String getOlmLibVersionJni();
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.olm;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message class used in Olm sessions to contain the encrypted data.<br>
|
|
||||||
* See {@link OlmSession#decryptMessage(OlmMessage)} and {@link OlmSession#encryptMessage(String)}.
|
|
||||||
* <br>Detailed implementation guide is available at <a href="http://matrix.org/docs/guides/e2e_implementation.html">Implementing End-to-End Encryption in Matrix clients</a>.
|
|
||||||
*/
|
|
||||||
public class OlmMessage {
|
|
||||||
/** PRE KEY message type (used to establish new Olm session) **/
|
|
||||||
public final static int MESSAGE_TYPE_PRE_KEY = 0;
|
|
||||||
/** normal message type **/
|
|
||||||
public final static int MESSAGE_TYPE_MESSAGE = 1;
|
|
||||||
|
|
||||||
/** the encrypted message **/
|
|
||||||
public String mCipherText;
|
|
||||||
|
|
||||||
/** defined by {@link #MESSAGE_TYPE_MESSAGE} or {@link #MESSAGE_TYPE_PRE_KEY}**/
|
|
||||||
public long mType;
|
|
||||||
}
|
|
|
@ -1,320 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2017 OpenMarket Ltd
|
|
||||||
* Copyright 2017 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.olm;
|
|
||||||
|
|
||||||
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class used to create an outbound a <a href="http://matrix.org/docs/guides/e2e_implementation.html#starting-a-megolm-session">Megolm session</a>.<br>
|
|
||||||
* To send a first message in an encrypted room, the client should start a new outbound Megolm session.
|
|
||||||
* The session ID and the session key must be shared with each device in the room within.
|
|
||||||
*
|
|
||||||
* <br><br>Detailed implementation guide is available at <a href="http://matrix.org/docs/guides/e2e_implementation.html">Implementing End-to-End Encryption in Matrix clients</a>.
|
|
||||||
*/
|
|
||||||
public class OlmOutboundGroupSession extends CommonSerializeUtils implements Serializable {
|
|
||||||
private static final long serialVersionUID = -3133097431283604416L;
|
|
||||||
private static final String LOG_TAG = "OlmOutboundGroupSession";
|
|
||||||
|
|
||||||
/** Session Id returned by JNI.<br>
|
|
||||||
* This value uniquely identifies the native outbound group session instance.
|
|
||||||
*/
|
|
||||||
private transient long mNativeId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.<br>
|
|
||||||
* Create and save a new session native instance ID and
|
|
||||||
* initialise a new outbound group session.<br>
|
|
||||||
* @throws OlmException constructor failure
|
|
||||||
*/
|
|
||||||
public OlmOutboundGroupSession() throws OlmException {
|
|
||||||
try {
|
|
||||||
mNativeId = createNewSessionJni();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_CREATE_OUTBOUND_GROUP_SESSION, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the corresponding OLM outbound group session in native side.<br>
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* Do not forget to call {@link #releaseSession()} when JAVA side is done.
|
|
||||||
* @return native session instance identifier (see {@link #mNativeId})
|
|
||||||
*/
|
|
||||||
private native long createNewSessionJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release native session and invalid its JAVA reference counter part.<br>
|
|
||||||
* Public API for {@link #releaseSessionJni()}.
|
|
||||||
*/
|
|
||||||
public void releaseSession() {
|
|
||||||
if (0 != mNativeId) {
|
|
||||||
releaseSessionJni();
|
|
||||||
}
|
|
||||||
mNativeId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the corresponding OLM outbound group session native object.<br>
|
|
||||||
* This method must ALWAYS be called when this JAVA instance
|
|
||||||
* is destroyed (ie. garbage collected) to prevent memory leak in native side.
|
|
||||||
* See {@link #createNewSessionJni()}.
|
|
||||||
*/
|
|
||||||
private native void releaseSessionJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true the object resources have been released.<br>
|
|
||||||
* @return true the object resources have been released
|
|
||||||
*/
|
|
||||||
public boolean isReleased() {
|
|
||||||
return (0 == mNativeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a base64-encoded identifier for this session.
|
|
||||||
* @return session identifier
|
|
||||||
* @throws OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public String sessionIdentifier() throws OlmException {
|
|
||||||
try {
|
|
||||||
return new String(sessionIdentifierJni(), "UTF-8");
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## sessionIdentifier() failed " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_OUTBOUND_GROUP_SESSION_IDENTIFIER, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the session identifier.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @return the session identifier
|
|
||||||
*/
|
|
||||||
private native byte[] sessionIdentifierJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current message index for this session.<br>
|
|
||||||
* Each message is sent with an increasing index, this
|
|
||||||
* method returns the index for the next message.
|
|
||||||
* @return current session index
|
|
||||||
*/
|
|
||||||
public int messageIndex() {
|
|
||||||
return messageIndexJni();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current message index for this session.<br>
|
|
||||||
* Each message is sent with an increasing index, this
|
|
||||||
* method returns the index for the next message.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @return current session index
|
|
||||||
*/
|
|
||||||
private native int messageIndexJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the base64-encoded current ratchet key for this session.<br>
|
|
||||||
* Each message is sent with a different ratchet key. This method returns the
|
|
||||||
* ratchet key that will be used for the next message.
|
|
||||||
* @return outbound session key
|
|
||||||
* @exception OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public String sessionKey() throws OlmException {
|
|
||||||
try {
|
|
||||||
byte[] sessionKeyBuffer = sessionKeyJni();
|
|
||||||
String ret = new String(sessionKeyBuffer, "UTF-8");
|
|
||||||
Arrays.fill(sessionKeyBuffer, (byte) 0);
|
|
||||||
return ret;
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## sessionKey() failed " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_OUTBOUND_GROUP_SESSION_KEY, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the session key.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @return the session key
|
|
||||||
*/
|
|
||||||
private native byte[] sessionKeyJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt some plain-text message.<br>
|
|
||||||
* The message given as parameter is encrypted and returned as the return value.
|
|
||||||
* @param aClearMsg message to be encrypted
|
|
||||||
* @return the encrypted message
|
|
||||||
* @exception OlmException the encryption failure reason
|
|
||||||
*/
|
|
||||||
public String encryptMessage(String aClearMsg) throws OlmException {
|
|
||||||
String retValue = null;
|
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(aClearMsg)) {
|
|
||||||
try {
|
|
||||||
byte[] clearMsgBuffer = aClearMsg.getBytes("UTF-8");
|
|
||||||
byte[] encryptedBuffer = encryptMessageJni(clearMsgBuffer);
|
|
||||||
Arrays.fill(clearMsgBuffer, (byte) 0);
|
|
||||||
|
|
||||||
if (null != encryptedBuffer) {
|
|
||||||
retValue = new String(encryptedBuffer , "UTF-8");
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## encryptMessage() failed " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_OUTBOUND_GROUP_ENCRYPT_MESSAGE, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return retValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt a bytes buffer messages.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aClearMsgBuffer the message to encode
|
|
||||||
* @return the encoded message
|
|
||||||
*/
|
|
||||||
private native byte[] encryptMessageJni(byte[] aClearMsgBuffer);
|
|
||||||
|
|
||||||
//==============================================================================================================
|
|
||||||
// Serialization management
|
|
||||||
//==============================================================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kick off the serialization mechanism.
|
|
||||||
* @param aOutStream output stream for serializing
|
|
||||||
* @throws IOException exception
|
|
||||||
*/
|
|
||||||
private void writeObject(ObjectOutputStream aOutStream) throws IOException {
|
|
||||||
serialize(aOutStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kick off the deserialization mechanism.
|
|
||||||
* @param aInStream input stream
|
|
||||||
* @throws Exception exception
|
|
||||||
*/
|
|
||||||
private void readObject(ObjectInputStream aInStream) throws Exception {
|
|
||||||
deserialize(aInStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the current outbound group session as a base64 byte buffers.<br>
|
|
||||||
* The session is serialized and encrypted with aKey.
|
|
||||||
* In case of failure, an error human readable
|
|
||||||
* description is provide in aErrorMsg.
|
|
||||||
* @param aKey encryption key
|
|
||||||
* @param aErrorMsg error message description
|
|
||||||
* @return pickled base64 bytes buffer if operation succeed, null otherwise
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected byte[] serialize(byte[] aKey, StringBuffer aErrorMsg) {
|
|
||||||
byte[] pickleRetValue = null;
|
|
||||||
|
|
||||||
// sanity check
|
|
||||||
if(null == aErrorMsg) {
|
|
||||||
Log.e(LOG_TAG,"## serialize(): invalid parameter - aErrorMsg=null");
|
|
||||||
} else if (null == aKey) {
|
|
||||||
aErrorMsg.append("Invalid input parameters in serialize()");
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
pickleRetValue = serializeJni(aKey);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG,"## serialize(): failed " + e.getMessage());
|
|
||||||
aErrorMsg.append(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pickleRetValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JNI counter part of {@link #serialize(byte[], StringBuffer)}.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aKey encryption key
|
|
||||||
* @return the serialized session
|
|
||||||
*/
|
|
||||||
private native byte[] serializeJni(byte[] aKey);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads an account from a pickled base64 string.<br>
|
|
||||||
* See {@link #serialize(byte[], StringBuffer)}
|
|
||||||
* @param aSerializedData pickled account in a base64 bytes buffer
|
|
||||||
* @param aKey key used to encrypted
|
|
||||||
* @exception Exception the exception
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void deserialize(byte[] aSerializedData, byte[] aKey) throws Exception {
|
|
||||||
String errorMsg = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if ((null == aSerializedData) || (null == aKey)) {
|
|
||||||
Log.e(LOG_TAG, "## deserialize(): invalid input parameters");
|
|
||||||
errorMsg = "invalid input parameters";
|
|
||||||
} else {
|
|
||||||
mNativeId = deserializeJni(aSerializedData, aKey);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## deserialize() failed " + e.getMessage());
|
|
||||||
errorMsg = e.getMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(errorMsg)) {
|
|
||||||
releaseSession();
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_DESERIALIZATION, errorMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allocate a new session and initialize it with the serialisation data.<br>
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aSerializedData the session serialisation buffer
|
|
||||||
* @param aKey the key used to encrypt the serialized account data
|
|
||||||
* @return the deserialized session
|
|
||||||
**/
|
|
||||||
private native long deserializeJni(byte[] aSerializedData, byte[] aKey);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a pickled outbound group session as a bytes buffer.<br>
|
|
||||||
* The session is serialized and encrypted with aKey.
|
|
||||||
* In case of failure, an error human readable
|
|
||||||
* description is provide in aErrorMsg.
|
|
||||||
* @param aKey encryption key
|
|
||||||
* @param aErrorMsg error message description
|
|
||||||
* @return the pickled outbound group session as bytes buffer
|
|
||||||
*/
|
|
||||||
public byte[] pickle(byte[] aKey, StringBuffer aErrorMsg) {
|
|
||||||
return serialize(aKey, aErrorMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads an outbound group session from a pickled bytes buffer.<br>
|
|
||||||
* See {@link #serialize(byte[], StringBuffer)}
|
|
||||||
* @param aSerializedData bytes buffer
|
|
||||||
* @param aKey key used to encrypted
|
|
||||||
* @exception Exception the exception
|
|
||||||
*/
|
|
||||||
public void unpickle(byte[] aSerializedData, byte[] aKey) throws Exception {
|
|
||||||
deserialize(aSerializedData, aKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2018 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.olm;
|
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
public class OlmPkDecryption {
|
|
||||||
private static final String LOG_TAG = "OlmPkDecryption";
|
|
||||||
|
|
||||||
/** Session Id returned by JNI.
|
|
||||||
* This value uniquely identifies the native session instance.
|
|
||||||
**/
|
|
||||||
private transient long mNativeId;
|
|
||||||
|
|
||||||
public OlmPkDecryption() throws OlmException {
|
|
||||||
try {
|
|
||||||
mNativeId = createNewPkDecryptionJni();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_PK_DECRYPTION_CREATION, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private native long createNewPkDecryptionJni();
|
|
||||||
|
|
||||||
private native void releasePkDecryptionJni();
|
|
||||||
|
|
||||||
public void releaseDecryption() {
|
|
||||||
if (0 != mNativeId) {
|
|
||||||
releasePkDecryptionJni();
|
|
||||||
}
|
|
||||||
mNativeId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isReleased() {
|
|
||||||
return (0 == mNativeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static native int privateKeyLength();
|
|
||||||
|
|
||||||
public String setPrivateKey(byte[] privateKey) throws OlmException {
|
|
||||||
try {
|
|
||||||
byte[] key = setPrivateKeyJni(privateKey);
|
|
||||||
return new String(key, "UTF-8");
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## setPrivateKey(): failed " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_PK_DECRYPTION_SET_PRIVATE_KEY, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private native byte[] setPrivateKeyJni(byte[] privateKey);
|
|
||||||
|
|
||||||
public String generateKey() throws OlmException {
|
|
||||||
try {
|
|
||||||
byte[] key = generateKeyJni();
|
|
||||||
return new String(key, "UTF-8");
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## setRecipientKey(): failed " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_PK_DECRYPTION_GENERATE_KEY, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private native byte[] generateKeyJni();
|
|
||||||
|
|
||||||
public byte[] privateKey() throws OlmException {
|
|
||||||
try {
|
|
||||||
return privateKeyJni();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## privateKey(): failed " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_PK_DECRYPTION_PRIVATE_KEY, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private native byte[] privateKeyJni();
|
|
||||||
|
|
||||||
public String decrypt(OlmPkMessage aMessage) throws OlmException {
|
|
||||||
if (null == aMessage) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] plaintextBuffer = decryptJni(aMessage);
|
|
||||||
try {
|
|
||||||
String plaintext = new String(plaintextBuffer, "UTF-8");
|
|
||||||
return plaintext;
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## pkDecrypt(): failed " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_PK_DECRYPTION_DECRYPT, e.getMessage());
|
|
||||||
} finally {
|
|
||||||
Arrays.fill(plaintextBuffer, (byte) 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private native byte[] decryptJni(OlmPkMessage aMessage);
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2018 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.olm;
|
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
public class OlmPkEncryption {
|
|
||||||
private static final String LOG_TAG = "OlmPkEncryption";
|
|
||||||
|
|
||||||
/** Session Id returned by JNI.
|
|
||||||
* This value uniquely identifies the native session instance.
|
|
||||||
**/
|
|
||||||
private transient long mNativeId;
|
|
||||||
|
|
||||||
public OlmPkEncryption() throws OlmException {
|
|
||||||
try {
|
|
||||||
mNativeId = createNewPkEncryptionJni();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_PK_ENCRYPTION_CREATION, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private native long createNewPkEncryptionJni();
|
|
||||||
|
|
||||||
private native void releasePkEncryptionJni();
|
|
||||||
|
|
||||||
public void releaseEncryption() {
|
|
||||||
if (0 != mNativeId) {
|
|
||||||
releasePkEncryptionJni();
|
|
||||||
}
|
|
||||||
mNativeId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isReleased() {
|
|
||||||
return (0 == mNativeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRecipientKey(String aKey) throws OlmException {
|
|
||||||
if (null == aKey) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
setRecipientKeyJni(aKey.getBytes("UTF-8"));
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## setRecipientKey(): failed " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_PK_ENCRYPTION_SET_RECIPIENT_KEY, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private native void setRecipientKeyJni(byte[] aKey);
|
|
||||||
|
|
||||||
public OlmPkMessage encrypt(String aPlaintext) throws OlmException {
|
|
||||||
if (null == aPlaintext) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
OlmPkMessage encryptedMsgRetValue = new OlmPkMessage();
|
|
||||||
|
|
||||||
byte[] plaintextBuffer = null;
|
|
||||||
try {
|
|
||||||
plaintextBuffer = aPlaintext.getBytes("UTF-8");
|
|
||||||
byte[] ciphertextBuffer = encryptJni(plaintextBuffer, encryptedMsgRetValue);
|
|
||||||
|
|
||||||
if (null != ciphertextBuffer) {
|
|
||||||
encryptedMsgRetValue.mCipherText = new String(ciphertextBuffer, "UTF-8");
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## pkEncrypt(): failed " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_PK_ENCRYPTION_ENCRYPT, e.getMessage());
|
|
||||||
} finally {
|
|
||||||
if (null != plaintextBuffer) {
|
|
||||||
Arrays.fill(plaintextBuffer, (byte) 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return encryptedMsgRetValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private native byte[] encryptJni(byte[] plaintext, OlmPkMessage aMessage);
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2018 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.olm;
|
|
||||||
|
|
||||||
public class OlmPkMessage {
|
|
||||||
public String mCipherText;
|
|
||||||
public String mMac;
|
|
||||||
public String mEphemeralKey;
|
|
||||||
}
|
|
|
@ -1,100 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2019 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.olm;
|
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
public class OlmPkSigning {
|
|
||||||
private static final String LOG_TAG = "OlmPkSigning";
|
|
||||||
|
|
||||||
/** PK Signing Id returned by JNI.
|
|
||||||
* This value uniquely identifies the native PK signing instance.
|
|
||||||
**/
|
|
||||||
private transient long mNativeId;
|
|
||||||
|
|
||||||
public OlmPkSigning() throws OlmException {
|
|
||||||
try {
|
|
||||||
mNativeId = createNewPkSigningJni();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_PK_SIGNING_CREATION, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private native long createNewPkSigningJni();
|
|
||||||
|
|
||||||
private native void releasePkSigningJni();
|
|
||||||
|
|
||||||
public void releaseSigning() {
|
|
||||||
if (0 != mNativeId) {
|
|
||||||
releasePkSigningJni();
|
|
||||||
}
|
|
||||||
mNativeId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isReleased() {
|
|
||||||
return (0 == mNativeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static native int seedLength();
|
|
||||||
|
|
||||||
public static byte[] generateSeed() throws OlmException {
|
|
||||||
try {
|
|
||||||
return generateSeedJni();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## generateSeed(): failed " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_PK_SIGNING_GENERATE_SEED, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static native byte[] generateSeedJni();
|
|
||||||
|
|
||||||
public String initWithSeed(byte[] seed) throws OlmException {
|
|
||||||
try {
|
|
||||||
byte[] pubKey = setKeyFromSeedJni(seed);
|
|
||||||
return new String(pubKey, "UTF-8");
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## initWithSeed(): failed " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_PK_SIGNING_INIT_WITH_SEED, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public native byte[] setKeyFromSeedJni(byte[] seed);
|
|
||||||
|
|
||||||
public String sign(String aMessage) throws OlmException {
|
|
||||||
if (null == aMessage) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] messageBuffer = null;
|
|
||||||
try {
|
|
||||||
messageBuffer = aMessage.getBytes("UTF-8");
|
|
||||||
byte[] signature = pkSignJni(messageBuffer);
|
|
||||||
return new String(signature, "UTF-8");
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## pkSign(): failed " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_PK_SIGNING_SIGN, e.getMessage());
|
|
||||||
} finally {
|
|
||||||
if (null != messageBuffer) {
|
|
||||||
Arrays.fill(messageBuffer, (byte) 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private native byte[] pkSignJni(byte[] message);
|
|
||||||
}
|
|
|
@ -1,167 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2019 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.matrix.olm;
|
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
|
|
||||||
public class OlmSAS {
|
|
||||||
|
|
||||||
private static final String LOG_TAG = OlmSAS.class.getName();
|
|
||||||
/**
|
|
||||||
* Session Id returned by JNI.
|
|
||||||
* This value uniquely identifies the native SAS instance.
|
|
||||||
**/
|
|
||||||
private transient long mNativeId;
|
|
||||||
|
|
||||||
private String theirPublicKey = null;
|
|
||||||
|
|
||||||
public OlmSAS() throws OlmException {
|
|
||||||
try {
|
|
||||||
mNativeId = createNewSASJni();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_SAS_CREATION, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the Public Key encoded in Base64 with no padding
|
|
||||||
* @return The public key
|
|
||||||
* @throws OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public String getPublicKey() throws OlmException {
|
|
||||||
try {
|
|
||||||
byte[] buffer = getPubKeyJni();
|
|
||||||
|
|
||||||
if (null != buffer) {
|
|
||||||
return new String(buffer, "UTF-8");
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## sessionIdentifier(): " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_SAS_ERROR, e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the public key of other user.
|
|
||||||
*
|
|
||||||
* @param otherPkey other user public key (base64 encoded with no padding)
|
|
||||||
* @throws OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public void setTheirPublicKey(String otherPkey) throws OlmException {
|
|
||||||
try {
|
|
||||||
setTheirPubKey(otherPkey.getBytes("UTF-8"));
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_SAS_ERROR, e.getMessage());
|
|
||||||
}
|
|
||||||
this.theirPublicKey = otherPkey;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate bytes to use for the short authentication string.
|
|
||||||
*
|
|
||||||
* @param info info extra information to mix in when generating the bytes, as
|
|
||||||
* per the Matrix spec.
|
|
||||||
* @param byteNumber The size of the short code to generate
|
|
||||||
* @return The generated shortcode
|
|
||||||
* @throws OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public byte[] generateShortCode(String info, int byteNumber) throws OlmException {
|
|
||||||
if (theirPublicKey == null || theirPublicKey.isEmpty()) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_SAS_MISSING_THEIR_PKEY, "call setTheirPublicKey first");
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return generateShortCodeJni(info.getBytes("UTF-8"), byteNumber);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## sessionIdentifier(): " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_SAS_GENERATE_SHORT_CODE, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public String calculateMac(String message, String info) throws OlmException {
|
|
||||||
try {
|
|
||||||
byte[] bytes = calculateMacJni(message.getBytes("UTF-8"), info.getBytes("UTF-8"));
|
|
||||||
if (bytes != null) return new String(bytes, "UTF-8");
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_SAS_ERROR, e.getMessage());
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String calculateMacFixedBase64(String message, String info) throws OlmException {
|
|
||||||
try {
|
|
||||||
byte[] bytes = calculateMacFixedBase64Jni(message.getBytes("UTF-8"), info.getBytes("UTF-8"));
|
|
||||||
if (bytes != null) return new String(bytes, "UTF-8");
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_SAS_ERROR, e.getMessage());
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String calculateMacLongKdf(String message, String info) throws OlmException {
|
|
||||||
try {
|
|
||||||
byte[] bytes = calculateMacLongKdfJni(message.getBytes("UTF-8"), info.getBytes("UTF-8"));
|
|
||||||
if (bytes != null) return new String(bytes, "UTF-8");
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_SAS_ERROR, e.getMessage());
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an OLM session in native side.<br>
|
|
||||||
* Do not forget to call {@link #releaseSASJni()} when JAVA side is done.
|
|
||||||
*
|
|
||||||
* @return native account instance identifier or throw an exception.
|
|
||||||
*/
|
|
||||||
private native long createNewSASJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the corresponding OLM session native object.<br>
|
|
||||||
* This method must ALWAYS be called when this JAVA instance
|
|
||||||
* is destroyed (ie. garbage collected) to prevent memory leak in native side.
|
|
||||||
* See {@link #createNewSASJni()}.
|
|
||||||
*/
|
|
||||||
private native void releaseSASJni();
|
|
||||||
|
|
||||||
private native byte[] getPubKeyJni();
|
|
||||||
|
|
||||||
private native void setTheirPubKey(byte[] pubKey);
|
|
||||||
|
|
||||||
private native byte[] generateShortCodeJni(byte[] info, int byteNumber);
|
|
||||||
|
|
||||||
private native byte[] calculateMacJni(byte[] message, byte[] info);
|
|
||||||
|
|
||||||
private native byte[] calculateMacFixedBase64Jni(byte[] message, byte[] info);
|
|
||||||
|
|
||||||
private native byte[] calculateMacLongKdfJni(byte[] message, byte[] info);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release native session and invalid its JAVA reference counter part.<br>
|
|
||||||
* Public API for {@link #releaseSASJni()}.
|
|
||||||
*/
|
|
||||||
public void releaseSas() {
|
|
||||||
if (0 != mNativeId) {
|
|
||||||
releaseSASJni();
|
|
||||||
}
|
|
||||||
mNativeId = 0;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,494 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.olm;
|
|
||||||
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Session class used to create Olm sessions in conjunction with {@link OlmAccount} class.<br>
|
|
||||||
* Olm session is used to encrypt data between devices, especially to create Olm group sessions (see {@link OlmOutboundGroupSession} and {@link OlmInboundGroupSession}).<br>
|
|
||||||
* To establish an Olm session with Bob, Alice calls {@link #initOutboundSession(OlmAccount, String, String)} with Bob's identity and onetime keys. Then Alice generates an encrypted PRE_KEY message ({@link #encryptMessage(String)})
|
|
||||||
* used by Bob to open the Olm session in his side with {@link #initOutboundSession(OlmAccount, String, String)}.
|
|
||||||
* From this step on, messages can be exchanged by using {@link #encryptMessage(String)} and {@link #decryptMessage(OlmMessage)}.
|
|
||||||
* <br><br>Detailed implementation guide is available at <a href="http://matrix.org/docs/guides/e2e_implementation.html">Implementing End-to-End Encryption in Matrix clients</a>.
|
|
||||||
*/
|
|
||||||
public class OlmSession extends CommonSerializeUtils implements Serializable {
|
|
||||||
private static final long serialVersionUID = -8975488639186976419L;
|
|
||||||
private static final String LOG_TAG = "OlmSession";
|
|
||||||
|
|
||||||
/** Session Id returned by JNI.
|
|
||||||
* This value uniquely identifies the native session instance.
|
|
||||||
**/
|
|
||||||
private transient long mNativeId;
|
|
||||||
|
|
||||||
public OlmSession() throws OlmException {
|
|
||||||
try {
|
|
||||||
mNativeId = createNewSessionJni();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_INIT_SESSION_CREATION, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an OLM session in native side.<br>
|
|
||||||
* Do not forget to call {@link #releaseSession()} when JAVA side is done.
|
|
||||||
* @return native account instance identifier or throw an exception.
|
|
||||||
*/
|
|
||||||
private native long createNewSessionJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Getter on the session ID.
|
|
||||||
* @return native session ID
|
|
||||||
*/
|
|
||||||
long getOlmSessionId(){
|
|
||||||
return mNativeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the corresponding OLM session native object.<br>
|
|
||||||
* This method must ALWAYS be called when this JAVA instance
|
|
||||||
* is destroyed (ie. garbage collected) to prevent memory leak in native side.
|
|
||||||
* See {@link #createNewSessionJni()}.
|
|
||||||
*/
|
|
||||||
private native void releaseSessionJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release native session and invalid its JAVA reference counter part.<br>
|
|
||||||
* Public API for {@link #releaseSessionJni()}.
|
|
||||||
*/
|
|
||||||
public void releaseSession() {
|
|
||||||
if (0 != mNativeId) {
|
|
||||||
releaseSessionJni();
|
|
||||||
}
|
|
||||||
mNativeId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true the object resources have been released.<br>
|
|
||||||
* @return true the object resources have been released
|
|
||||||
*/
|
|
||||||
public boolean isReleased() {
|
|
||||||
return (0 == mNativeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new out-bound session for sending messages to a recipient
|
|
||||||
* identified by an identity key and a one time key.<br>
|
|
||||||
* @param aAccount the account to associate with this session
|
|
||||||
* @param aTheirIdentityKey the identity key of the recipient
|
|
||||||
* @param aTheirOneTimeKey the one time key of the recipient
|
|
||||||
* @exception OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public void initOutboundSession(OlmAccount aAccount, String aTheirIdentityKey, String aTheirOneTimeKey) throws OlmException {
|
|
||||||
if ((null == aAccount) || TextUtils.isEmpty(aTheirIdentityKey) || TextUtils.isEmpty(aTheirOneTimeKey)) {
|
|
||||||
Log.e(LOG_TAG, "## initOutboundSession(): invalid input parameters");
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_INIT_OUTBOUND_SESSION, "invalid input parameters");
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
initOutboundSessionJni(aAccount.getOlmAccountId(), aTheirIdentityKey.getBytes("UTF-8"), aTheirOneTimeKey.getBytes("UTF-8"));
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## initOutboundSession(): " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_INIT_OUTBOUND_SESSION, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new in-bound session for sending/receiving messages from an
|
|
||||||
* incoming PRE_KEY message.<br> The recipient is defined as the entity
|
|
||||||
* with whom the session is established.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aOlmAccountId account instance
|
|
||||||
* @param aTheirIdentityKey the identity key of the recipient
|
|
||||||
* @param aTheirOneTimeKey the one time key of the recipient
|
|
||||||
**/
|
|
||||||
private native void initOutboundSessionJni(long aOlmAccountId, byte[] aTheirIdentityKey, byte[] aTheirOneTimeKey);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new in-bound session for sending/receiving messages from an
|
|
||||||
* incoming PRE_KEY message ({@link OlmMessage#MESSAGE_TYPE_PRE_KEY}).<br>
|
|
||||||
* This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
|
|
||||||
* @param aAccount the account to associate with this session
|
|
||||||
* @param aPreKeyMsg PRE KEY message
|
|
||||||
* @exception OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public void initInboundSession(OlmAccount aAccount, String aPreKeyMsg) throws OlmException {
|
|
||||||
if ((null == aAccount) || TextUtils.isEmpty(aPreKeyMsg)){
|
|
||||||
Log.e(LOG_TAG, "## initInboundSession(): invalid input parameters");
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_INIT_INBOUND_SESSION, "invalid input parameters");
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
initInboundSessionJni(aAccount.getOlmAccountId(), aPreKeyMsg.getBytes("UTF-8"));
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## initInboundSession(): " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_INIT_INBOUND_SESSION, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new in-bound session for sending/receiving messages from an
|
|
||||||
* incoming PRE_KEY message.<br>
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aOlmAccountId account instance
|
|
||||||
* @param aOneTimeKeyMsg PRE_KEY message
|
|
||||||
*/
|
|
||||||
private native void initInboundSessionJni(long aOlmAccountId, byte[] aOneTimeKeyMsg);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new in-bound session for sending/receiving messages from an
|
|
||||||
* incoming PRE_KEY({@link OlmMessage#MESSAGE_TYPE_PRE_KEY}) message based on the sender identity key.<br>
|
|
||||||
* Public API for {@link #initInboundSessionFromIdKeyJni(long, byte[], byte[])}.
|
|
||||||
* This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
|
|
||||||
* This method must only be called the first time a pre-key message is received from an inbound session.
|
|
||||||
* @param aAccount the account to associate with this session
|
|
||||||
* @param aTheirIdentityKey the sender identity key
|
|
||||||
* @param aPreKeyMsg PRE KEY message
|
|
||||||
* @exception OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public void initInboundSessionFrom(OlmAccount aAccount, String aTheirIdentityKey, String aPreKeyMsg) throws OlmException {
|
|
||||||
if ( (null==aAccount) || TextUtils.isEmpty(aPreKeyMsg)){
|
|
||||||
Log.e(LOG_TAG, "## initInboundSessionFrom(): invalid input parameters");
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_INIT_INBOUND_SESSION_FROM, "invalid input parameters");
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
initInboundSessionFromIdKeyJni(aAccount.getOlmAccountId(), aTheirIdentityKey.getBytes("UTF-8"), aPreKeyMsg.getBytes("UTF-8"));
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## initInboundSessionFrom(): " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_INIT_INBOUND_SESSION_FROM, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new in-bound session for sending/receiving messages from an
|
|
||||||
* incoming PRE_KEY message based on the recipient identity key.<br>
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aOlmAccountId account instance
|
|
||||||
* @param aTheirIdentityKey the identity key of the recipient
|
|
||||||
* @param aOneTimeKeyMsg encrypted message
|
|
||||||
*/
|
|
||||||
private native void initInboundSessionFromIdKeyJni(long aOlmAccountId, byte[] aTheirIdentityKey, byte[] aOneTimeKeyMsg);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the session identifier.<br> Will be the same for both ends of the
|
|
||||||
* conversation. The session identifier is returned as a String object.
|
|
||||||
* Session Id sample: "session_id":"M4fOVwD6AABrkTKl"
|
|
||||||
* Public API for {@link #getSessionIdentifierJni()}.
|
|
||||||
* @return the session ID
|
|
||||||
* @exception OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public String sessionIdentifier() throws OlmException {
|
|
||||||
try {
|
|
||||||
byte[] buffer = getSessionIdentifierJni();
|
|
||||||
|
|
||||||
if (null != buffer) {
|
|
||||||
return new String(buffer, "UTF-8");
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## sessionIdentifier(): " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_SESSION_IDENTIFIER, e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the session identifier for this session.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @return the session identifier
|
|
||||||
*/
|
|
||||||
private native byte[] getSessionIdentifierJni();
|
|
||||||
|
|
||||||
public String sessionDescribe() throws OlmException {
|
|
||||||
try {
|
|
||||||
byte[] buffer = olmSessionDescribeJni();
|
|
||||||
|
|
||||||
if (null != buffer) {
|
|
||||||
return new String(buffer, "UTF-8");
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## sessionDescribe(): " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_SESSION_DESCRIBE, e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private native byte[] olmSessionDescribeJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the PRE_KEY({@link OlmMessage#MESSAGE_TYPE_PRE_KEY}) message is for this in-bound session.<br>
|
|
||||||
* This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
|
|
||||||
* Public API for {@link #matchesInboundSessionJni(byte[])}.
|
|
||||||
* @param aOneTimeKeyMsg PRE KEY message
|
|
||||||
* @return true if the one time key matches.
|
|
||||||
*/
|
|
||||||
public boolean matchesInboundSession(String aOneTimeKeyMsg) {
|
|
||||||
boolean retCode = false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
retCode = matchesInboundSessionJni(aOneTimeKeyMsg.getBytes("UTF-8"));
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## matchesInboundSession(): failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return retCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the PRE_KEY message is for this in-bound session.<br>
|
|
||||||
* This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aOneTimeKeyMsg PRE KEY message
|
|
||||||
* @return true if the PRE_KEY message matches
|
|
||||||
*/
|
|
||||||
private native boolean matchesInboundSessionJni(byte[] aOneTimeKeyMsg);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the PRE_KEY({@link OlmMessage#MESSAGE_TYPE_PRE_KEY}) message is for this in-bound session based on the sender identity key.<br>
|
|
||||||
* This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
|
|
||||||
* Public API for {@link #matchesInboundSessionJni(byte[])}.
|
|
||||||
* @param aTheirIdentityKey the sender identity key
|
|
||||||
* @param aOneTimeKeyMsg PRE KEY message
|
|
||||||
* @return this if operation succeed, null otherwise
|
|
||||||
*/
|
|
||||||
public boolean matchesInboundSessionFrom(String aTheirIdentityKey, String aOneTimeKeyMsg) {
|
|
||||||
boolean retCode = false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
retCode = matchesInboundSessionFromIdKeyJni(aTheirIdentityKey.getBytes("UTF-8"), aOneTimeKeyMsg.getBytes("UTF-8"));
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## matchesInboundSessionFrom(): failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return retCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the PRE_KEY message is for this in-bound session based on the sender identity key.<br>
|
|
||||||
* This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY).
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aTheirIdentityKey the identity key of the sender
|
|
||||||
* @param aOneTimeKeyMsg PRE KEY message
|
|
||||||
* @return true if the PRE_KEY message matches.
|
|
||||||
*/
|
|
||||||
private native boolean matchesInboundSessionFromIdKeyJni(byte[] aTheirIdentityKey, byte[] aOneTimeKeyMsg);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt a message using the session.<br>
|
|
||||||
* The encrypted message is returned in a OlmMessage object.
|
|
||||||
* Public API for {@link #encryptMessageJni(byte[], OlmMessage)}.
|
|
||||||
* @param aClearMsg message to encrypted
|
|
||||||
* @return the encrypted message
|
|
||||||
* @exception OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public OlmMessage encryptMessage(String aClearMsg) throws OlmException {
|
|
||||||
if (null == aClearMsg) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
OlmMessage encryptedMsgRetValue = new OlmMessage();
|
|
||||||
|
|
||||||
try {
|
|
||||||
byte[] clearMsgBuffer = aClearMsg.getBytes("UTF-8");
|
|
||||||
byte[] encryptedMessageBuffer = encryptMessageJni(clearMsgBuffer, encryptedMsgRetValue);
|
|
||||||
Arrays.fill(clearMsgBuffer, (byte) 0);
|
|
||||||
|
|
||||||
if (null != encryptedMessageBuffer) {
|
|
||||||
encryptedMsgRetValue.mCipherText = new String(encryptedMessageBuffer, "UTF-8");
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## encryptMessage(): failed " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_ENCRYPT_MESSAGE, e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return encryptedMsgRetValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt a message using the session.<br>
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aClearMsg clear text message
|
|
||||||
* @param aEncryptedMsg ciphered message
|
|
||||||
* @return the encrypted message
|
|
||||||
*/
|
|
||||||
private native byte[] encryptMessageJni(byte[] aClearMsg, OlmMessage aEncryptedMsg);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypt a message using the session.<br>
|
|
||||||
* The encrypted message is given as a OlmMessage object.
|
|
||||||
* @param aEncryptedMsg message to decrypt
|
|
||||||
* @return the decrypted message
|
|
||||||
* @exception OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public String decryptMessage(OlmMessage aEncryptedMsg) throws OlmException {
|
|
||||||
if (null == aEncryptedMsg) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
byte[] plaintextBuffer = decryptMessageJni(aEncryptedMsg);
|
|
||||||
String plaintext = new String(plaintextBuffer, "UTF-8");
|
|
||||||
Arrays.fill(plaintextBuffer, (byte) 0);
|
|
||||||
return plaintext;
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## decryptMessage(): failed " + e.getMessage());
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_SESSION_DECRYPT_MESSAGE, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Decrypt a message using the session.<br>
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aEncryptedMsg message to decrypt
|
|
||||||
* @return the decrypted message
|
|
||||||
*/
|
|
||||||
private native byte[] decryptMessageJni(OlmMessage aEncryptedMsg);
|
|
||||||
|
|
||||||
//==============================================================================================================
|
|
||||||
// Serialization management
|
|
||||||
//==============================================================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kick off the serialization mechanism.
|
|
||||||
* @param aOutStream output stream for serializing
|
|
||||||
* @throws IOException exception
|
|
||||||
*/
|
|
||||||
private void writeObject(ObjectOutputStream aOutStream) throws IOException {
|
|
||||||
serialize(aOutStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kick off the deserialization mechanism.
|
|
||||||
* @param aInStream input stream
|
|
||||||
* @throws IOException exception
|
|
||||||
* @throws ClassNotFoundException exception
|
|
||||||
*/
|
|
||||||
private void readObject(ObjectInputStream aInStream) throws Exception {
|
|
||||||
deserialize(aInStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a session as a bytes buffer.<br>
|
|
||||||
* The account is serialized and encrypted with aKey.
|
|
||||||
* In case of failure, an error human readable
|
|
||||||
* description is provide in aErrorMsg.
|
|
||||||
* @param aKey encryption key
|
|
||||||
* @param aErrorMsg error message description
|
|
||||||
* @return session as a bytes buffer
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected byte[] serialize(byte[] aKey, StringBuffer aErrorMsg) {
|
|
||||||
byte[] pickleRetValue = null;
|
|
||||||
|
|
||||||
// sanity check
|
|
||||||
if(null == aErrorMsg) {
|
|
||||||
Log.e(LOG_TAG,"## serializeDataWithKey(): invalid parameter - aErrorMsg=null");
|
|
||||||
} else if (null == aKey) {
|
|
||||||
aErrorMsg.append("Invalid input parameters in serializeDataWithKey()");
|
|
||||||
} else {
|
|
||||||
aErrorMsg.setLength(0);
|
|
||||||
try {
|
|
||||||
pickleRetValue = serializeJni(aKey);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG,"## serializeDataWithKey(): failed " + e.getMessage());
|
|
||||||
aErrorMsg.append(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pickleRetValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize and encrypt session instance.<br>
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aKeyBuffer key used to encrypt the serialized account data
|
|
||||||
* @return the serialised account as bytes buffer.
|
|
||||||
**/
|
|
||||||
private native byte[] serializeJni(byte[] aKeyBuffer);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads an account from a pickled base64 string.<br>
|
|
||||||
* See {@link #serialize(byte[], StringBuffer)}
|
|
||||||
* @param aSerializedData pickled account in a base64 string format
|
|
||||||
* @param aKey key used to encrypted
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void deserialize(byte[] aSerializedData, byte[] aKey) throws Exception {
|
|
||||||
String errorMsg = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if ((null == aSerializedData) || (null == aKey)) {
|
|
||||||
Log.e(LOG_TAG, "## deserialize(): invalid input parameters");
|
|
||||||
errorMsg = "invalid input parameters";
|
|
||||||
} else {
|
|
||||||
mNativeId = deserializeJni(aSerializedData, aKey);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## deserialize() failed " + e.getMessage());
|
|
||||||
errorMsg = e.getMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(errorMsg)) {
|
|
||||||
releaseSession();
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_ACCOUNT_DESERIALIZATION, errorMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Allocate a new session and initialize it with the serialisation data.<br>
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aSerializedData the session serialisation buffer
|
|
||||||
* @param aKey the key used to encrypt the serialized account data
|
|
||||||
* @return the deserialized session
|
|
||||||
**/
|
|
||||||
private native long deserializeJni(byte[] aSerializedData, byte[] aKey);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a pickled session as a bytes buffer.<br>
|
|
||||||
* The session is serialized and encrypted with aKey.
|
|
||||||
* In case of failure, an error human readable
|
|
||||||
* description is provide in aErrorMsg.
|
|
||||||
* @param aKey encryption key
|
|
||||||
* @param aErrorMsg error message description
|
|
||||||
* @return the pickled session as bytes buffer
|
|
||||||
*/
|
|
||||||
public byte[] pickle(byte[] aKey, StringBuffer aErrorMsg) {
|
|
||||||
return serialize(aKey, aErrorMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a session from a pickled bytes buffer.<br>
|
|
||||||
* See {@link #serialize(byte[], StringBuffer)}
|
|
||||||
* @param aSerializedData bytes buffer
|
|
||||||
* @param aKey key used to encrypted
|
|
||||||
* @exception Exception the exception
|
|
||||||
*/
|
|
||||||
public void unpickle(byte[] aSerializedData, byte[] aKey) throws Exception {
|
|
||||||
deserialize(aSerializedData, aKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,240 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2017 OpenMarket Ltd
|
|
||||||
* Copyright 2017 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.olm;
|
|
||||||
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Olm SDK helper class.
|
|
||||||
*/
|
|
||||||
public class OlmUtility {
|
|
||||||
private static final String LOG_TAG = "OlmUtility";
|
|
||||||
|
|
||||||
public static final int RANDOM_KEY_SIZE = 32;
|
|
||||||
|
|
||||||
/** Instance Id returned by JNI.
|
|
||||||
* This value uniquely identifies this utility instance.
|
|
||||||
**/
|
|
||||||
private long mNativeId;
|
|
||||||
|
|
||||||
public OlmUtility() throws OlmException {
|
|
||||||
initUtility();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a native utility instance.
|
|
||||||
* To be called before any other API call.
|
|
||||||
* @exception OlmException the exception
|
|
||||||
*/
|
|
||||||
private void initUtility() throws OlmException {
|
|
||||||
try {
|
|
||||||
mNativeId = createUtilityJni();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_UTILITY_CREATION, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private native long createUtilityJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release native instance.<br>
|
|
||||||
* Public API for {@link #releaseUtilityJni()}.
|
|
||||||
*/
|
|
||||||
public void releaseUtility() {
|
|
||||||
if (0 != mNativeId) {
|
|
||||||
releaseUtilityJni();
|
|
||||||
}
|
|
||||||
mNativeId = 0;
|
|
||||||
}
|
|
||||||
private native void releaseUtilityJni();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify an ed25519 signature.<br>
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aSignature the base64-encoded message signature to be checked.
|
|
||||||
* @param aFingerprintKey the ed25519 key (fingerprint key)
|
|
||||||
* @param aMessage the signed message
|
|
||||||
* @exception OlmException the failure reason
|
|
||||||
*/
|
|
||||||
public void verifyEd25519Signature(String aSignature, String aFingerprintKey, String aMessage) throws OlmException {
|
|
||||||
String errorMessage;
|
|
||||||
byte[] messageBuffer = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (TextUtils.isEmpty(aSignature) || TextUtils.isEmpty(aFingerprintKey) || TextUtils.isEmpty(aMessage)) {
|
|
||||||
Log.e(LOG_TAG, "## verifyEd25519Signature(): invalid input parameters");
|
|
||||||
errorMessage = "JAVA sanity check failure - invalid input parameters";
|
|
||||||
} else {
|
|
||||||
messageBuffer = aMessage.getBytes("UTF-8");
|
|
||||||
errorMessage = verifyEd25519SignatureJni(aSignature.getBytes("UTF-8"), aFingerprintKey.getBytes("UTF-8"), messageBuffer);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## verifyEd25519Signature(): failed " + e.getMessage());
|
|
||||||
errorMessage = e.getMessage();
|
|
||||||
} finally {
|
|
||||||
if (messageBuffer != null) {
|
|
||||||
Arrays.fill(messageBuffer, (byte) 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(errorMessage)) {
|
|
||||||
throw new OlmException(OlmException.EXCEPTION_CODE_UTILITY_VERIFY_SIGNATURE, errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify an ed25519 signature.
|
|
||||||
* Return a human readable error message in case of verification failure.
|
|
||||||
* @param aSignature the base64-encoded message signature to be checked.
|
|
||||||
* @param aFingerprintKey the ed25519 key
|
|
||||||
* @param aMessage the signed message
|
|
||||||
* @return null if validation succeed, the error message string if operation failed
|
|
||||||
*/
|
|
||||||
private native String verifyEd25519SignatureJni(byte[] aSignature, byte[] aFingerprintKey, byte[] aMessage);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute the hash(SHA-256) value of the string given in parameter(aMessageToHash).<br>
|
|
||||||
* The hash value is the returned by the method.
|
|
||||||
* @param aMessageToHash message to be hashed
|
|
||||||
* @return hash value if operation succeed, null otherwise
|
|
||||||
*/
|
|
||||||
public String sha256(String aMessageToHash) {
|
|
||||||
String hashRetValue = null;
|
|
||||||
|
|
||||||
if (null != aMessageToHash) {
|
|
||||||
byte[] messageBuffer = null;
|
|
||||||
try {
|
|
||||||
messageBuffer = aMessageToHash.getBytes("UTF-8");
|
|
||||||
hashRetValue = new String(sha256Jni(messageBuffer), "UTF-8");
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## sha256(): failed " + e.getMessage());
|
|
||||||
} finally {
|
|
||||||
if (null != messageBuffer) {
|
|
||||||
Arrays.fill(messageBuffer, (byte) 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hashRetValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute the digest (SHA 256) for the message passed in parameter.<br>
|
|
||||||
* The digest value is the function return value.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aMessage the message
|
|
||||||
* @return digest of the message.
|
|
||||||
**/
|
|
||||||
private native byte[] sha256Jni(byte[] aMessage);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper method to compute a string based on random integers.
|
|
||||||
* @return bytes buffer containing randoms integer values
|
|
||||||
*/
|
|
||||||
public static byte[] getRandomKey() {
|
|
||||||
SecureRandom secureRandom = new SecureRandom();
|
|
||||||
byte[] buffer = new byte[RANDOM_KEY_SIZE];
|
|
||||||
secureRandom.nextBytes(buffer);
|
|
||||||
|
|
||||||
// the key is saved as string
|
|
||||||
// so avoid the UTF8 marker bytes
|
|
||||||
for(int i = 0; i < RANDOM_KEY_SIZE; i++) {
|
|
||||||
buffer[i] = (byte)(buffer[i] & 0x7F);
|
|
||||||
}
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true the object resources have been released.<br>
|
|
||||||
* @return true the object resources have been released
|
|
||||||
*/
|
|
||||||
public boolean isReleased() {
|
|
||||||
return (0 == mNativeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a string-string dictionary from a jsonObject.<br>
|
|
||||||
* @param jsonObject the object to parse
|
|
||||||
* @return the map
|
|
||||||
*/
|
|
||||||
public static Map<String, String> toStringMap(JSONObject jsonObject) {
|
|
||||||
if (null != jsonObject) {
|
|
||||||
HashMap<String, String> map = new HashMap<>();
|
|
||||||
Iterator<String> keysItr = jsonObject.keys();
|
|
||||||
while(keysItr.hasNext()) {
|
|
||||||
String key = keysItr.next();
|
|
||||||
try {
|
|
||||||
Object value = jsonObject.get(key);
|
|
||||||
|
|
||||||
if (value instanceof String) {
|
|
||||||
map.put(key, (String) value);
|
|
||||||
} else {
|
|
||||||
Log.e(LOG_TAG, "## toStringMap(): unexpected type " + value.getClass());
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## toStringMap(): failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a string-string dictionary of string dictionary from a jsonObject.<br>
|
|
||||||
* @param jsonObject the object to parse
|
|
||||||
* @return the map
|
|
||||||
*/
|
|
||||||
public static Map<String, Map<String, String>> toStringMapMap(JSONObject jsonObject) {
|
|
||||||
if (null != jsonObject) {
|
|
||||||
HashMap<String, Map<String, String>> map = new HashMap<>();
|
|
||||||
|
|
||||||
Iterator<String> keysItr = jsonObject.keys();
|
|
||||||
while(keysItr.hasNext()) {
|
|
||||||
String key = keysItr.next();
|
|
||||||
try {
|
|
||||||
Object value = jsonObject.get(key);
|
|
||||||
|
|
||||||
if (value instanceof JSONObject) {
|
|
||||||
map.put(key, toStringMap((JSONObject) value));
|
|
||||||
} else {
|
|
||||||
Log.e(LOG_TAG, "## toStringMapMap(): unexpected type " + value.getClass());
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOG_TAG, "## toStringMapMap(): failed " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
LOCAL_PATH := $(call my-dir)
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
|
|
||||||
|
|
||||||
LOCAL_MODULE := olm
|
|
||||||
|
|
||||||
SRC_ROOT_DIR := ../../../../..
|
|
||||||
|
|
||||||
include $(LOCAL_PATH)/$(SRC_ROOT_DIR)/common.mk
|
|
||||||
OLM_VERSION := $(MAJOR).$(MINOR).$(PATCH)
|
|
||||||
|
|
||||||
$(info LOCAL_PATH=$(LOCAL_PATH))
|
|
||||||
$(info SRC_ROOT_DIR=$(SRC_ROOT_DIR))
|
|
||||||
$(info OLM_VERSION=$(OLM_VERSION))
|
|
||||||
|
|
||||||
LOCAL_CPPFLAGS+= -std=c++11 -Wall
|
|
||||||
LOCAL_CONLYFLAGS+= -std=c99
|
|
||||||
LOCAL_CFLAGS+= -DOLMLIB_VERSION_MAJOR=$(MAJOR) \
|
|
||||||
-DOLMLIB_VERSION_MINOR=$(MINOR) \
|
|
||||||
-DOLMLIB_VERSION_PATCH=$(PATCH)
|
|
||||||
|
|
||||||
#LOCAL_CFLAGS+= -DNDK_DEBUG
|
|
||||||
|
|
||||||
LOCAL_CFLAGS+=-fstack-protector-all -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Wall
|
|
||||||
LOCAL_LDFLAGS=-z relro -z now
|
|
||||||
|
|
||||||
LOCAL_C_INCLUDES+= $(LOCAL_PATH)/$(SRC_ROOT_DIR)/include/ \
|
|
||||||
$(LOCAL_PATH)/$(SRC_ROOT_DIR)/lib
|
|
||||||
|
|
||||||
$(info LOCAL_C_INCLUDES=$(LOCAL_C_INCLUDES))
|
|
||||||
|
|
||||||
LOCAL_SRC_FILES := $(SRC_ROOT_DIR)/src/account.cpp \
|
|
||||||
$(SRC_ROOT_DIR)/src/base64.cpp \
|
|
||||||
$(SRC_ROOT_DIR)/src/cipher.cpp \
|
|
||||||
$(SRC_ROOT_DIR)/src/crypto.cpp \
|
|
||||||
$(SRC_ROOT_DIR)/src/memory.cpp \
|
|
||||||
$(SRC_ROOT_DIR)/src/message.cpp \
|
|
||||||
$(SRC_ROOT_DIR)/src/olm.cpp \
|
|
||||||
$(SRC_ROOT_DIR)/src/pickle.cpp \
|
|
||||||
$(SRC_ROOT_DIR)/src/ratchet.cpp \
|
|
||||||
$(SRC_ROOT_DIR)/src/session.cpp \
|
|
||||||
$(SRC_ROOT_DIR)/src/utility.cpp \
|
|
||||||
$(SRC_ROOT_DIR)/src/pk.cpp \
|
|
||||||
$(SRC_ROOT_DIR)/src/sas.c \
|
|
||||||
$(SRC_ROOT_DIR)/src/ed25519.c \
|
|
||||||
$(SRC_ROOT_DIR)/src/error.c \
|
|
||||||
$(SRC_ROOT_DIR)/src/inbound_group_session.c \
|
|
||||||
$(SRC_ROOT_DIR)/src/megolm.c \
|
|
||||||
$(SRC_ROOT_DIR)/src/outbound_group_session.c \
|
|
||||||
$(SRC_ROOT_DIR)/src/pickle_encoding.c \
|
|
||||||
$(SRC_ROOT_DIR)/lib/crypto-algorithms/sha256.c \
|
|
||||||
$(SRC_ROOT_DIR)/lib/crypto-algorithms/aes.c \
|
|
||||||
$(SRC_ROOT_DIR)/lib/curve25519-donna/curve25519-donna.c \
|
|
||||||
olm_account.cpp \
|
|
||||||
olm_session.cpp \
|
|
||||||
olm_jni_helper.cpp \
|
|
||||||
olm_inbound_group_session.cpp \
|
|
||||||
olm_outbound_group_session.cpp \
|
|
||||||
olm_utility.cpp \
|
|
||||||
olm_manager.cpp \
|
|
||||||
olm_pk.cpp \
|
|
||||||
olm_sas.cpp
|
|
||||||
|
|
||||||
LOCAL_LDLIBS := -llog
|
|
||||||
|
|
||||||
include $(BUILD_SHARED_LIBRARY)
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
APP_PLATFORM := android-16
|
|
||||||
APP_ABI := arm64-v8a armeabi-v7a x86_64 x86
|
|
||||||
APP_STL := c++_static
|
|
|
@ -1,850 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "olm_account.h"
|
|
||||||
|
|
||||||
using namespace AndroidOlmSdk;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Init memory allocation for account creation.
|
|
||||||
* @return valid memory allocation, NULL otherwise
|
|
||||||
**/
|
|
||||||
OlmAccount* initializeAccountMemory()
|
|
||||||
{
|
|
||||||
size_t accountSize = olm_account_size();
|
|
||||||
OlmAccount* accountPtr = (OlmAccount*)malloc(accountSize);
|
|
||||||
|
|
||||||
if (accountPtr)
|
|
||||||
{
|
|
||||||
// init account object
|
|
||||||
accountPtr = olm_account(accountPtr);
|
|
||||||
LOGD("## initializeAccountMemory(): success - OLM account size=%lu",static_cast<long unsigned int>(accountSize));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGE("## initializeAccountMemory(): failure - OOM");
|
|
||||||
}
|
|
||||||
|
|
||||||
return accountPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new account and return it to JAVA side.<br>
|
|
||||||
* Since a C prt is returned as a jlong, special care will be taken
|
|
||||||
* to make the cast (OlmAccount* => jlong) platform independent.
|
|
||||||
* @return the initialized OlmAccount* instance or throw an exception if fails
|
|
||||||
**/
|
|
||||||
JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(createNewAccountJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
OlmAccount *accountPtr = initializeAccountMemory();
|
|
||||||
|
|
||||||
// init account memory allocation
|
|
||||||
if (!accountPtr)
|
|
||||||
{
|
|
||||||
LOGE("## initNewAccount(): failure - init account OOM");
|
|
||||||
errorMessage = "init account OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// get random buffer size
|
|
||||||
size_t randomSize = olm_create_account_random_length(accountPtr);
|
|
||||||
|
|
||||||
LOGD("## initNewAccount(): randomSize=%lu", static_cast<long unsigned int>(randomSize));
|
|
||||||
|
|
||||||
uint8_t *randomBuffPtr = NULL;
|
|
||||||
size_t accountRetCode;
|
|
||||||
|
|
||||||
// allocate random buffer
|
|
||||||
if ((0 != randomSize) && !setRandomInBuffer(env, &randomBuffPtr, randomSize))
|
|
||||||
{
|
|
||||||
LOGE("## initNewAccount(): failure - random buffer init");
|
|
||||||
errorMessage = "random buffer init";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// create account
|
|
||||||
accountRetCode = olm_create_account(accountPtr, (void*)randomBuffPtr, randomSize);
|
|
||||||
|
|
||||||
if (accountRetCode == olm_error())
|
|
||||||
{
|
|
||||||
LOGE("## initNewAccount(): failure - account creation failed Msg=%s", olm_account_last_error(accountPtr));
|
|
||||||
errorMessage = olm_account_last_error(accountPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGD("## initNewAccount(): success - OLM account created");
|
|
||||||
LOGD("## initNewAccount(): success - accountPtr=%p (jlong)(intptr_t)accountPtr=%lld",accountPtr,(jlong)(intptr_t)accountPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (randomBuffPtr)
|
|
||||||
{
|
|
||||||
memset(randomBuffPtr, 0, randomSize);
|
|
||||||
free(randomBuffPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
// release the allocated data
|
|
||||||
if (accountPtr)
|
|
||||||
{
|
|
||||||
olm_clear_account(accountPtr);
|
|
||||||
free(accountPtr);
|
|
||||||
}
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (jlong)(intptr_t)accountPtr;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Release the account allocation made by initializeAccountMemory().<br>
|
|
||||||
* This method MUST be called when java counter part account instance is done.
|
|
||||||
*/
|
|
||||||
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(releaseAccountJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
LOGD("## releaseAccountJni(): IN");
|
|
||||||
|
|
||||||
OlmAccount* accountPtr = getAccountInstanceId(env, thiz);
|
|
||||||
|
|
||||||
if (!accountPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## releaseAccountJni(): failure - invalid Account ptr=NULL");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## releaseAccountJni(): accountPtr=%p",accountPtr);
|
|
||||||
olm_clear_account(accountPtr);
|
|
||||||
|
|
||||||
LOGD(" ## releaseAccountJni(): IN");
|
|
||||||
// even if free(NULL) does not crash, logs are performed for debug purpose
|
|
||||||
free(accountPtr);
|
|
||||||
LOGD(" ## releaseAccountJni(): OUT");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// *********************************************************************
|
|
||||||
// ************************* IDENTITY KEYS API *************************
|
|
||||||
// *********************************************************************
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get identity keys: Ed25519 fingerprint key and Curve25519 identity key.<br>
|
|
||||||
* The keys are returned in the byte array.
|
|
||||||
* @return the identity keys or throw an exception if it fails
|
|
||||||
**/
|
|
||||||
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(identityKeysJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
jbyteArray byteArrayRetValue = NULL;
|
|
||||||
OlmAccount* accountPtr = getAccountInstanceId(env, thiz);
|
|
||||||
|
|
||||||
if (!accountPtr)
|
|
||||||
{
|
|
||||||
LOGE("## identityKeys(): failure - invalid Account ptr=NULL");
|
|
||||||
errorMessage = "invalid Account ptr";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD("## identityKeys(): accountPtr =%p", accountPtr);
|
|
||||||
|
|
||||||
// identity keys allocation
|
|
||||||
size_t identityKeysLength = olm_account_identity_keys_length(accountPtr);
|
|
||||||
uint8_t *identityKeysBytesPtr = (uint8_t*)malloc(identityKeysLength);
|
|
||||||
|
|
||||||
if (!identityKeysBytesPtr)
|
|
||||||
{
|
|
||||||
LOGE("## identityKeys(): failure - identity keys array OOM");
|
|
||||||
errorMessage = "identity keys array OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// retrieve key pairs in identityKeysBytesPtr
|
|
||||||
size_t keysResult = olm_account_identity_keys(accountPtr, identityKeysBytesPtr, identityKeysLength);
|
|
||||||
|
|
||||||
if (keysResult == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = (const char *)olm_account_last_error(accountPtr);
|
|
||||||
LOGE("## identityKeys(): failure - error getting identity keys Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// allocate the byte array to be returned to java
|
|
||||||
byteArrayRetValue = env->NewByteArray(identityKeysLength);
|
|
||||||
|
|
||||||
if (!byteArrayRetValue)
|
|
||||||
{
|
|
||||||
LOGE("## identityKeys(): failure - return byte array OOM");
|
|
||||||
errorMessage = "byte array OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
env->SetByteArrayRegion(byteArrayRetValue, 0/*offset*/, identityKeysLength, (const jbyte*)identityKeysBytesPtr);
|
|
||||||
LOGD("## identityKeys(): success - result=%lu", static_cast<long unsigned int>(keysResult));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(identityKeysBytesPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return byteArrayRetValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// *********************************************************************
|
|
||||||
// ************************* ONE TIME KEYS API *************************
|
|
||||||
// *********************************************************************
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the public parts of the unpublished "one time keys" for the account.<br>
|
|
||||||
* The returned data is a JSON-formatted object with the single property
|
|
||||||
* <tt>curve25519</tt>, which is itself an object mapping key id to
|
|
||||||
* base64-encoded Curve25519 key.<br>
|
|
||||||
* @return byte array containing the one time keys or throw an exception if it fails
|
|
||||||
*/
|
|
||||||
JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(maxOneTimeKeysJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
OlmAccount* accountPtr = getAccountInstanceId(env, thiz);
|
|
||||||
size_t maxKeys = -1;
|
|
||||||
|
|
||||||
if (!accountPtr)
|
|
||||||
{
|
|
||||||
LOGE("## maxOneTimeKey(): failure - invalid Account ptr=NULL");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
maxKeys = olm_account_max_number_of_one_time_keys(accountPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGD("## maxOneTimeKey(): Max keys=%lu", static_cast<long unsigned int>(maxKeys));
|
|
||||||
|
|
||||||
return (jlong)maxKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate "one time keys".
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aNumberOfKeys number of keys to generate
|
|
||||||
**/
|
|
||||||
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(generateOneTimeKeysJni)(JNIEnv *env, jobject thiz, jint aNumberOfKeys)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
OlmAccount *accountPtr = getAccountInstanceId(env, thiz);
|
|
||||||
|
|
||||||
if (!accountPtr)
|
|
||||||
{
|
|
||||||
LOGE("## generateOneTimeKeysJni(): failure - invalid Account ptr");
|
|
||||||
errorMessage = "invalid Account ptr";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// keys memory allocation
|
|
||||||
size_t randomLength = olm_account_generate_one_time_keys_random_length(accountPtr, (size_t)aNumberOfKeys);
|
|
||||||
LOGD("## generateOneTimeKeysJni(): randomLength=%lu", static_cast<long unsigned int>(randomLength));
|
|
||||||
|
|
||||||
uint8_t *randomBufferPtr = NULL;
|
|
||||||
|
|
||||||
if ((0 != randomLength) && !setRandomInBuffer(env, &randomBufferPtr, randomLength))
|
|
||||||
{
|
|
||||||
LOGE("## generateOneTimeKeysJni(): failure - random buffer init");
|
|
||||||
errorMessage = "random buffer init";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD("## generateOneTimeKeysJni(): accountPtr =%p aNumberOfKeys=%d",accountPtr, aNumberOfKeys);
|
|
||||||
|
|
||||||
// retrieve key pairs in keysBytesPtr
|
|
||||||
size_t result = olm_account_generate_one_time_keys(accountPtr, (size_t)aNumberOfKeys, (void*)randomBufferPtr, randomLength);
|
|
||||||
|
|
||||||
if (result == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_account_last_error(accountPtr);
|
|
||||||
LOGE("## generateOneTimeKeysJni(): failure - error generating one time keys Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD("## generateOneTimeKeysJni(): success - result=%lu", static_cast<long unsigned int>(result));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (randomBufferPtr)
|
|
||||||
{
|
|
||||||
memset(randomBufferPtr, 0, randomLength);
|
|
||||||
free(randomBufferPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get "one time keys".<br>
|
|
||||||
* Return the public parts of the unpublished "one time keys" for the account
|
|
||||||
* @return a valid byte array if operation succeed, null otherwise
|
|
||||||
**/
|
|
||||||
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(oneTimeKeysJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
jbyteArray byteArrayRetValue = NULL;
|
|
||||||
OlmAccount* accountPtr = getAccountInstanceId(env, thiz);
|
|
||||||
|
|
||||||
LOGD("## oneTimeKeysJni(): IN");
|
|
||||||
|
|
||||||
if (!accountPtr)
|
|
||||||
{
|
|
||||||
LOGE("## oneTimeKeysJni(): failure - invalid Account ptr");
|
|
||||||
errorMessage = "invalid Account ptr";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// keys memory allocation
|
|
||||||
size_t keysLength = olm_account_one_time_keys_length(accountPtr);
|
|
||||||
uint8_t *keysBytesPtr = (uint8_t *)malloc(keysLength*sizeof(uint8_t));
|
|
||||||
|
|
||||||
if (!keysBytesPtr)
|
|
||||||
{
|
|
||||||
LOGE("## oneTimeKeysJni(): failure - one time keys array OOM");
|
|
||||||
errorMessage = "one time keys array OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// retrieve key pairs in keysBytesPtr
|
|
||||||
size_t keysResult = olm_account_one_time_keys(accountPtr, keysBytesPtr, keysLength);
|
|
||||||
|
|
||||||
if (keysResult == olm_error()) {
|
|
||||||
LOGE("## oneTimeKeysJni(): failure - error getting one time keys Msg=%s",(const char *)olm_account_last_error(accountPtr));
|
|
||||||
errorMessage = (const char *)olm_account_last_error(accountPtr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// allocate the byte array to be returned to java
|
|
||||||
byteArrayRetValue = env->NewByteArray(keysLength);
|
|
||||||
|
|
||||||
if (!byteArrayRetValue)
|
|
||||||
{
|
|
||||||
LOGE("## oneTimeKeysJni(): failure - return byte array OOM");
|
|
||||||
errorMessage = "return byte array OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
env->SetByteArrayRegion(byteArrayRetValue, 0/*offset*/, keysLength, (const jbyte*)keysBytesPtr);
|
|
||||||
LOGD("## oneTimeKeysJni(): success");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(keysBytesPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return byteArrayRetValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the "one time keys" that the session used from the account.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aNativeOlmSessionId session instance
|
|
||||||
**/
|
|
||||||
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(removeOneTimeKeysJni)(JNIEnv *env, jobject thiz, jlong aNativeOlmSessionId)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
OlmAccount* accountPtr = NULL;
|
|
||||||
OlmSession* sessionPtr = (OlmSession*)aNativeOlmSessionId;
|
|
||||||
|
|
||||||
if (!sessionPtr)
|
|
||||||
{
|
|
||||||
LOGE("## removeOneTimeKeysJni(): failure - invalid session ptr");
|
|
||||||
errorMessage = "invalid session ptr";
|
|
||||||
}
|
|
||||||
else if (!(accountPtr = getAccountInstanceId(env, thiz)))
|
|
||||||
{
|
|
||||||
LOGE("## removeOneTimeKeysJni(): failure - invalid account ptr");
|
|
||||||
errorMessage = "invalid account ptr";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t result = olm_remove_one_time_keys(accountPtr, sessionPtr);
|
|
||||||
|
|
||||||
if (result == olm_error())
|
|
||||||
{ // the account doesn't have any matching "one time keys"..
|
|
||||||
LOGW("## removeOneTimeKeysJni(): failure - removing one time keys Msg=%s", olm_account_last_error(accountPtr));
|
|
||||||
errorMessage = (const char *)olm_account_last_error(accountPtr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD("## removeOneTimeKeysJni(): success");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark the current set of "one time keys" as being published.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
**/
|
|
||||||
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(markOneTimeKeysAsPublishedJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
OlmAccount* accountPtr = getAccountInstanceId(env, thiz);
|
|
||||||
|
|
||||||
if (!accountPtr)
|
|
||||||
{
|
|
||||||
LOGE("## markOneTimeKeysAsPublishedJni(): failure - invalid account ptr");
|
|
||||||
errorMessage = "invalid account ptr";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t result = olm_account_mark_keys_as_published(accountPtr);
|
|
||||||
|
|
||||||
if (result == olm_error())
|
|
||||||
{
|
|
||||||
LOGW("## markOneTimeKeysAsPublishedJni(): failure - Msg=%s",(const char *)olm_account_last_error(accountPtr));
|
|
||||||
errorMessage = (const char *)olm_account_last_error(accountPtr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD("## markOneTimeKeysAsPublishedJni(): success - retCode=%lu",static_cast<long unsigned int>(result));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate "fallback key".
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
**/
|
|
||||||
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(generateFallbackKeyJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
OlmAccount *accountPtr = getAccountInstanceId(env, thiz);
|
|
||||||
|
|
||||||
if (!accountPtr)
|
|
||||||
{
|
|
||||||
LOGE("## generateFallbackKeyJni(): failure - invalid Account ptr");
|
|
||||||
errorMessage = "invalid Account ptr";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// keys memory allocation
|
|
||||||
size_t randomLength = olm_account_generate_fallback_key_random_length(accountPtr);
|
|
||||||
LOGD("## generateFallbackKeyJni(): randomLength=%lu", static_cast<long unsigned int>(randomLength));
|
|
||||||
|
|
||||||
uint8_t *randomBufferPtr = NULL;
|
|
||||||
|
|
||||||
if ((0 != randomLength) && !setRandomInBuffer(env, &randomBufferPtr, randomLength))
|
|
||||||
{
|
|
||||||
LOGE("## generateFallbackKeyJni(): failure - random buffer init");
|
|
||||||
errorMessage = "random buffer init";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD("## generateFallbackKeyJni(): accountPtr =%p", accountPtr);
|
|
||||||
|
|
||||||
// retrieve key pairs in keysBytesPtr
|
|
||||||
size_t result = olm_account_generate_fallback_key(accountPtr, (void*)randomBufferPtr, randomLength);
|
|
||||||
|
|
||||||
if (result == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_account_last_error(accountPtr);
|
|
||||||
LOGE("## generateFallbackKeyJni(): failure - error generating fallback keys Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD("## generateFallbackKeyJni(): success - result=%lu", static_cast<long unsigned int>(result));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (randomBufferPtr)
|
|
||||||
{
|
|
||||||
memset(randomBufferPtr, 0, randomLength);
|
|
||||||
free(randomBufferPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get "fallback key".<br>
|
|
||||||
* Return the public parts of the unpublished "fallback key" for the account
|
|
||||||
* @return a valid byte array if operation succeed, null otherwise
|
|
||||||
**/
|
|
||||||
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(fallbackKeyJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
jbyteArray byteArrayRetValue = NULL;
|
|
||||||
OlmAccount* accountPtr = getAccountInstanceId(env, thiz);
|
|
||||||
|
|
||||||
LOGD("## fallbackKeyJni(): IN");
|
|
||||||
|
|
||||||
if (!accountPtr)
|
|
||||||
{
|
|
||||||
LOGE("## fallbackKeyJni(): failure - invalid Account ptr");
|
|
||||||
errorMessage = "invalid Account ptr";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// keys memory allocation
|
|
||||||
size_t keysLength = olm_account_unpublished_fallback_key_length(accountPtr);
|
|
||||||
uint8_t *keysBytesPtr = (uint8_t *)malloc(keysLength*sizeof(uint8_t));
|
|
||||||
|
|
||||||
if (!keysBytesPtr)
|
|
||||||
{
|
|
||||||
LOGE("## fallbackKeyJni(): failure - fallback key OOM");
|
|
||||||
errorMessage = "fallback key OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// retrieve key pairs in keysBytesPtr
|
|
||||||
size_t keysResult = olm_account_unpublished_fallback_key(accountPtr, keysBytesPtr, keysLength);
|
|
||||||
|
|
||||||
if (keysResult == olm_error()) {
|
|
||||||
LOGE("## fallbackKeyJni(): failure - error getting fallback key Msg=%s",(const char *)olm_account_last_error(accountPtr));
|
|
||||||
errorMessage = (const char *)olm_account_last_error(accountPtr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// allocate the byte array to be returned to java
|
|
||||||
byteArrayRetValue = env->NewByteArray(keysLength);
|
|
||||||
|
|
||||||
if (!byteArrayRetValue)
|
|
||||||
{
|
|
||||||
LOGE("## fallbackKeyJni(): failure - return byte array OOM");
|
|
||||||
errorMessage = "return byte array OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
env->SetByteArrayRegion(byteArrayRetValue, 0/*offset*/, keysLength, (const jbyte*)keysBytesPtr);
|
|
||||||
LOGD("## fallbackKeyJni(): success");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(keysBytesPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return byteArrayRetValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Forget about the old fallback key.
|
|
||||||
*
|
|
||||||
* This should be called once you are reasonably certain that you will not
|
|
||||||
* receive any more messages that use the old fallback key (e.g. 5 minutes
|
|
||||||
* after the new fallback key has been published).
|
|
||||||
**/
|
|
||||||
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(forgetFallbackKeyJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
OlmAccount *accountPtr = getAccountInstanceId(env, thiz);
|
|
||||||
|
|
||||||
if (!accountPtr)
|
|
||||||
{
|
|
||||||
LOGE("## forgetFallbackKeyJni(): failure - invalid Account ptr");
|
|
||||||
errorMessage = "invalid Account ptr";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
olm_account_forget_old_fallback_key(accountPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign a message with the ed25519 key (fingerprint) for this account.<br>
|
|
||||||
* The signed message is returned by the function.
|
|
||||||
* @param aMessage message to sign
|
|
||||||
* @return the signed message, null otherwise
|
|
||||||
**/
|
|
||||||
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(signMessageJni)(JNIEnv *env, jobject thiz, jbyteArray aMessage)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
OlmAccount* accountPtr = NULL;
|
|
||||||
jbyteArray signedMsgRetValueBuffer = NULL;
|
|
||||||
|
|
||||||
if (!aMessage)
|
|
||||||
{
|
|
||||||
LOGE("## signMessageJni(): failure - invalid aMessage param");
|
|
||||||
errorMessage = "invalid aMessage param";
|
|
||||||
}
|
|
||||||
else if (!(accountPtr = getAccountInstanceId(env, thiz)))
|
|
||||||
{
|
|
||||||
LOGE("## signMessageJni(): failure - invalid account ptr");
|
|
||||||
errorMessage = "invalid account ptr";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int messageLength = env->GetArrayLength(aMessage);
|
|
||||||
jbyte* messageToSign = env->GetByteArrayElements(aMessage, NULL);
|
|
||||||
|
|
||||||
// signature memory allocation
|
|
||||||
size_t signatureLength = olm_account_signature_length(accountPtr);
|
|
||||||
void* signedMsgPtr = malloc(signatureLength * sizeof(uint8_t));
|
|
||||||
|
|
||||||
if (!signedMsgPtr)
|
|
||||||
{
|
|
||||||
LOGE("## signMessageJni(): failure - signature allocation OOM");
|
|
||||||
errorMessage = "signature allocation OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// sign message
|
|
||||||
size_t resultSign = olm_account_sign(accountPtr,
|
|
||||||
(void*)messageToSign,
|
|
||||||
(size_t)messageLength,
|
|
||||||
signedMsgPtr,
|
|
||||||
signatureLength);
|
|
||||||
|
|
||||||
if (resultSign == olm_error())
|
|
||||||
{
|
|
||||||
LOGE("## signMessageJni(): failure - error signing message Msg=%s",(const char *)olm_account_last_error(accountPtr));
|
|
||||||
errorMessage = (const char *)olm_account_last_error(accountPtr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD("## signMessageJni(): success - retCode=%lu signatureLength=%lu", static_cast<long unsigned int>(resultSign), static_cast<long unsigned int>(signatureLength));
|
|
||||||
|
|
||||||
signedMsgRetValueBuffer = env->NewByteArray(signatureLength);
|
|
||||||
env->SetByteArrayRegion(signedMsgRetValueBuffer, 0 , signatureLength, (jbyte*)signedMsgPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(signedMsgPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// release messageToSign
|
|
||||||
if (messageToSign)
|
|
||||||
{
|
|
||||||
env->ReleaseByteArrayElements(aMessage, messageToSign, JNI_ABORT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return signedMsgRetValueBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize and encrypt account instance.<br>
|
|
||||||
* @param aKeyBuffer key used to encrypt the serialized account data
|
|
||||||
* @return the serialised account as bytes buffer.
|
|
||||||
**/
|
|
||||||
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(serializeJni)(JNIEnv *env, jobject thiz, jbyteArray aKeyBuffer)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
jbyteArray pickledDataRetValue = 0;
|
|
||||||
jbyte* keyPtr = NULL;
|
|
||||||
jboolean keyIsCopied = JNI_FALSE;
|
|
||||||
OlmAccount* accountPtr = NULL;
|
|
||||||
|
|
||||||
LOGD("## serializeJni(): IN");
|
|
||||||
|
|
||||||
if (!aKeyBuffer)
|
|
||||||
{
|
|
||||||
LOGE(" ## serializeJni(): failure - invalid key");
|
|
||||||
errorMessage = "invalid key";
|
|
||||||
}
|
|
||||||
else if (!(accountPtr = getAccountInstanceId(env, thiz)))
|
|
||||||
{
|
|
||||||
LOGE(" ## serializeJni(): failure - invalid account ptr");
|
|
||||||
errorMessage = "invalid account ptr";
|
|
||||||
}
|
|
||||||
else if (!(keyPtr = env->GetByteArrayElements(aKeyBuffer, &keyIsCopied)))
|
|
||||||
{
|
|
||||||
LOGE(" ## serializeJni(): failure - keyPtr JNI allocation OOM");
|
|
||||||
errorMessage = "keyPtr JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t pickledLength = olm_pickle_account_length(accountPtr);
|
|
||||||
size_t keyLength = (size_t)env->GetArrayLength(aKeyBuffer);
|
|
||||||
LOGD(" ## serializeJni(): pickledLength=%lu keyLength=%lu",static_cast<long unsigned int>(pickledLength), static_cast<long unsigned int>(keyLength));
|
|
||||||
|
|
||||||
void* pickledPtr = malloc(pickledLength * sizeof(uint8_t));
|
|
||||||
|
|
||||||
if (!pickledPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## serializeJni(): failure - pickledPtr buffer OOM");
|
|
||||||
errorMessage = "pickledPtr buffer OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t result = olm_pickle_account(accountPtr,
|
|
||||||
(void const *)keyPtr,
|
|
||||||
keyLength,
|
|
||||||
(void*)pickledPtr,
|
|
||||||
pickledLength);
|
|
||||||
if (result == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_account_last_error(accountPtr);
|
|
||||||
LOGE(" ## serializeJni(): failure - olm_pickle_account() Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## serializeJni(): success - result=%lu pickled=%.*s", static_cast<long unsigned int>(result), static_cast<int>(pickledLength), static_cast<char*>(pickledPtr));
|
|
||||||
pickledDataRetValue = env->NewByteArray(pickledLength);
|
|
||||||
env->SetByteArrayRegion(pickledDataRetValue, 0 , pickledLength, (jbyte*)pickledPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(pickledPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// free alloc
|
|
||||||
if (keyPtr)
|
|
||||||
{
|
|
||||||
if (keyIsCopied) {
|
|
||||||
memset(keyPtr, 0, (size_t)env->GetArrayLength(aKeyBuffer));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(aKeyBuffer, keyPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return pickledDataRetValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allocate a new account and initialise it with the serialisation data.<br>
|
|
||||||
* @param aSerializedDataBuffer the account serialisation buffer
|
|
||||||
* @param aKeyBuffer the key used to encrypt the serialized account data
|
|
||||||
* @return the deserialised account
|
|
||||||
**/
|
|
||||||
JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(deserializeJni)(JNIEnv *env, jobject thiz, jbyteArray aSerializedDataBuffer, jbyteArray aKeyBuffer)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
|
|
||||||
OlmAccount* accountPtr = NULL;
|
|
||||||
|
|
||||||
jbyte* keyPtr = NULL;
|
|
||||||
jboolean keyIsCopied = JNI_FALSE;
|
|
||||||
jbyte* pickledPtr = NULL;
|
|
||||||
|
|
||||||
LOGD("## deserializeJni(): IN");
|
|
||||||
|
|
||||||
if (!aKeyBuffer)
|
|
||||||
{
|
|
||||||
LOGE(" ## deserializeJni(): failure - invalid key");
|
|
||||||
errorMessage = "invalid key";
|
|
||||||
}
|
|
||||||
else if (!aSerializedDataBuffer)
|
|
||||||
{
|
|
||||||
LOGE(" ## deserializeJni(): failure - invalid serialized data");
|
|
||||||
errorMessage = "invalid serialized data";
|
|
||||||
}
|
|
||||||
else if (!(accountPtr = initializeAccountMemory()))
|
|
||||||
{
|
|
||||||
LOGE(" ## deserializeJni(): failure - account failure OOM");
|
|
||||||
errorMessage = "account failure OOM";
|
|
||||||
}
|
|
||||||
else if (!(keyPtr = env->GetByteArrayElements(aKeyBuffer, &keyIsCopied)))
|
|
||||||
{
|
|
||||||
LOGE(" ## deserializeJni(): failure - keyPtr JNI allocation OOM");
|
|
||||||
errorMessage = "keyPtr JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else if (!(pickledPtr = env->GetByteArrayElements(aSerializedDataBuffer, 0)))
|
|
||||||
{
|
|
||||||
LOGE(" ## deserializeJni(): failure - pickledPtr JNI allocation OOM");
|
|
||||||
errorMessage = "pickledPtr JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t pickledLength = (size_t)env->GetArrayLength(aSerializedDataBuffer);
|
|
||||||
size_t keyLength = (size_t)env->GetArrayLength(aKeyBuffer);
|
|
||||||
LOGD(" ## deserializeJni(): pickledLength=%lu keyLength=%lu",static_cast<long unsigned int>(pickledLength), static_cast<long unsigned int>(keyLength));
|
|
||||||
LOGD(" ## deserializeJni(): pickled=%.*s", static_cast<int> (pickledLength), (char const *)pickledPtr);
|
|
||||||
|
|
||||||
size_t result = olm_unpickle_account(accountPtr,
|
|
||||||
(void const *)keyPtr,
|
|
||||||
keyLength,
|
|
||||||
(void*)pickledPtr,
|
|
||||||
pickledLength);
|
|
||||||
if (result == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_account_last_error(accountPtr);
|
|
||||||
LOGE(" ## deserializeJni(): failure - olm_unpickle_account() Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## deserializeJni(): success - result=%lu ", static_cast<long unsigned int>(result));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// free alloc
|
|
||||||
if (keyPtr)
|
|
||||||
{
|
|
||||||
if (keyIsCopied) {
|
|
||||||
memset(keyPtr, 0, (size_t)env->GetArrayLength(aKeyBuffer));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(aKeyBuffer, keyPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pickledPtr)
|
|
||||||
{
|
|
||||||
env->ReleaseByteArrayElements(aSerializedDataBuffer, pickledPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
if (accountPtr)
|
|
||||||
{
|
|
||||||
olm_clear_account(accountPtr);
|
|
||||||
free(accountPtr);
|
|
||||||
}
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (jlong)(intptr_t)accountPtr;
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2017 OpenMarket Ltd
|
|
||||||
* Copyright 2017 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _OLMACCOUNT_H
|
|
||||||
#define _OLMACCOUNT_H
|
|
||||||
|
|
||||||
#include "olm_jni.h"
|
|
||||||
#include "olm/olm.h"
|
|
||||||
|
|
||||||
#define OLM_ACCOUNT_FUNC_DEF(func_name) FUNC_DEF(OlmAccount,func_name)
|
|
||||||
#define OLM_MANAGER_FUNC_DEF(func_name) FUNC_DEF(OlmManager,func_name)
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// account creation/destruction
|
|
||||||
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(releaseAccountJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(createNewAccountJni)(JNIEnv *env, jobject thiz);
|
|
||||||
|
|
||||||
// identity keys
|
|
||||||
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(identityKeysJni)(JNIEnv *env, jobject thiz);
|
|
||||||
|
|
||||||
// one time keys
|
|
||||||
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(oneTimeKeysJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(maxOneTimeKeysJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(generateOneTimeKeysJni)(JNIEnv *env, jobject thiz, jint aNumberOfKeys);
|
|
||||||
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(removeOneTimeKeysJni)(JNIEnv *env, jobject thiz, jlong aNativeOlmSessionId);
|
|
||||||
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(markOneTimeKeysAsPublishedJni)(JNIEnv *env, jobject thiz);
|
|
||||||
|
|
||||||
// fallback keys
|
|
||||||
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(generateFallbackKeyJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(fallbackKeyJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT void OLM_ACCOUNT_FUNC_DEF(forgetFallbackKeyJni)(JNIEnv *env, jobject thiz);
|
|
||||||
|
|
||||||
// signing
|
|
||||||
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(signMessageJni)(JNIEnv *env, jobject thiz, jbyteArray aMessage);
|
|
||||||
|
|
||||||
// serialization
|
|
||||||
JNIEXPORT jbyteArray OLM_ACCOUNT_FUNC_DEF(serializeJni)(JNIEnv *env, jobject thiz, jbyteArray aKeyBuffer);
|
|
||||||
JNIEXPORT jlong OLM_ACCOUNT_FUNC_DEF(deserializeJni)(JNIEnv *env, jobject thiz, jbyteArray aSerializedDataBuffer, jbyteArray aKeyBuffer);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,654 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "olm_inbound_group_session.h"
|
|
||||||
|
|
||||||
using namespace AndroidOlmSdk;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release the session allocation made by initializeInboundGroupSessionMemory().<br>
|
|
||||||
* This method MUST be called when java counter part account instance is done.
|
|
||||||
*/
|
|
||||||
JNIEXPORT void OLM_INBOUND_GROUP_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
OlmInboundGroupSession* sessionPtr = getInboundGroupSessionInstanceId(env,thiz);
|
|
||||||
|
|
||||||
LOGD("## releaseSessionJni(): InBound group session IN");
|
|
||||||
|
|
||||||
if (!sessionPtr)
|
|
||||||
{
|
|
||||||
LOGE("## releaseSessionJni(): failure - invalid inbound group session instance");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## releaseSessionJni(): sessionPtr=%p", sessionPtr);
|
|
||||||
#ifdef ENABLE_JNI_LOG
|
|
||||||
size_t retCode = olm_clear_inbound_group_session(sessionPtr);
|
|
||||||
LOGD(" ## releaseSessionJni(): clear_inbound_group_session=%lu",static_cast<long unsigned int>(retCode));
|
|
||||||
#else
|
|
||||||
olm_clear_inbound_group_session(sessionPtr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
LOGD(" ## releaseSessionJni(): free IN");
|
|
||||||
free(sessionPtr);
|
|
||||||
LOGD(" ## releaseSessionJni(): free OUT");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize a new inbound group session and return it to JAVA side.<br>
|
|
||||||
* Since a C prt is returned as a jlong, special care will be taken
|
|
||||||
* to make the cast (OlmInboundGroupSession* => jlong) platform independent.
|
|
||||||
* @param aSessionKeyBuffer session key from an outbound session
|
|
||||||
* @param isImported true when the session key has been retrieved from a backup
|
|
||||||
* @return the initialized OlmInboundGroupSession* instance or throw an exception it fails.
|
|
||||||
**/
|
|
||||||
JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(createNewSessionJni)(JNIEnv *env, jobject thiz, jbyteArray aSessionKeyBuffer, jboolean isImported)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
OlmInboundGroupSession* sessionPtr = NULL;
|
|
||||||
jbyte* sessionKeyPtr = NULL;
|
|
||||||
jboolean sessionWasCopied = JNI_FALSE;
|
|
||||||
size_t sessionSize = olm_inbound_group_session_size();
|
|
||||||
|
|
||||||
LOGD("## createNewSessionJni(): inbound group session IN");
|
|
||||||
|
|
||||||
if (!sessionSize)
|
|
||||||
{
|
|
||||||
LOGE(" ## createNewSessionJni(): failure - inbound group session size = 0");
|
|
||||||
errorMessage = "inbound group session size = 0";
|
|
||||||
}
|
|
||||||
else if (!(sessionPtr = (OlmInboundGroupSession*)malloc(sessionSize)))
|
|
||||||
{
|
|
||||||
LOGE(" ## createNewSessionJni(): failure - inbound group session OOM");
|
|
||||||
errorMessage = "inbound group session OOM";
|
|
||||||
}
|
|
||||||
else if (!aSessionKeyBuffer)
|
|
||||||
{
|
|
||||||
LOGE(" ## createNewSessionJni(): failure - invalid aSessionKey");
|
|
||||||
errorMessage = "invalid aSessionKey";
|
|
||||||
}
|
|
||||||
else if (!(sessionKeyPtr = env->GetByteArrayElements(aSessionKeyBuffer, &sessionWasCopied)))
|
|
||||||
{
|
|
||||||
LOGE(" ## createNewSessionJni(): failure - session key JNI allocation OOM");
|
|
||||||
errorMessage = "Session key JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sessionPtr = olm_inbound_group_session(sessionPtr);
|
|
||||||
|
|
||||||
size_t sessionKeyLength = (size_t)env->GetArrayLength(aSessionKeyBuffer);
|
|
||||||
LOGD(" ## createNewSessionJni(): sessionKeyLength=%lu", static_cast<long unsigned int>(sessionKeyLength));
|
|
||||||
|
|
||||||
size_t sessionResult;
|
|
||||||
|
|
||||||
if (JNI_FALSE == isImported)
|
|
||||||
{
|
|
||||||
LOGD(" ## createNewSessionJni(): init");
|
|
||||||
sessionResult = olm_init_inbound_group_session(sessionPtr, (const uint8_t*)sessionKeyPtr, sessionKeyLength);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## createNewSessionJni(): import");
|
|
||||||
sessionResult = olm_import_inbound_group_session(sessionPtr, (const uint8_t*)sessionKeyPtr, sessionKeyLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sessionResult == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_inbound_group_session_last_error(sessionPtr);
|
|
||||||
LOGE(" ## createNewSessionJni(): failure - init inbound session creation Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## createNewSessionJni(): success - result=%lu", static_cast<long unsigned int>(sessionResult));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sessionKeyPtr)
|
|
||||||
{
|
|
||||||
if (sessionWasCopied) {
|
|
||||||
memset(sessionKeyPtr, 0, (size_t)env->GetArrayLength(aSessionKeyBuffer));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(aSessionKeyBuffer, sessionKeyPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
// release the allocated session
|
|
||||||
if (sessionPtr)
|
|
||||||
{
|
|
||||||
olm_clear_inbound_group_session(sessionPtr);
|
|
||||||
free(sessionPtr);
|
|
||||||
}
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (jlong)(intptr_t)sessionPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a base64-encoded identifier for this inbound group session.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @return the base64-encoded identifier
|
|
||||||
*/
|
|
||||||
JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(sessionIdentifierJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
OlmInboundGroupSession *sessionPtr = getInboundGroupSessionInstanceId(env, thiz);
|
|
||||||
jbyteArray returnValue = 0;
|
|
||||||
|
|
||||||
LOGD("## sessionIdentifierJni(): inbound group session IN");
|
|
||||||
|
|
||||||
if (!sessionPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## sessionIdentifierJni(): failure - invalid inbound group session instance");
|
|
||||||
errorMessage = "invalid inbound group session instance";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// get the size to alloc
|
|
||||||
size_t lengthSessionId = olm_inbound_group_session_id_length(sessionPtr);
|
|
||||||
LOGD(" ## sessionIdentifierJni(): inbound group session lengthSessionId=%lu",static_cast<long unsigned int>(lengthSessionId));
|
|
||||||
|
|
||||||
uint8_t *sessionIdPtr = (uint8_t*)malloc(lengthSessionId*sizeof(uint8_t));
|
|
||||||
|
|
||||||
if (!sessionIdPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## sessionIdentifierJni(): failure - inbound group session identifier allocation OOM");
|
|
||||||
errorMessage = "inbound group session identifier allocation OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t result = olm_inbound_group_session_id(sessionPtr, sessionIdPtr, lengthSessionId);
|
|
||||||
|
|
||||||
if (result == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = (const char *)olm_inbound_group_session_last_error(sessionPtr);
|
|
||||||
LOGE(" ## sessionIdentifierJni(): failure - get inbound group session identifier failure Msg=%s",(const char *)olm_inbound_group_session_last_error(sessionPtr));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## sessionIdentifierJni(): success - inbound group session result=%lu sessionId=%.*s",static_cast<long unsigned int>(result), static_cast<int>(result), (char*)sessionIdPtr);
|
|
||||||
|
|
||||||
returnValue = env->NewByteArray(result);
|
|
||||||
env->SetByteArrayRegion(returnValue, 0 , result, (jbyte*)sessionIdPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(sessionIdPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypt a message.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aEncryptedMsg the encrypted message
|
|
||||||
* @param aDecryptMessageResult the decryptMessage information
|
|
||||||
* @return the decrypted message
|
|
||||||
*/
|
|
||||||
JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(decryptMessageJni)(JNIEnv *env, jobject thiz, jbyteArray aEncryptedMsgBuffer, jobject aDecryptionResult)
|
|
||||||
{
|
|
||||||
jbyteArray decryptedMsgBuffer = 0;
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
|
|
||||||
OlmInboundGroupSession *sessionPtr = getInboundGroupSessionInstanceId(env, thiz);
|
|
||||||
jbyte *encryptedMsgPtr = NULL;
|
|
||||||
jclass indexObjJClass = 0;
|
|
||||||
jfieldID indexMsgFieldId;
|
|
||||||
|
|
||||||
LOGD("## decryptMessageJni(): inbound group session IN");
|
|
||||||
|
|
||||||
if (!sessionPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## decryptMessageJni(): failure - invalid inbound group session ptr=NULL");
|
|
||||||
errorMessage = "invalid inbound group session ptr=NULL";
|
|
||||||
}
|
|
||||||
else if (!aEncryptedMsgBuffer)
|
|
||||||
{
|
|
||||||
LOGE(" ## decryptMessageJni(): failure - invalid encrypted message");
|
|
||||||
errorMessage = "invalid encrypted message";
|
|
||||||
}
|
|
||||||
else if (!aDecryptionResult)
|
|
||||||
{
|
|
||||||
LOGE(" ## decryptMessageJni(): failure - invalid index object");
|
|
||||||
errorMessage = "invalid index object";
|
|
||||||
}
|
|
||||||
else if (!(encryptedMsgPtr = env->GetByteArrayElements(aEncryptedMsgBuffer, 0)))
|
|
||||||
{
|
|
||||||
LOGE(" ## decryptMessageJni(): failure - encrypted message JNI allocation OOM");
|
|
||||||
errorMessage = "encrypted message JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else if (!(indexObjJClass = env->GetObjectClass(aDecryptionResult)))
|
|
||||||
{
|
|
||||||
LOGE("## decryptMessageJni(): failure - unable to get index class");
|
|
||||||
errorMessage = "unable to get index class";
|
|
||||||
}
|
|
||||||
else if (!(indexMsgFieldId = env->GetFieldID(indexObjJClass,"mIndex","J")))
|
|
||||||
{
|
|
||||||
LOGE("## decryptMessageJni(): failure - unable to get index type field");
|
|
||||||
errorMessage = "unable to get index type field";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// get encrypted message length
|
|
||||||
size_t encryptedMsgLength = (size_t)env->GetArrayLength(aEncryptedMsgBuffer);
|
|
||||||
uint8_t *tempEncryptedPtr = static_cast<uint8_t*>(malloc(encryptedMsgLength*sizeof(uint8_t)));
|
|
||||||
|
|
||||||
// create a dedicated temp buffer to be used in next Olm API calls
|
|
||||||
if (!tempEncryptedPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## decryptMessageJni(): failure - tempEncryptedPtr allocation OOM");
|
|
||||||
errorMessage = "tempEncryptedPtr allocation OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength);
|
|
||||||
LOGD(" ## decryptMessageJni(): encryptedMsgLength=%lu encryptedMsg=%.*s",static_cast<long unsigned int>(encryptedMsgLength), static_cast<int>(encryptedMsgLength), encryptedMsgPtr);
|
|
||||||
|
|
||||||
// get max plaintext length
|
|
||||||
size_t maxPlainTextLength = olm_group_decrypt_max_plaintext_length(sessionPtr,
|
|
||||||
tempEncryptedPtr,
|
|
||||||
encryptedMsgLength);
|
|
||||||
if (maxPlainTextLength == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_inbound_group_session_last_error(sessionPtr);
|
|
||||||
LOGE(" ## decryptMessageJni(): failure - olm_group_decrypt_max_plaintext_length Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## decryptMessageJni(): maxPlaintextLength=%lu",static_cast<long unsigned int>(maxPlainTextLength));
|
|
||||||
|
|
||||||
uint32_t messageIndex = 0;
|
|
||||||
|
|
||||||
// allocate output decrypted message
|
|
||||||
uint8_t *plainTextMsgPtr = static_cast<uint8_t*>(malloc(maxPlainTextLength*sizeof(uint8_t)));
|
|
||||||
|
|
||||||
// decrypt, but before reload encrypted buffer (previous one was destroyed)
|
|
||||||
memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength);
|
|
||||||
size_t plaintextLength = olm_group_decrypt(sessionPtr,
|
|
||||||
tempEncryptedPtr,
|
|
||||||
encryptedMsgLength,
|
|
||||||
plainTextMsgPtr,
|
|
||||||
maxPlainTextLength,
|
|
||||||
&messageIndex);
|
|
||||||
if (plaintextLength == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_inbound_group_session_last_error(sessionPtr);
|
|
||||||
LOGE(" ## decryptMessageJni(): failure - olm_group_decrypt Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// update index
|
|
||||||
env->SetLongField(aDecryptionResult, indexMsgFieldId, (jlong)messageIndex);
|
|
||||||
|
|
||||||
decryptedMsgBuffer = env->NewByteArray(plaintextLength);
|
|
||||||
env->SetByteArrayRegion(decryptedMsgBuffer, 0 , plaintextLength, (jbyte*)plainTextMsgPtr);
|
|
||||||
|
|
||||||
LOGD(" ## decryptMessageJni(): UTF-8 Conversion - decrypted returnedLg=%lu OK",static_cast<long unsigned int>(plaintextLength));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (plainTextMsgPtr)
|
|
||||||
{
|
|
||||||
memset(plainTextMsgPtr, 0, maxPlainTextLength*sizeof(uint8_t));
|
|
||||||
free(plainTextMsgPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tempEncryptedPtr)
|
|
||||||
{
|
|
||||||
free(tempEncryptedPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// free alloc
|
|
||||||
if (encryptedMsgPtr)
|
|
||||||
{
|
|
||||||
env->ReleaseByteArrayElements(aEncryptedMsgBuffer, encryptedMsgPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return decryptedMsgBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides the first known index.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @return the first known index
|
|
||||||
*/
|
|
||||||
JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(firstKnownIndexJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
OlmInboundGroupSession *sessionPtr = getInboundGroupSessionInstanceId(env, thiz);
|
|
||||||
long returnValue = 0;
|
|
||||||
|
|
||||||
LOGD("## firstKnownIndexJni(): inbound group session IN");
|
|
||||||
|
|
||||||
if (!sessionPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## firstKnownIndexJni(): failure - invalid inbound group session instance");
|
|
||||||
errorMessage = "invalid inbound group session instance";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
returnValue = olm_inbound_group_session_first_known_index(sessionPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells if the session is verified.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @return true if the session is verified
|
|
||||||
*/
|
|
||||||
JNIEXPORT jboolean OLM_INBOUND_GROUP_SESSION_FUNC_DEF(isVerifiedJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
OlmInboundGroupSession *sessionPtr = getInboundGroupSessionInstanceId(env, thiz);
|
|
||||||
jboolean returnValue = JNI_FALSE;
|
|
||||||
|
|
||||||
LOGD("## isVerifiedJni(): inbound group session IN");
|
|
||||||
|
|
||||||
if (!sessionPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## isVerifiedJni(): failure - invalid inbound group session instance");
|
|
||||||
errorMessage = "invalid inbound group session instance";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGE(" ## isVerifiedJni(): faaa %d", olm_inbound_group_session_is_verified(sessionPtr));
|
|
||||||
|
|
||||||
returnValue = (0 != olm_inbound_group_session_is_verified(sessionPtr)) ? JNI_TRUE : JNI_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exports the session as byte array from a message index
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param messageIndex key used to encrypt the serialized session data
|
|
||||||
* @return the session saved as bytes array
|
|
||||||
**/
|
|
||||||
JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(exportJni)(JNIEnv *env, jobject thiz, jlong messageIndex) {
|
|
||||||
jbyteArray exportedByteArray = 0;
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
OlmInboundGroupSession *sessionPtr = getInboundGroupSessionInstanceId(env, thiz);
|
|
||||||
|
|
||||||
LOGD("## exportJni(): inbound group session IN");
|
|
||||||
|
|
||||||
if (!sessionPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## exportJni (): failure - invalid inbound group session instance");
|
|
||||||
errorMessage = "invalid inbound group session instance";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t length = olm_export_inbound_group_session_length(sessionPtr);
|
|
||||||
|
|
||||||
LOGD(" ## exportJni(): length =%lu", static_cast<long unsigned int>(length));
|
|
||||||
|
|
||||||
void *bufferPtr = malloc(length * sizeof(uint8_t));
|
|
||||||
|
|
||||||
if (!bufferPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## exportJni(): failure - pickledPtr buffer OOM");
|
|
||||||
errorMessage = "pickledPtr buffer OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t result = olm_export_inbound_group_session(sessionPtr,
|
|
||||||
(uint8_t*)bufferPtr,
|
|
||||||
length,
|
|
||||||
(long) messageIndex);
|
|
||||||
|
|
||||||
if (result == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_inbound_group_session_last_error(sessionPtr);
|
|
||||||
LOGE(" ## exportJni(): failure - olm_export_inbound_group_session() Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## exportJni(): success - result=%lu buffer=%.*s", static_cast<long unsigned int>(result), static_cast<int>(length), static_cast<char*>(bufferPtr));
|
|
||||||
|
|
||||||
exportedByteArray = env->NewByteArray(length);
|
|
||||||
env->SetByteArrayRegion(exportedByteArray, 0 , length, (jbyte*)bufferPtr);
|
|
||||||
|
|
||||||
// clean before leaving
|
|
||||||
memset(bufferPtr, 0, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(bufferPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return exportedByteArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize and encrypt session instance into a base64 string.<br>
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aKeyBuffer key used to encrypt the serialized session data
|
|
||||||
* @return a base64 string if operation succeed, null otherwise
|
|
||||||
**/
|
|
||||||
JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(serializeJni)(JNIEnv *env, jobject thiz, jbyteArray aKeyBuffer)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
|
|
||||||
jbyteArray pickledDataRet = 0;
|
|
||||||
jbyte* keyPtr = NULL;
|
|
||||||
jboolean keyWasCopied = JNI_FALSE;
|
|
||||||
OlmInboundGroupSession* sessionPtr = getInboundGroupSessionInstanceId(env, thiz);
|
|
||||||
|
|
||||||
LOGD("## inbound group session serializeJni(): IN");
|
|
||||||
|
|
||||||
if (!sessionPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## serializeJni(): failure - invalid session ptr");
|
|
||||||
errorMessage = "invalid session ptr";
|
|
||||||
}
|
|
||||||
else if (!aKeyBuffer)
|
|
||||||
{
|
|
||||||
LOGE(" ## serializeJni(): failure - invalid key");
|
|
||||||
errorMessage = "invalid key";
|
|
||||||
}
|
|
||||||
else if (!(keyPtr = env->GetByteArrayElements(aKeyBuffer, &keyWasCopied)))
|
|
||||||
{
|
|
||||||
LOGE(" ## serializeJni(): failure - keyPtr JNI allocation OOM");
|
|
||||||
errorMessage = "keyPtr JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t pickledLength = olm_pickle_inbound_group_session_length(sessionPtr);
|
|
||||||
size_t keyLength = (size_t)env->GetArrayLength(aKeyBuffer);
|
|
||||||
LOGD(" ## serializeJni(): pickledLength=%lu keyLength=%lu", static_cast<long unsigned int>(pickledLength), static_cast<long unsigned int>(keyLength));
|
|
||||||
|
|
||||||
void *pickledPtr = malloc(pickledLength*sizeof(uint8_t));
|
|
||||||
|
|
||||||
if (!pickledPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## serializeJni(): failure - pickledPtr buffer OOM");
|
|
||||||
errorMessage = "pickledPtr buffer OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t result = olm_pickle_inbound_group_session(sessionPtr,
|
|
||||||
(void const *)keyPtr,
|
|
||||||
keyLength,
|
|
||||||
(void*)pickledPtr,
|
|
||||||
pickledLength);
|
|
||||||
if (result == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_inbound_group_session_last_error(sessionPtr);
|
|
||||||
LOGE(" ## serializeJni(): failure - olm_pickle_outbound_group_session() Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## serializeJni(): success - result=%lu pickled=%.*s", static_cast<long unsigned int>(result), static_cast<int>(pickledLength), static_cast<char*>(pickledPtr));
|
|
||||||
|
|
||||||
pickledDataRet = env->NewByteArray(pickledLength);
|
|
||||||
env->SetByteArrayRegion(pickledDataRet, 0 , pickledLength, (jbyte*)pickledPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(pickledPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// free alloc
|
|
||||||
if (keyPtr)
|
|
||||||
{
|
|
||||||
if (keyWasCopied) {
|
|
||||||
memset(keyPtr, 0, (size_t)env->GetArrayLength(aKeyBuffer));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(aKeyBuffer, keyPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return pickledDataRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allocate a new session and initialize it with the serialisation data.<br>
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aSerializedData the session serialisation buffer
|
|
||||||
* @param aKey the key used to encrypt the serialized account data
|
|
||||||
* @return the deserialized session
|
|
||||||
**/
|
|
||||||
JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(deserializeJni)(JNIEnv *env, jobject thiz, jbyteArray aSerializedDataBuffer, jbyteArray aKeyBuffer)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
|
|
||||||
OlmInboundGroupSession* sessionPtr = NULL;
|
|
||||||
size_t sessionSize = olm_inbound_group_session_size();
|
|
||||||
jbyte* keyPtr = NULL;
|
|
||||||
jboolean keyWasCopied = JNI_FALSE;
|
|
||||||
jbyte* pickledPtr = NULL;
|
|
||||||
|
|
||||||
LOGD("## deserializeJni(): IN");
|
|
||||||
|
|
||||||
if (!sessionSize)
|
|
||||||
{
|
|
||||||
LOGE(" ## deserializeJni(): failure - inbound group session size = 0");
|
|
||||||
errorMessage = "inbound group session size = 0";
|
|
||||||
}
|
|
||||||
else if (!(sessionPtr = (OlmInboundGroupSession*)malloc(sessionSize)))
|
|
||||||
{
|
|
||||||
LOGE(" ## deserializeJni(): failure - session failure OOM");
|
|
||||||
errorMessage = "session failure OOM";
|
|
||||||
}
|
|
||||||
else if (!aKeyBuffer)
|
|
||||||
{
|
|
||||||
LOGE(" ## deserializeJni(): failure - invalid key");
|
|
||||||
errorMessage = "invalid key";
|
|
||||||
}
|
|
||||||
else if (!aSerializedDataBuffer)
|
|
||||||
{
|
|
||||||
LOGE(" ## deserializeJni(): failure - serialized data");
|
|
||||||
errorMessage = "serialized data";
|
|
||||||
}
|
|
||||||
else if (!(keyPtr = env->GetByteArrayElements(aKeyBuffer, &keyWasCopied)))
|
|
||||||
{
|
|
||||||
LOGE(" ## deserializeJni(): failure - keyPtr JNI allocation OOM");
|
|
||||||
errorMessage = "keyPtr JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else if (!(pickledPtr = env->GetByteArrayElements(aSerializedDataBuffer, 0)))
|
|
||||||
{
|
|
||||||
LOGE(" ## deserializeJni(): failure - pickledPtr JNI allocation OOM");
|
|
||||||
errorMessage = "pickledPtr JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sessionPtr = olm_inbound_group_session(sessionPtr);
|
|
||||||
|
|
||||||
size_t pickledLength = (size_t)env->GetArrayLength(aSerializedDataBuffer);
|
|
||||||
size_t keyLength = (size_t)env->GetArrayLength(aKeyBuffer);
|
|
||||||
LOGD(" ## deserializeJni(): pickledLength=%lu keyLength=%lu",static_cast<long unsigned int>(pickledLength), static_cast<long unsigned int>(keyLength));
|
|
||||||
LOGD(" ## deserializeJni(): pickled=%.*s", static_cast<int>(pickledLength), (char const *)pickledPtr);
|
|
||||||
|
|
||||||
size_t result = olm_unpickle_inbound_group_session(sessionPtr,
|
|
||||||
(void const *)keyPtr,
|
|
||||||
keyLength,
|
|
||||||
(void*)pickledPtr,
|
|
||||||
pickledLength);
|
|
||||||
if (result == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_inbound_group_session_last_error(sessionPtr);
|
|
||||||
LOGE(" ## deserializeJni(): failure - olm_unpickle_inbound_group_session() Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## deserializeJni(): success - result=%lu ", static_cast<long unsigned int>(result));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// free alloc
|
|
||||||
if (keyPtr)
|
|
||||||
{
|
|
||||||
if (keyWasCopied) {
|
|
||||||
memset(keyPtr, 0, (size_t)env->GetArrayLength(aKeyBuffer));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(aKeyBuffer, keyPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pickledPtr)
|
|
||||||
{
|
|
||||||
env->ReleaseByteArrayElements(aSerializedDataBuffer, pickledPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
if (sessionPtr)
|
|
||||||
{
|
|
||||||
olm_clear_inbound_group_session(sessionPtr);
|
|
||||||
free(sessionPtr);
|
|
||||||
}
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (jlong)(intptr_t)sessionPtr;
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _OLMINBOUND_GROUP_SESSION_H
|
|
||||||
#define _OLMINBOUND_GROUP_SESSION_H
|
|
||||||
|
|
||||||
#include "olm_jni.h"
|
|
||||||
#include "olm/olm.h"
|
|
||||||
#include "olm/inbound_group_session.h"
|
|
||||||
|
|
||||||
#define OLM_INBOUND_GROUP_SESSION_FUNC_DEF(func_name) FUNC_DEF(OlmInboundGroupSession,func_name)
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// session creation/destruction
|
|
||||||
JNIEXPORT void OLM_INBOUND_GROUP_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(createNewSessionJni)(JNIEnv *env, jobject thiz, jbyteArray aSessionKeyBuffer, jboolean isImported);
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(sessionIdentifierJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(decryptMessageJni)(JNIEnv *env, jobject thiz, jbyteArray aEncryptedMsg, jobject aDecryptIndex);
|
|
||||||
|
|
||||||
JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(firstKnownIndexJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT jboolean OLM_INBOUND_GROUP_SESSION_FUNC_DEF(isVerifiedJni)(JNIEnv *env, jobject thiz);
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(exportJni)(JNIEnv *env, jobject thiz, jlong messageIndex);
|
|
||||||
|
|
||||||
// serialization
|
|
||||||
JNIEXPORT jbyteArray OLM_INBOUND_GROUP_SESSION_FUNC_DEF(serializeJni)(JNIEnv *env, jobject thiz, jbyteArray aKey);
|
|
||||||
JNIEXPORT jlong OLM_INBOUND_GROUP_SESSION_FUNC_DEF(deserializeJni)(JNIEnv *env, jobject thiz, jbyteArray aSerializedData, jbyteArray aKey);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,83 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016,2018,2019 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _OLMJNI_H
|
|
||||||
#define _OLMJNI_H
|
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <string>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sstream>
|
|
||||||
#include <jni.h>
|
|
||||||
#include <android/log.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define TAG "OlmJniNative"
|
|
||||||
|
|
||||||
/* logging macros */
|
|
||||||
//#define ENABLE_JNI_LOG
|
|
||||||
|
|
||||||
#ifdef NDK_DEBUG
|
|
||||||
#warning NDK_DEBUG is defined!
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef ENABLE_JNI_LOG
|
|
||||||
#warning ENABLE_JNI_LOG is defined!
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
|
|
||||||
|
|
||||||
#ifdef ENABLE_JNI_LOG
|
|
||||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
|
|
||||||
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
|
|
||||||
#else
|
|
||||||
#define LOGD(...)
|
|
||||||
#define LOGW(...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define FUNC_DEF(class_name,func_name) JNICALL Java_org_matrix_olm_##class_name##_##func_name
|
|
||||||
|
|
||||||
namespace AndroidOlmSdk
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// internal helper functions
|
|
||||||
bool setRandomInBuffer(JNIEnv *env, uint8_t **aBuffer2Ptr, size_t aRandomSize);
|
|
||||||
|
|
||||||
struct OlmSession* getSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
|
|
||||||
struct OlmAccount* getAccountInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
|
|
||||||
struct OlmInboundGroupSession* getInboundGroupSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
|
|
||||||
struct OlmOutboundGroupSession* getOutboundGroupSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
|
|
||||||
struct OlmUtility* getUtilityInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
|
|
||||||
struct OlmPkDecryption* getPkDecryptionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
|
|
||||||
struct OlmPkEncryption* getPkEncryptionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
|
|
||||||
struct OlmPkSigning* getPkSigningInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
|
|
||||||
struct OlmSAS* getOlmSasInstanceId(JNIEnv* aJniEnv, jobject aJavaObject);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,234 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016,2018,2019 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "olm_jni_helper.h"
|
|
||||||
#include "olm/olm.h"
|
|
||||||
#include <sys/time.h>
|
|
||||||
|
|
||||||
using namespace AndroidOlmSdk;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Init a buffer with a given number of random values.
|
|
||||||
* @param aBuffer2Ptr the buffer to be initialized
|
|
||||||
* @param aRandomSize the number of random values to apply
|
|
||||||
* @return true if operation succeed, false otherwise
|
|
||||||
**/
|
|
||||||
bool setRandomInBuffer(JNIEnv *env, uint8_t **aBuffer2Ptr, size_t aRandomSize)
|
|
||||||
{
|
|
||||||
bool retCode = false;
|
|
||||||
int bufferLen = aRandomSize*sizeof(uint8_t);
|
|
||||||
|
|
||||||
if (!aBuffer2Ptr)
|
|
||||||
{
|
|
||||||
LOGE("## setRandomInBuffer(): failure - aBuffer=NULL");
|
|
||||||
}
|
|
||||||
else if (!aRandomSize)
|
|
||||||
{
|
|
||||||
LOGE("## setRandomInBuffer(): failure - random size=0");
|
|
||||||
}
|
|
||||||
else if (!(*aBuffer2Ptr = (uint8_t*)malloc(bufferLen)))
|
|
||||||
{
|
|
||||||
LOGE("## setRandomInBuffer(): failure - alloc mem OOM");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD("## setRandomInBuffer(): randomSize=%lu",static_cast<long unsigned int>(aRandomSize));
|
|
||||||
|
|
||||||
// use the secureRandom class
|
|
||||||
jclass cls = env->FindClass("java/security/SecureRandom");
|
|
||||||
|
|
||||||
if (cls)
|
|
||||||
{
|
|
||||||
jobject newObj = 0;
|
|
||||||
jmethodID constructor = env->GetMethodID(cls, "<init>", "()V");
|
|
||||||
jmethodID nextByteMethod = env->GetMethodID(cls, "nextBytes", "([B)V");
|
|
||||||
|
|
||||||
if (constructor)
|
|
||||||
{
|
|
||||||
newObj = env->NewObject(cls, constructor);
|
|
||||||
jbyteArray tempByteArray = env->NewByteArray(bufferLen);
|
|
||||||
|
|
||||||
if (newObj && tempByteArray)
|
|
||||||
{
|
|
||||||
env->CallVoidMethod(newObj, nextByteMethod, tempByteArray);
|
|
||||||
|
|
||||||
if (!env->ExceptionOccurred())
|
|
||||||
{
|
|
||||||
jbyte* buffer = env->GetByteArrayElements(tempByteArray, NULL);
|
|
||||||
|
|
||||||
if (buffer)
|
|
||||||
{
|
|
||||||
memcpy(*aBuffer2Ptr, buffer, bufferLen);
|
|
||||||
retCode = true;
|
|
||||||
|
|
||||||
// clear tempByteArray to hide sensitive data.
|
|
||||||
memset(buffer, 0, bufferLen);
|
|
||||||
env->SetByteArrayRegion(tempByteArray, 0, bufferLen, buffer);
|
|
||||||
|
|
||||||
// ensure that the buffer is released
|
|
||||||
env->ReleaseByteArrayElements(tempByteArray, buffer, JNI_ABORT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tempByteArray)
|
|
||||||
{
|
|
||||||
env->DeleteLocalRef(tempByteArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newObj)
|
|
||||||
{
|
|
||||||
env->DeleteLocalRef(newObj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// debug purpose
|
|
||||||
/*for(int i = 0; i < aRandomSize; i++)
|
|
||||||
{
|
|
||||||
LOGD("## setRandomInBuffer(): randomBuffPtr[%ld]=%d",i, (*aBuffer2Ptr)[i]);
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
return retCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read the instance ID of the calling object.
|
|
||||||
* @param aJniEnv pointer pointing on the JNI function table
|
|
||||||
* @param aJavaObject reference to the object on which the method is invoked
|
|
||||||
* @param aCallingClass java calling class name
|
|
||||||
* @return the related instance ID
|
|
||||||
**/
|
|
||||||
jlong getInstanceId(JNIEnv* aJniEnv, jobject aJavaObject, const char *aCallingClass)
|
|
||||||
{
|
|
||||||
jlong instanceId = 0;
|
|
||||||
|
|
||||||
if (aJniEnv)
|
|
||||||
{
|
|
||||||
jclass requiredClass = aJniEnv->FindClass(aCallingClass);
|
|
||||||
jclass loaderClass = 0;
|
|
||||||
|
|
||||||
if (requiredClass && (JNI_TRUE != aJniEnv->IsInstanceOf(aJavaObject, requiredClass)))
|
|
||||||
{
|
|
||||||
LOGE("## getInstanceId() failure - invalid instance of");
|
|
||||||
}
|
|
||||||
else if ((loaderClass = aJniEnv->GetObjectClass(aJavaObject)))
|
|
||||||
{
|
|
||||||
jfieldID instanceIdField = aJniEnv->GetFieldID(loaderClass, "mNativeId", "J");
|
|
||||||
|
|
||||||
if (instanceIdField)
|
|
||||||
{
|
|
||||||
instanceId = aJniEnv->GetLongField(aJavaObject, instanceIdField);
|
|
||||||
LOGD("## getInstanceId(): read from java instanceId=%lld",instanceId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGE("## getInstanceId() ERROR! GetFieldID=null");
|
|
||||||
}
|
|
||||||
|
|
||||||
aJniEnv->DeleteLocalRef(loaderClass);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGE("## getInstanceId() ERROR! GetObjectClass=null");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGE("## getInstanceId() ERROR! aJniEnv=NULL");
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGD("## getInstanceId() success - instanceId=%p (jlong)(intptr_t)instanceId=%lld",(void*)instanceId, (jlong)(intptr_t)instanceId);
|
|
||||||
|
|
||||||
return instanceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read the account instance ID of the calling object.
|
|
||||||
* @param aJniEnv pointer pointing on the JNI function table
|
|
||||||
* @param aJavaObject reference to the object on which the method is invoked
|
|
||||||
* @return the related OlmAccount.
|
|
||||||
**/
|
|
||||||
struct OlmAccount* getAccountInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
|
|
||||||
{
|
|
||||||
return (struct OlmAccount*)getInstanceId(aJniEnv, aJavaObject, CLASS_OLM_ACCOUNT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read the session instance ID of the calling object (aJavaObject).<br>
|
|
||||||
* @param aJniEnv pointer pointing on the JNI function table
|
|
||||||
* @param aJavaObject reference to the object on which the method is invoked
|
|
||||||
* @return the related OlmSession.
|
|
||||||
**/
|
|
||||||
struct OlmSession* getSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
|
|
||||||
{
|
|
||||||
return (struct OlmSession*)getInstanceId(aJniEnv, aJavaObject, CLASS_OLM_SESSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read the inbound group session instance ID of the calling object (aJavaObject).<br>
|
|
||||||
* @param aJniEnv pointer pointing on the JNI function table
|
|
||||||
* @param aJavaObject reference to the object on which the method is invoked
|
|
||||||
* @return the related OlmInboundGroupSession.
|
|
||||||
**/
|
|
||||||
struct OlmInboundGroupSession* getInboundGroupSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
|
|
||||||
{
|
|
||||||
return (struct OlmInboundGroupSession*)getInstanceId(aJniEnv, aJavaObject, CLASS_OLM_INBOUND_GROUP_SESSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read the outbound group session instance ID of the calling object (aJavaObject).<br>
|
|
||||||
* @param aJniEnv pointer pointing on the JNI function table
|
|
||||||
* @param aJavaObject reference to the object on which the method is invoked
|
|
||||||
* @return the related OlmOutboundGroupSession
|
|
||||||
**/
|
|
||||||
struct OlmOutboundGroupSession* getOutboundGroupSessionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
|
|
||||||
{
|
|
||||||
return (struct OlmOutboundGroupSession*)getInstanceId(aJniEnv, aJavaObject, CLASS_OLM_OUTBOUND_GROUP_SESSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read the utility instance ID of the calling object (aJavaObject).<br>
|
|
||||||
* @param aJniEnv pointer pointing on the JNI function table
|
|
||||||
* @param aJavaObject reference to the object on which the method is invoked
|
|
||||||
* @return the related OlmUtility
|
|
||||||
**/
|
|
||||||
struct OlmUtility* getUtilityInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
|
|
||||||
{
|
|
||||||
return (struct OlmUtility*)getInstanceId(aJniEnv, aJavaObject, CLASS_OLM_UTILITY);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OlmPkDecryption* getPkDecryptionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
|
|
||||||
{
|
|
||||||
return (struct OlmPkDecryption*)getInstanceId(aJniEnv, aJavaObject, CLASS_OLM_PK_DECRYPTION);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OlmPkEncryption* getPkEncryptionInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
|
|
||||||
{
|
|
||||||
return (struct OlmPkEncryption*)getInstanceId(aJniEnv, aJavaObject, CLASS_OLM_PK_ENCRYPTION);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OlmPkSigning* getPkSigningInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
|
|
||||||
{
|
|
||||||
return (struct OlmPkSigning*)getInstanceId(aJniEnv, aJavaObject, CLASS_OLM_PK_SIGNING);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OlmSAS* getOlmSasInstanceId(JNIEnv* aJniEnv, jobject aJavaObject)
|
|
||||||
{
|
|
||||||
return (struct OlmSAS*)getInstanceId(aJniEnv, aJavaObject, CLASS_OLM_SAS);
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016,2018,2019 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "olm_jni.h"
|
|
||||||
|
|
||||||
// constant strings
|
|
||||||
namespace AndroidOlmSdk
|
|
||||||
{
|
|
||||||
static const char *CLASS_OLM_INBOUND_GROUP_SESSION = "org/matrix/olm/OlmInboundGroupSession";
|
|
||||||
static const char *CLASS_OLM_OUTBOUND_GROUP_SESSION = "org/matrix/olm/OlmOutboundGroupSession";
|
|
||||||
static const char *CLASS_OLM_SESSION = "org/matrix/olm/OlmSession";
|
|
||||||
static const char *CLASS_OLM_ACCOUNT = "org/matrix/olm/OlmAccount";
|
|
||||||
static const char *CLASS_OLM_UTILITY = "org/matrix/olm/OlmUtility";
|
|
||||||
static const char *CLASS_OLM_PK_ENCRYPTION = "org/matrix/olm/OlmPkEncryption";
|
|
||||||
static const char *CLASS_OLM_PK_DECRYPTION = "org/matrix/olm/OlmPkDecryption";
|
|
||||||
static const char *CLASS_OLM_PK_SIGNING = "org/matrix/olm/OlmPkSigning";
|
|
||||||
static const char *CLASS_OLM_SAS = "org/matrix/olm/OlmSAS";
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "olm_manager.h"
|
|
||||||
|
|
||||||
using namespace AndroidOlmSdk;
|
|
||||||
|
|
||||||
JNIEXPORT jstring OLM_MANAGER_FUNC_DEF(getOlmLibVersionJni)(JNIEnv* env, jobject thiz)
|
|
||||||
{
|
|
||||||
uint8_t majorVer=0, minorVer=0, patchVer=0;
|
|
||||||
jstring returnValueStr=0;
|
|
||||||
char buff[150];
|
|
||||||
|
|
||||||
olm_get_library_version(&majorVer, &minorVer, &patchVer);
|
|
||||||
LOGD("## getOlmLibVersionJni(): Major=%d Minor=%d Patch=%d", majorVer, minorVer, patchVer);
|
|
||||||
|
|
||||||
snprintf(buff, sizeof(buff), "%d.%d.%d", majorVer, minorVer, patchVer);
|
|
||||||
returnValueStr = env->NewStringUTF((const char*)buff);
|
|
||||||
|
|
||||||
return returnValueStr;
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _OLMMANAGER_H
|
|
||||||
#define _OLMMANAGER_H
|
|
||||||
|
|
||||||
#include "olm_jni.h"
|
|
||||||
#include "olm/olm.h"
|
|
||||||
|
|
||||||
#define OLM_MANAGER_FUNC_DEF(func_name) FUNC_DEF(OlmManager,func_name)
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
JNIEXPORT jstring OLM_MANAGER_FUNC_DEF(getOlmLibVersionJni)(JNIEnv *env, jobject thiz);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,563 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "olm_outbound_group_session.h"
|
|
||||||
|
|
||||||
using namespace AndroidOlmSdk;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release the session allocation made by initializeOutboundGroupSessionMemory().<br>
|
|
||||||
* This method MUST be called when java counter part account instance is done.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
JNIEXPORT void OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
LOGD("## releaseSessionJni(): OutBound group session IN");
|
|
||||||
|
|
||||||
OlmOutboundGroupSession* sessionPtr = (OlmOutboundGroupSession*)getOutboundGroupSessionInstanceId(env,thiz);
|
|
||||||
|
|
||||||
if (!sessionPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## releaseSessionJni(): failure - invalid outbound group session instance");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## releaseSessionJni(): sessionPtr=%p",sessionPtr);
|
|
||||||
|
|
||||||
#ifdef ENABLE_JNI_LOG
|
|
||||||
size_t retCode = olm_clear_outbound_group_session(sessionPtr);
|
|
||||||
LOGD(" ## releaseSessionJni(): clear_outbound_group_session=%lu",static_cast<long unsigned int>(retCode));
|
|
||||||
#else
|
|
||||||
olm_clear_outbound_group_session(sessionPtr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
LOGD(" ## releaseSessionJni(): free IN");
|
|
||||||
free(sessionPtr);
|
|
||||||
LOGD(" ## releaseSessionJni(): free OUT");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize a new outbound group session and return it to JAVA side.<br>
|
|
||||||
* Since a C prt is returned as a jlong, special care will be taken
|
|
||||||
* to make the cast (OlmOutboundGroupSession* => jlong) platform independent.
|
|
||||||
* @return the initialized OlmOutboundGroupSession* instance or throw an exception
|
|
||||||
**/
|
|
||||||
JNIEXPORT jlong OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(createNewSessionJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
|
|
||||||
OlmOutboundGroupSession* sessionPtr = NULL;
|
|
||||||
size_t sessionSize = 0;
|
|
||||||
|
|
||||||
LOGD("## createNewSessionJni(): outbound group session IN");
|
|
||||||
sessionSize = olm_outbound_group_session_size();
|
|
||||||
|
|
||||||
if (0 == sessionSize)
|
|
||||||
{
|
|
||||||
LOGE(" ## createNewSessionJni(): failure - outbound group session size = 0");
|
|
||||||
errorMessage = "outbound group session size = 0";
|
|
||||||
}
|
|
||||||
else if (!(sessionPtr = (OlmOutboundGroupSession*)malloc(sessionSize)))
|
|
||||||
{
|
|
||||||
LOGE(" ## createNewSessionJni(): failure - outbound group session OOM");
|
|
||||||
errorMessage = "outbound group session OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sessionPtr = olm_outbound_group_session(sessionPtr);
|
|
||||||
LOGD(" ## createNewSessionJni(): success - outbound group session size=%lu",static_cast<long unsigned int>(sessionSize));
|
|
||||||
|
|
||||||
// compute random buffer
|
|
||||||
size_t randomLength = olm_init_outbound_group_session_random_length(sessionPtr);
|
|
||||||
uint8_t *randomBuffPtr = NULL;
|
|
||||||
|
|
||||||
LOGW(" ## createNewSessionJni(): randomLength=%lu",static_cast<long unsigned int>(randomLength));
|
|
||||||
|
|
||||||
if ((0 != randomLength) && !setRandomInBuffer(env, &randomBuffPtr, randomLength))
|
|
||||||
{
|
|
||||||
LOGE(" ## createNewSessionJni(): failure - random buffer init");
|
|
||||||
errorMessage = "random buffer init";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (0 == randomLength)
|
|
||||||
{
|
|
||||||
LOGW(" ## createNewSessionJni(): random buffer is not required");
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sessionResult = olm_init_outbound_group_session(sessionPtr, randomBuffPtr, randomLength);
|
|
||||||
|
|
||||||
if (sessionResult == olm_error()) {
|
|
||||||
errorMessage = (const char *)olm_outbound_group_session_last_error(sessionPtr);
|
|
||||||
LOGE(" ## createNewSessionJni(): failure - init outbound session creation Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## createNewSessionJni(): success - result=%lu", static_cast<long unsigned int>(sessionResult));
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear the random buffer
|
|
||||||
memset(randomBuffPtr, 0, randomLength);
|
|
||||||
free(randomBuffPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
if (sessionPtr)
|
|
||||||
{
|
|
||||||
olm_clear_outbound_group_session(sessionPtr);
|
|
||||||
free(sessionPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (jlong)(intptr_t)sessionPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the session identifier.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @return the session identifier
|
|
||||||
*/
|
|
||||||
JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(sessionIdentifierJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
LOGD("## sessionIdentifierJni(): outbound group session IN");
|
|
||||||
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
OlmOutboundGroupSession *sessionPtr = (OlmOutboundGroupSession*)getOutboundGroupSessionInstanceId(env,thiz);
|
|
||||||
jbyteArray returnValue = 0;
|
|
||||||
|
|
||||||
if (!sessionPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## sessionIdentifierJni(): failure - invalid outbound group session instance");
|
|
||||||
errorMessage = "invalid outbound group session instance";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// get the size to alloc
|
|
||||||
size_t lengthSessionId = olm_outbound_group_session_id_length(sessionPtr);
|
|
||||||
LOGD(" ## sessionIdentifierJni(): outbound group session lengthSessionId=%lu",static_cast<long unsigned int>(lengthSessionId));
|
|
||||||
|
|
||||||
uint8_t *sessionIdPtr = (uint8_t*)malloc(lengthSessionId*sizeof(uint8_t));
|
|
||||||
|
|
||||||
if (!sessionIdPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## sessionIdentifierJni(): failure - outbound identifier allocation OOM");
|
|
||||||
errorMessage = "outbound identifier allocation OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t result = olm_outbound_group_session_id(sessionPtr, sessionIdPtr, lengthSessionId);
|
|
||||||
|
|
||||||
if (result == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = reinterpret_cast<const char*>(olm_outbound_group_session_last_error(sessionPtr));
|
|
||||||
LOGE(" ## sessionIdentifierJni(): failure - outbound group session identifier failure Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
returnValue = env->NewByteArray(result);
|
|
||||||
env->SetByteArrayRegion(returnValue, 0 , result, (jbyte*)sessionIdPtr);
|
|
||||||
|
|
||||||
LOGD(" ## sessionIdentifierJni(): success - outbound group session identifier result=%lu sessionId= %.*s",static_cast<long unsigned int>(result), static_cast<int>(result), reinterpret_cast<char*>(sessionIdPtr));
|
|
||||||
}
|
|
||||||
|
|
||||||
// free alloc
|
|
||||||
free(sessionIdPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current message index for this session.<br>
|
|
||||||
* Each message is sent with an increasing index, this
|
|
||||||
* method returns the index for the next message.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @return current session index
|
|
||||||
*/
|
|
||||||
JNIEXPORT jint OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(messageIndexJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
OlmOutboundGroupSession *sessionPtr = NULL;
|
|
||||||
jint indexRetValue = 0;
|
|
||||||
|
|
||||||
LOGD("## messageIndexJni(): IN");
|
|
||||||
|
|
||||||
if (!(sessionPtr = (OlmOutboundGroupSession*)getOutboundGroupSessionInstanceId(env,thiz)))
|
|
||||||
{
|
|
||||||
LOGE(" ## messageIndexJni(): failure - invalid outbound group session instance");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
indexRetValue = static_cast<jint>(olm_outbound_group_session_message_index(sessionPtr));
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGD(" ## messageIndexJni(): success - index=%d",indexRetValue);
|
|
||||||
|
|
||||||
return indexRetValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the session key.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @return the session key
|
|
||||||
*/
|
|
||||||
JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(sessionKeyJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
LOGD("## sessionKeyJni(): outbound group session IN");
|
|
||||||
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
OlmOutboundGroupSession *sessionPtr = (OlmOutboundGroupSession*)getOutboundGroupSessionInstanceId(env,thiz);
|
|
||||||
jbyteArray returnValue = 0;
|
|
||||||
|
|
||||||
if (!sessionPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## sessionKeyJni(): failure - invalid outbound group session instance");
|
|
||||||
errorMessage = "invalid outbound group session instance";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// get the size to alloc
|
|
||||||
size_t sessionKeyLength = olm_outbound_group_session_key_length(sessionPtr);
|
|
||||||
LOGD(" ## sessionKeyJni(): sessionKeyLength=%lu",static_cast<long unsigned int>(sessionKeyLength));
|
|
||||||
|
|
||||||
uint8_t *sessionKeyPtr = (uint8_t*)malloc(sessionKeyLength*sizeof(uint8_t));
|
|
||||||
|
|
||||||
if (!sessionKeyPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## sessionKeyJni(): failure - session key allocation OOM");
|
|
||||||
errorMessage = "session key allocation OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t result = olm_outbound_group_session_key(sessionPtr, sessionKeyPtr, sessionKeyLength);
|
|
||||||
|
|
||||||
if (result == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = (const char *)olm_outbound_group_session_last_error(sessionPtr);
|
|
||||||
LOGE(" ## sessionKeyJni(): failure - session key failure Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## sessionKeyJni(): success - outbound group session key result=%lu sessionKey=%.*s",static_cast<long unsigned int>(result), static_cast<int>(result), reinterpret_cast<char*>(sessionKeyPtr));
|
|
||||||
|
|
||||||
returnValue = env->NewByteArray(result);
|
|
||||||
env->SetByteArrayRegion(returnValue, 0 , result, (jbyte*)sessionKeyPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// free alloc
|
|
||||||
free(sessionKeyPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt a bytes buffer messages.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aClearMsgBuffer the message to encode
|
|
||||||
* @return the encoded message
|
|
||||||
*/
|
|
||||||
JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobject thiz, jbyteArray aClearMsgBuffer)
|
|
||||||
{
|
|
||||||
LOGD("## encryptMessageJni(): IN");
|
|
||||||
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
jbyteArray encryptedMsgRet = 0;
|
|
||||||
|
|
||||||
OlmOutboundGroupSession *sessionPtr = NULL;
|
|
||||||
jbyte* clearMsgPtr = NULL;
|
|
||||||
jboolean clearMsgIsCopied = JNI_FALSE;
|
|
||||||
|
|
||||||
if (!(sessionPtr = (OlmOutboundGroupSession*)getOutboundGroupSessionInstanceId(env,thiz)))
|
|
||||||
{
|
|
||||||
LOGE(" ## encryptMessageJni(): failure - invalid outbound group session ptr=NULL");
|
|
||||||
errorMessage = "invalid outbound group session ptr=NULL";
|
|
||||||
}
|
|
||||||
else if (!aClearMsgBuffer)
|
|
||||||
{
|
|
||||||
LOGE(" ## encryptMessageJni(): failure - invalid clear message");
|
|
||||||
errorMessage = "invalid clear message";
|
|
||||||
}
|
|
||||||
else if (!(clearMsgPtr = env->GetByteArrayElements(aClearMsgBuffer, &clearMsgIsCopied)))
|
|
||||||
{
|
|
||||||
LOGE(" ## encryptMessageJni(): failure - clear message JNI allocation OOM");
|
|
||||||
errorMessage = "clear message JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// get clear message length
|
|
||||||
size_t clearMsgLength = (size_t)env->GetArrayLength(aClearMsgBuffer);
|
|
||||||
LOGD(" ## encryptMessageJni(): clearMsgLength=%lu",static_cast<long unsigned int>(clearMsgLength));
|
|
||||||
|
|
||||||
// compute max encrypted length
|
|
||||||
size_t encryptedMsgLength = olm_group_encrypt_message_length(sessionPtr,clearMsgLength);
|
|
||||||
uint8_t *encryptedMsgPtr = (uint8_t*)malloc(encryptedMsgLength*sizeof(uint8_t));
|
|
||||||
|
|
||||||
if (!encryptedMsgPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## encryptMessageJni(): failure - encryptedMsgPtr buffer OOM");
|
|
||||||
errorMessage = "encryptedMsgPtr buffer OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## encryptMessageJni(): estimated encryptedMsgLength=%lu",static_cast<long unsigned int>(encryptedMsgLength));
|
|
||||||
|
|
||||||
size_t encryptedLength = olm_group_encrypt(sessionPtr,
|
|
||||||
(uint8_t*)clearMsgPtr,
|
|
||||||
clearMsgLength,
|
|
||||||
encryptedMsgPtr,
|
|
||||||
encryptedMsgLength);
|
|
||||||
|
|
||||||
|
|
||||||
if (encryptedLength == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_outbound_group_session_last_error(sessionPtr);
|
|
||||||
LOGE(" ## encryptMessageJni(): failure - olm_group_decrypt_max_plaintext_length Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## encryptMessageJni(): encrypted returnedLg=%lu plainTextMsgPtr=%.*s",static_cast<long unsigned int>(encryptedLength), static_cast<int>(encryptedLength), reinterpret_cast<char*>(encryptedMsgPtr));
|
|
||||||
|
|
||||||
encryptedMsgRet = env->NewByteArray(encryptedLength);
|
|
||||||
env->SetByteArrayRegion(encryptedMsgRet, 0 , encryptedLength, (jbyte*)encryptedMsgPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(encryptedMsgPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// free alloc
|
|
||||||
if (clearMsgPtr)
|
|
||||||
{
|
|
||||||
if (clearMsgIsCopied)
|
|
||||||
{
|
|
||||||
memset(clearMsgPtr, 0, (size_t)env->GetArrayLength(aClearMsgBuffer));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(aClearMsgBuffer, clearMsgPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return encryptedMsgRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize and encrypt session instance into a base64 string.<br>
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aKey key used to encrypt the serialized session data
|
|
||||||
* @return a base64 string if operation succeed, null otherwise
|
|
||||||
**/
|
|
||||||
JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(serializeJni)(JNIEnv *env, jobject thiz, jbyteArray aKeyBuffer)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
jbyteArray returnValue = 0;
|
|
||||||
|
|
||||||
jbyte* keyPtr = NULL;
|
|
||||||
jboolean keyWasCopied = JNI_FALSE;
|
|
||||||
OlmOutboundGroupSession* sessionPtr = NULL;
|
|
||||||
|
|
||||||
LOGD("## outbound group session serializeJni(): IN");
|
|
||||||
|
|
||||||
if (!(sessionPtr = (OlmOutboundGroupSession*)getOutboundGroupSessionInstanceId(env,thiz)))
|
|
||||||
{
|
|
||||||
LOGE(" ## serializeJni(): failure - invalid session ptr");
|
|
||||||
errorMessage = "invalid session ptr";
|
|
||||||
}
|
|
||||||
else if (!aKeyBuffer)
|
|
||||||
{
|
|
||||||
LOGE(" ## serializeJni(): failure - invalid key");
|
|
||||||
errorMessage = "invalid key";
|
|
||||||
}
|
|
||||||
else if (!(keyPtr = env->GetByteArrayElements(aKeyBuffer, &keyWasCopied)))
|
|
||||||
{
|
|
||||||
LOGE(" ## serializeJni(): failure - keyPtr JNI allocation OOM");
|
|
||||||
errorMessage = "keyPtr JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t pickledLength = olm_pickle_outbound_group_session_length(sessionPtr);
|
|
||||||
size_t keyLength = (size_t)env->GetArrayLength(aKeyBuffer);
|
|
||||||
LOGD(" ## serializeJni(): pickledLength=%lu keyLength=%lu",static_cast<long unsigned int>(pickledLength), static_cast<long unsigned int>(keyLength));
|
|
||||||
|
|
||||||
void *pickledPtr = malloc(pickledLength*sizeof(uint8_t));
|
|
||||||
|
|
||||||
if(!pickledPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## serializeJni(): failure - pickledPtr buffer OOM");
|
|
||||||
errorMessage = "pickledPtr buffer OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t result = olm_pickle_outbound_group_session(sessionPtr,
|
|
||||||
(void const *)keyPtr,
|
|
||||||
keyLength,
|
|
||||||
(void*)pickledPtr,
|
|
||||||
pickledLength);
|
|
||||||
if (result == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_outbound_group_session_last_error(sessionPtr);
|
|
||||||
LOGE(" ## serializeJni(): failure - olm_pickle_outbound_group_session() Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## serializeJni(): success - result=%lu pickled=%.*s", static_cast<long unsigned int>(result), static_cast<int>(result), static_cast<char*>(pickledPtr));
|
|
||||||
|
|
||||||
returnValue = env->NewByteArray(pickledLength);
|
|
||||||
env->SetByteArrayRegion(returnValue, 0 , pickledLength, (jbyte*)pickledPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(pickledPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// free alloc
|
|
||||||
if (keyPtr)
|
|
||||||
{
|
|
||||||
if (keyWasCopied) {
|
|
||||||
memset(keyPtr, 0, (size_t)env->GetArrayLength(aKeyBuffer));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(aKeyBuffer, keyPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allocate a new session and initialize it with the serialisation data.<br>
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aSerializedData the session serialisation buffer
|
|
||||||
* @param aKey the key used to encrypt the serialized account data
|
|
||||||
* @return the deserialized session
|
|
||||||
**/
|
|
||||||
JNIEXPORT jlong OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(deserializeJni)(JNIEnv *env, jobject thiz, jbyteArray aSerializedDataBuffer, jbyteArray aKeyBuffer)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
size_t sessionSize = olm_outbound_group_session_size();
|
|
||||||
OlmOutboundGroupSession* sessionPtr = NULL;
|
|
||||||
|
|
||||||
jbyte* keyPtr = NULL;
|
|
||||||
jboolean keyWasCopied = JNI_FALSE;
|
|
||||||
jbyte* pickledPtr = NULL;
|
|
||||||
|
|
||||||
LOGD("## deserializeJni(): IN");
|
|
||||||
|
|
||||||
if (!sessionSize)
|
|
||||||
{
|
|
||||||
LOGE(" ## deserializeJni(): failure - outbound group session size = 0");
|
|
||||||
errorMessage = "outbound group session size = 0";
|
|
||||||
}
|
|
||||||
else if (!(sessionPtr = (OlmOutboundGroupSession*)malloc(sessionSize)))
|
|
||||||
{
|
|
||||||
LOGE(" ## deserializeJni(): failure - session failure OOM");
|
|
||||||
errorMessage = "session failure OOM";
|
|
||||||
}
|
|
||||||
else if (!aKeyBuffer)
|
|
||||||
{
|
|
||||||
LOGE(" ## deserializeJni(): failure - invalid key");
|
|
||||||
errorMessage = "invalid key";
|
|
||||||
}
|
|
||||||
else if (!aSerializedDataBuffer)
|
|
||||||
{
|
|
||||||
LOGE(" ## deserializeJni(): failure - serialized data");
|
|
||||||
errorMessage = "invalid serialized data";
|
|
||||||
}
|
|
||||||
else if (!(keyPtr = env->GetByteArrayElements(aKeyBuffer, &keyWasCopied)))
|
|
||||||
{
|
|
||||||
LOGE(" ## deserializeJni(): failure - keyPtr JNI allocation OOM");
|
|
||||||
errorMessage = "keyPtr JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else if (!(pickledPtr = env->GetByteArrayElements(aSerializedDataBuffer, 0)))
|
|
||||||
{
|
|
||||||
LOGE(" ## deserializeJni(): failure - pickledPtr JNI allocation OOM");
|
|
||||||
errorMessage = "pickledPtr JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sessionPtr = olm_outbound_group_session(sessionPtr);
|
|
||||||
size_t pickledLength = (size_t)env->GetArrayLength(aSerializedDataBuffer);
|
|
||||||
size_t keyLength = (size_t)env->GetArrayLength(aKeyBuffer);
|
|
||||||
LOGD(" ## deserializeJni(): pickledLength=%lu keyLength=%lu",static_cast<long unsigned int>(pickledLength), static_cast<long unsigned int>(keyLength));
|
|
||||||
LOGD(" ## deserializeJni(): pickled=%.*s", static_cast<int>(pickledLength), (char const *)pickledPtr);
|
|
||||||
|
|
||||||
size_t result = olm_unpickle_outbound_group_session(sessionPtr,
|
|
||||||
(void const *)keyPtr,
|
|
||||||
keyLength,
|
|
||||||
(void*)pickledPtr,
|
|
||||||
pickledLength);
|
|
||||||
if (result == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_outbound_group_session_last_error(sessionPtr);
|
|
||||||
LOGE(" ## deserializeJni(): failure - olm_unpickle_outbound_group_session() Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## deserializeJni(): success - result=%lu ", static_cast<long unsigned int>(result));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// free alloc
|
|
||||||
if (keyPtr)
|
|
||||||
{
|
|
||||||
if (keyWasCopied) {
|
|
||||||
memset(keyPtr, 0, (size_t)env->GetArrayLength(aKeyBuffer));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(aKeyBuffer, keyPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pickledPtr)
|
|
||||||
{
|
|
||||||
env->ReleaseByteArrayElements(aSerializedDataBuffer, pickledPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
if (sessionPtr)
|
|
||||||
{
|
|
||||||
olm_clear_outbound_group_session(sessionPtr);
|
|
||||||
free(sessionPtr);
|
|
||||||
}
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (jlong)(intptr_t)sessionPtr;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _OLMOUTBOUND_GROUP_SESSION_H
|
|
||||||
#define _OLMOUTBOUND_GROUP_SESSION_H
|
|
||||||
|
|
||||||
#include "olm_jni.h"
|
|
||||||
#include "olm/olm.h"
|
|
||||||
#include "olm/outbound_group_session.h"
|
|
||||||
|
|
||||||
#define OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(func_name) FUNC_DEF(OlmOutboundGroupSession,func_name)
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// session creation/destruction
|
|
||||||
JNIEXPORT void OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT jlong OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(createNewSessionJni)(JNIEnv *env, jobject thiz);
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(sessionIdentifierJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT jint OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(messageIndexJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(sessionKeyJni)(JNIEnv *env, jobject thiz);
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobject thiz, jbyteArray aClearMsgBuffer);
|
|
||||||
|
|
||||||
// serialization
|
|
||||||
JNIEXPORT jbyteArray OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(serializeJni)(JNIEnv *env, jobject thiz, jbyteArray aKey);
|
|
||||||
JNIEXPORT jlong OLM_OUTBOUND_GROUP_SESSION_FUNC_DEF(deserializeJni)(JNIEnv *env, jobject thiz, jbyteArray aSerializedData, jbyteArray aKey);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,992 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2018,2019 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "olm_pk.h"
|
|
||||||
|
|
||||||
#include "olm/olm.h"
|
|
||||||
|
|
||||||
using namespace AndroidOlmSdk;
|
|
||||||
|
|
||||||
OlmPkEncryption * initializePkEncryptionMemory()
|
|
||||||
{
|
|
||||||
size_t encryptionSize = olm_pk_encryption_size();
|
|
||||||
OlmPkEncryption *encryptionPtr = (OlmPkEncryption *)malloc(encryptionSize);
|
|
||||||
|
|
||||||
if (encryptionPtr)
|
|
||||||
{
|
|
||||||
// init encryption object
|
|
||||||
encryptionPtr = olm_pk_encryption(encryptionPtr);
|
|
||||||
LOGD(
|
|
||||||
"## initializePkEncryptionMemory(): success - OLM encryption size=%lu",
|
|
||||||
static_cast<long unsigned int>(encryptionSize)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGE("## initializePkEncryptionMemory(): failure - OOM");
|
|
||||||
}
|
|
||||||
|
|
||||||
return encryptionPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jlong OLM_PK_ENCRYPTION_FUNC_DEF(createNewPkEncryptionJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
OlmPkEncryption *encryptionPtr = initializePkEncryptionMemory();
|
|
||||||
|
|
||||||
// init encryption memory allocation
|
|
||||||
if (!encryptionPtr)
|
|
||||||
{
|
|
||||||
LOGE("## createNewPkEncryptionJni(): failure - init encryption OOM");
|
|
||||||
errorMessage = "init encryption OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD("## createNewPkEncryptionJni(): success - OLM encryption created");
|
|
||||||
LOGD(
|
|
||||||
"## createNewPkEncryptionJni(): encryptionPtr=%p (jlong)(intptr_t)encryptionPtr=%lld",
|
|
||||||
encryptionPtr, (jlong)(intptr_t)encryptionPtr
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
// release the allocated data
|
|
||||||
if (encryptionPtr)
|
|
||||||
{
|
|
||||||
olm_clear_pk_encryption(encryptionPtr);
|
|
||||||
free(encryptionPtr);
|
|
||||||
}
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (jlong)(intptr_t)encryptionPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT void OLM_PK_ENCRYPTION_FUNC_DEF(releasePkEncryptionJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
LOGD("## releasePkEncryptionJni(): IN");
|
|
||||||
|
|
||||||
OlmPkEncryption* encryptionPtr = getPkEncryptionInstanceId(env, thiz);
|
|
||||||
|
|
||||||
if (!encryptionPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## releasePkEncryptionJni(): failure - invalid Encryption ptr=NULL");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## releasePkEncryptionJni(): encryptionPtr=%p", encryptionPtr);
|
|
||||||
olm_clear_pk_encryption(encryptionPtr);
|
|
||||||
|
|
||||||
LOGD(" ## releasePkEncryptionJni(): IN");
|
|
||||||
// even if free(NULL) does not crash, logs are performed for debug
|
|
||||||
// purpose
|
|
||||||
free(encryptionPtr);
|
|
||||||
LOGD(" ## releasePkEncryptionJni(): OUT");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT void OLM_PK_ENCRYPTION_FUNC_DEF(setRecipientKeyJni)(
|
|
||||||
JNIEnv *env, jobject thiz, jbyteArray aKeyBuffer
|
|
||||||
) {
|
|
||||||
const char *errorMessage = NULL;
|
|
||||||
jbyte *keyPtr = NULL;
|
|
||||||
|
|
||||||
OlmPkEncryption *encryptionPtr = getPkEncryptionInstanceId(env, thiz);
|
|
||||||
|
|
||||||
if (!encryptionPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## pkSetRecipientKeyJni(): failure - invalid Encryption ptr=NULL");
|
|
||||||
}
|
|
||||||
else if (!aKeyBuffer)
|
|
||||||
{
|
|
||||||
LOGE(" ## pkSetRecipientKeyJni(): failure - invalid key");
|
|
||||||
errorMessage = "invalid key";
|
|
||||||
}
|
|
||||||
else if (!(keyPtr = env->GetByteArrayElements(aKeyBuffer, 0)))
|
|
||||||
{
|
|
||||||
LOGE(" ## pkSetRecipientKeyJni(): failure - key JNI allocation OOM");
|
|
||||||
errorMessage = "key JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (olm_pk_encryption_set_recipient_key(encryptionPtr, keyPtr, (size_t)env->GetArrayLength(aKeyBuffer)) == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_pk_encryption_last_error(encryptionPtr);
|
|
||||||
LOGE(
|
|
||||||
" ## pkSetRecipientKeyJni(): failure - olm_pk_encryption_set_recipient_key Msg=%s",
|
|
||||||
errorMessage
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyPtr)
|
|
||||||
{
|
|
||||||
env->ReleaseByteArrayElements(aKeyBuffer, keyPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray OLM_PK_ENCRYPTION_FUNC_DEF(encryptJni)(
|
|
||||||
JNIEnv *env, jobject thiz, jbyteArray aPlaintextBuffer, jobject aEncryptedMsg
|
|
||||||
) {
|
|
||||||
jbyteArray encryptedMsgRet = 0;
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
jbyte *plaintextPtr = NULL;
|
|
||||||
jboolean plaintextIsCopied = JNI_FALSE;
|
|
||||||
|
|
||||||
OlmPkEncryption *encryptionPtr = getPkEncryptionInstanceId(env, thiz);
|
|
||||||
jclass encryptedMsgJClass = 0;
|
|
||||||
jfieldID macFieldId;
|
|
||||||
jfieldID ephemeralFieldId;
|
|
||||||
|
|
||||||
if (!encryptionPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## pkEncryptJni(): failure - invalid Encryption ptr=NULL");
|
|
||||||
}
|
|
||||||
else if (!aPlaintextBuffer)
|
|
||||||
{
|
|
||||||
LOGE(" ## pkEncryptJni(): failure - invalid clear message");
|
|
||||||
errorMessage = "invalid clear message";
|
|
||||||
}
|
|
||||||
else if (!(plaintextPtr = env->GetByteArrayElements(aPlaintextBuffer, &plaintextIsCopied)))
|
|
||||||
{
|
|
||||||
LOGE(" ## pkEncryptJni(): failure - plaintext JNI allocation OOM");
|
|
||||||
errorMessage = "plaintext JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else if (!(encryptedMsgJClass = env->GetObjectClass(aEncryptedMsg)))
|
|
||||||
{
|
|
||||||
LOGE(" ## pkEncryptJni(): failure - unable to get encrypted message class");
|
|
||||||
errorMessage = "unable to get encrypted message class";
|
|
||||||
}
|
|
||||||
else if (!(macFieldId = env->GetFieldID(encryptedMsgJClass, "mMac", "Ljava/lang/String;")))
|
|
||||||
{
|
|
||||||
LOGE("## pkEncryptJni(): failure - unable to get MAC field");
|
|
||||||
errorMessage = "unable to get MAC field";
|
|
||||||
}
|
|
||||||
else if (!(ephemeralFieldId = env->GetFieldID(encryptedMsgJClass, "mEphemeralKey", "Ljava/lang/String;")))
|
|
||||||
{
|
|
||||||
LOGE("## pkEncryptJni(): failure - unable to get ephemeral key field");
|
|
||||||
errorMessage = "unable to get ephemeral key field";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t plaintextLength = (size_t)env->GetArrayLength(aPlaintextBuffer);
|
|
||||||
size_t ciphertextLength = olm_pk_ciphertext_length(encryptionPtr, plaintextLength);
|
|
||||||
size_t macLength = olm_pk_mac_length(encryptionPtr);
|
|
||||||
size_t ephemeralLength = olm_pk_key_length();
|
|
||||||
uint8_t *ciphertextPtr = NULL, *macPtr = NULL, *ephemeralPtr = NULL;
|
|
||||||
size_t randomLength = olm_pk_encrypt_random_length(encryptionPtr);
|
|
||||||
uint8_t *randomBuffPtr = NULL;
|
|
||||||
LOGD("## pkEncryptJni(): randomLength=%lu",static_cast<long unsigned int>(randomLength));
|
|
||||||
if (!(ciphertextPtr = (uint8_t*)malloc(ciphertextLength)))
|
|
||||||
{
|
|
||||||
LOGE("## pkEncryptJni(): failure - ciphertext JNI allocation OOM");
|
|
||||||
errorMessage = "ciphertext JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else if (!(macPtr = (uint8_t*)malloc(macLength + 1)))
|
|
||||||
{
|
|
||||||
LOGE("## pkEncryptJni(): failure - MAC JNI allocation OOM");
|
|
||||||
errorMessage = "MAC JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else if (!(ephemeralPtr = (uint8_t*)malloc(ephemeralLength + 1)))
|
|
||||||
{
|
|
||||||
LOGE("## pkEncryptJni(): failure: ephemeral key JNI allocation OOM");
|
|
||||||
errorMessage = "ephemeral JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else if (!setRandomInBuffer(env, &randomBuffPtr, randomLength))
|
|
||||||
{
|
|
||||||
LOGE("## pkEncryptJni(): failure - random buffer init");
|
|
||||||
errorMessage = "random buffer init";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
macPtr[macLength] = '\0';
|
|
||||||
ephemeralPtr[ephemeralLength] = '\0';
|
|
||||||
|
|
||||||
size_t returnValue = olm_pk_encrypt(
|
|
||||||
encryptionPtr,
|
|
||||||
plaintextPtr, plaintextLength,
|
|
||||||
ciphertextPtr, ciphertextLength,
|
|
||||||
macPtr, macLength,
|
|
||||||
ephemeralPtr, ephemeralLength,
|
|
||||||
randomBuffPtr, randomLength
|
|
||||||
);
|
|
||||||
|
|
||||||
if (returnValue == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_pk_encryption_last_error(encryptionPtr);
|
|
||||||
LOGE("## pkEncryptJni(): failure - olm_pk_encrypt Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
encryptedMsgRet = env->NewByteArray(ciphertextLength);
|
|
||||||
env->SetByteArrayRegion(
|
|
||||||
encryptedMsgRet, 0, ciphertextLength, (jbyte*)ciphertextPtr
|
|
||||||
);
|
|
||||||
|
|
||||||
jstring macStr = env->NewStringUTF((char*)macPtr);
|
|
||||||
env->SetObjectField(aEncryptedMsg, macFieldId, macStr);
|
|
||||||
jstring ephemeralStr = env->NewStringUTF((char*)ephemeralPtr);
|
|
||||||
env->SetObjectField(aEncryptedMsg, ephemeralFieldId, ephemeralStr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (randomBuffPtr)
|
|
||||||
{
|
|
||||||
memset(randomBuffPtr, 0, randomLength);
|
|
||||||
free(randomBuffPtr);
|
|
||||||
}
|
|
||||||
if (ephemeralPtr)
|
|
||||||
{
|
|
||||||
free(ephemeralPtr);
|
|
||||||
}
|
|
||||||
if (macPtr)
|
|
||||||
{
|
|
||||||
free(macPtr);
|
|
||||||
}
|
|
||||||
if (ciphertextPtr)
|
|
||||||
{
|
|
||||||
free(ciphertextPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (plaintextPtr)
|
|
||||||
{
|
|
||||||
if (plaintextIsCopied)
|
|
||||||
{
|
|
||||||
memset(plaintextPtr, 0, (size_t)env->GetArrayLength(aPlaintextBuffer));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(aPlaintextBuffer, plaintextPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return encryptedMsgRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
OlmPkDecryption * initializePkDecryptionMemory()
|
|
||||||
{
|
|
||||||
size_t decryptionSize = olm_pk_decryption_size();
|
|
||||||
OlmPkDecryption *decryptionPtr = (OlmPkDecryption *)malloc(decryptionSize);
|
|
||||||
|
|
||||||
if (decryptionPtr)
|
|
||||||
{
|
|
||||||
// init decryption object
|
|
||||||
decryptionPtr = olm_pk_decryption(decryptionPtr);
|
|
||||||
LOGD(
|
|
||||||
"## initializePkDecryptionMemory(): success - OLM decryption size=%lu",
|
|
||||||
static_cast<long unsigned int>(decryptionSize)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGE("## initializePkDecryptionMemory(): failure - OOM");
|
|
||||||
}
|
|
||||||
|
|
||||||
return decryptionPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jlong OLM_PK_DECRYPTION_FUNC_DEF(createNewPkDecryptionJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
OlmPkDecryption *decryptionPtr = initializePkDecryptionMemory();
|
|
||||||
|
|
||||||
// init encryption memory allocation
|
|
||||||
if (!decryptionPtr)
|
|
||||||
{
|
|
||||||
LOGE("## createNewPkDecryptionJni(): failure - init decryption OOM");
|
|
||||||
errorMessage = "init decryption OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD("## createNewPkDecryptionJni(): success - OLM decryption created");
|
|
||||||
LOGD(
|
|
||||||
"## createNewPkDecryptionJni(): decryptionPtr=%p (jlong)(intptr_t)decryptionPtr=%lld",
|
|
||||||
decryptionPtr, (jlong)(intptr_t)decryptionPtr
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
// release the allocated data
|
|
||||||
if (decryptionPtr)
|
|
||||||
{
|
|
||||||
olm_clear_pk_decryption(decryptionPtr);
|
|
||||||
free(decryptionPtr);
|
|
||||||
}
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (jlong)(intptr_t)decryptionPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT void OLM_PK_DECRYPTION_FUNC_DEF(releasePkDecryptionJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
LOGD("## releasePkDecryptionJni(): IN");
|
|
||||||
|
|
||||||
OlmPkDecryption* decryptionPtr = getPkDecryptionInstanceId(env, thiz);
|
|
||||||
|
|
||||||
if (!decryptionPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## releasePkDecryptionJni(): failure - invalid Decryption ptr=NULL");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## releasePkDecryptionJni(): decryptionPtr=%p", encryptionPtr);
|
|
||||||
olm_clear_pk_decryption(decryptionPtr);
|
|
||||||
|
|
||||||
LOGD(" ## releasePkDecryptionJni(): IN");
|
|
||||||
// even if free(NULL) does not crash, logs are performed for debug
|
|
||||||
// purpose
|
|
||||||
free(decryptionPtr);
|
|
||||||
LOGD(" ## releasePkDecryptionJni(): OUT");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jint OLM_PK_DECRYPTION_FUNC_DEF(privateKeyLength)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
return (jint) olm_pk_private_key_length();
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(setPrivateKeyJni)(JNIEnv *env, jobject thiz, jbyteArray key)
|
|
||||||
{
|
|
||||||
jbyteArray publicKeyRet = 0;
|
|
||||||
jbyte *keyPtr = NULL;
|
|
||||||
jboolean keyWasCopied = JNI_FALSE;
|
|
||||||
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
|
|
||||||
OlmPkDecryption* decryptionPtr = getPkDecryptionInstanceId(env, thiz);
|
|
||||||
|
|
||||||
if (!decryptionPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## pkSetPrivateKeyJni(): failure - invalid Decryption ptr=NULL");
|
|
||||||
}
|
|
||||||
else if (!key)
|
|
||||||
{
|
|
||||||
LOGE(" ## pkSetPrivateKeyJni(): failure - invalid key");
|
|
||||||
errorMessage = "invalid key";
|
|
||||||
}
|
|
||||||
else if (!(keyPtr = env->GetByteArrayElements(key, &keyWasCopied)))
|
|
||||||
{
|
|
||||||
LOGE(" ## pkSetPrivateKeyJni(): failure - key JNI allocation OOM");
|
|
||||||
errorMessage = "key JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t publicKeyLength = olm_pk_key_length();
|
|
||||||
uint8_t *publicKeyPtr = NULL;
|
|
||||||
size_t keyLength = (size_t)env->GetArrayLength(key);
|
|
||||||
if (!(publicKeyPtr = (uint8_t*)malloc(publicKeyLength)))
|
|
||||||
{
|
|
||||||
LOGE("## pkSetPrivateKeyJni(): failure - public key JNI allocation OOM");
|
|
||||||
errorMessage = "public key JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t returnValue = olm_pk_key_from_private(
|
|
||||||
decryptionPtr,
|
|
||||||
publicKeyPtr, publicKeyLength,
|
|
||||||
keyPtr, keyLength
|
|
||||||
);
|
|
||||||
if (returnValue == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_pk_decryption_last_error(decryptionPtr);
|
|
||||||
LOGE(" ## pkSetPrivateKeyJni(): failure - olm_pk_key_from_private Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
publicKeyRet = env->NewByteArray(publicKeyLength);
|
|
||||||
env->SetByteArrayRegion(
|
|
||||||
publicKeyRet, 0, publicKeyLength, (jbyte*)publicKeyPtr
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyPtr)
|
|
||||||
{
|
|
||||||
if (keyWasCopied)
|
|
||||||
{
|
|
||||||
memset(keyPtr, 0, (size_t)env->GetArrayLength(key));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(key, keyPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return publicKeyRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(generateKeyJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
size_t randomLength = olm_pk_private_key_length();
|
|
||||||
uint8_t *randomBuffPtr = NULL;
|
|
||||||
|
|
||||||
jbyteArray publicKeyRet = 0;
|
|
||||||
uint8_t *publicKeyPtr = NULL;
|
|
||||||
size_t publicKeyLength = olm_pk_key_length();
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
|
|
||||||
OlmPkDecryption *decryptionPtr = getPkDecryptionInstanceId(env, thiz);
|
|
||||||
|
|
||||||
if (!decryptionPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## pkGenerateKeyJni(): failure - invalid Decryption ptr=NULL");
|
|
||||||
errorMessage = "invalid Decryption ptr=NULL";
|
|
||||||
}
|
|
||||||
else if (!setRandomInBuffer(env, &randomBuffPtr, randomLength))
|
|
||||||
{
|
|
||||||
LOGE("## pkGenerateKeyJni(): failure - random buffer init");
|
|
||||||
errorMessage = "random buffer init";
|
|
||||||
}
|
|
||||||
else if (!(publicKeyPtr = static_cast<uint8_t*>(malloc(publicKeyLength))))
|
|
||||||
{
|
|
||||||
LOGE("## pkGenerateKeyJni(): failure - public key allocation OOM");
|
|
||||||
errorMessage = "public key allocation OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (olm_pk_key_from_private(decryptionPtr, publicKeyPtr, publicKeyLength, randomBuffPtr, randomLength) == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_pk_decryption_last_error(decryptionPtr);
|
|
||||||
LOGE("## pkGenerateKeyJni(): failure - olm_pk_generate_key Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
publicKeyRet = env->NewByteArray(publicKeyLength);
|
|
||||||
env->SetByteArrayRegion(publicKeyRet, 0, publicKeyLength, (jbyte*)publicKeyPtr);
|
|
||||||
LOGD("## pkGenerateKeyJni(): public key generated");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (randomBuffPtr)
|
|
||||||
{
|
|
||||||
memset(randomBuffPtr, 0, randomLength);
|
|
||||||
free(randomBuffPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return publicKeyRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(privateKeyJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
jbyteArray privateKeyRet = 0;
|
|
||||||
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
|
|
||||||
OlmPkDecryption* decryptionPtr = getPkDecryptionInstanceId(env, thiz);
|
|
||||||
|
|
||||||
if (!decryptionPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## pkPrivateKeyJni(): failure - invalid Decryption ptr=NULL");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t privateKeyLength = olm_pk_private_key_length();
|
|
||||||
uint8_t *privateKeyPtr = NULL;
|
|
||||||
if (!(privateKeyPtr = (uint8_t*)malloc(privateKeyLength)))
|
|
||||||
{
|
|
||||||
LOGE("## pkPrivateKeyJni(): failure - private key JNI allocation OOM");
|
|
||||||
errorMessage = "private key JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t returnValue = olm_pk_get_private_key(
|
|
||||||
decryptionPtr,
|
|
||||||
privateKeyPtr, privateKeyLength
|
|
||||||
);
|
|
||||||
if (returnValue == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_pk_decryption_last_error(decryptionPtr);
|
|
||||||
LOGE(" ## pkPrivateKeyJni(): failure - olm_pk_get_private_key Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
privateKeyRet = env->NewByteArray(privateKeyLength);
|
|
||||||
env->SetByteArrayRegion(
|
|
||||||
privateKeyRet, 0, privateKeyLength, (jbyte*)privateKeyPtr
|
|
||||||
);
|
|
||||||
memset(privateKeyPtr, 0, privateKeyLength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return privateKeyRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(decryptJni)(
|
|
||||||
JNIEnv *env, jobject thiz, jobject aEncryptedMsg
|
|
||||||
) {
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
OlmPkDecryption *decryptionPtr = getPkDecryptionInstanceId(env, thiz);
|
|
||||||
|
|
||||||
jclass encryptedMsgJClass = 0;
|
|
||||||
jstring ciphertextJstring = 0;
|
|
||||||
jstring macJstring = 0;
|
|
||||||
jstring ephemeralKeyJstring = 0;
|
|
||||||
jfieldID ciphertextFieldId;
|
|
||||||
jfieldID macFieldId;
|
|
||||||
jfieldID ephemeralKeyFieldId;
|
|
||||||
|
|
||||||
const char *ciphertextPtr = NULL;
|
|
||||||
const char *macPtr = NULL;
|
|
||||||
const char *ephemeralKeyPtr = NULL;
|
|
||||||
|
|
||||||
jbyteArray decryptedMsgRet = 0;
|
|
||||||
|
|
||||||
if (!decryptionPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## pkDecryptJni(): failure - invalid Decryption ptr=NULL");
|
|
||||||
errorMessage = "invalid Decryption ptr=NULL";
|
|
||||||
}
|
|
||||||
else if (!aEncryptedMsg)
|
|
||||||
{
|
|
||||||
LOGE(" ## pkDecryptJni(): failure - invalid encrypted message");
|
|
||||||
errorMessage = "invalid encrypted message";
|
|
||||||
}
|
|
||||||
else if (!(encryptedMsgJClass = env->GetObjectClass(aEncryptedMsg)))
|
|
||||||
{
|
|
||||||
LOGE("## pkDecryptJni(): failure - unable to get encrypted message class");
|
|
||||||
errorMessage = "unable to get encrypted message class";
|
|
||||||
}
|
|
||||||
else if (!(ciphertextFieldId = env->GetFieldID(encryptedMsgJClass,"mCipherText","Ljava/lang/String;")))
|
|
||||||
{
|
|
||||||
LOGE("## pkDecryptJni(): failure - unable to get message field");
|
|
||||||
errorMessage = "unable to get message field";
|
|
||||||
}
|
|
||||||
else if (!(ciphertextJstring = (jstring)env->GetObjectField(aEncryptedMsg, ciphertextFieldId)))
|
|
||||||
{
|
|
||||||
LOGE("## pkDecryptJni(): failure - no ciphertext");
|
|
||||||
errorMessage = "no ciphertext";
|
|
||||||
}
|
|
||||||
else if (!(ciphertextPtr = env->GetStringUTFChars(ciphertextJstring, 0)))
|
|
||||||
{
|
|
||||||
LOGE("## pkDecryptJni(): failure - ciphertext JNI allocation OOM");
|
|
||||||
errorMessage = "ciphertext JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else if (!(ciphertextJstring = (jstring)env->GetObjectField(aEncryptedMsg, ciphertextFieldId)))
|
|
||||||
{
|
|
||||||
LOGE("## pkDecryptJni(): failure - no ciphertext");
|
|
||||||
errorMessage = "no ciphertext";
|
|
||||||
}
|
|
||||||
else if (!(ciphertextPtr = env->GetStringUTFChars(ciphertextJstring, 0)))
|
|
||||||
{
|
|
||||||
LOGE("## decryptMessageJni(): failure - ciphertext JNI allocation OOM");
|
|
||||||
errorMessage = "ciphertext JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else if (!(macFieldId = env->GetFieldID(encryptedMsgJClass,"mMac","Ljava/lang/String;")))
|
|
||||||
{
|
|
||||||
LOGE("## pkDecryptJni(): failure - unable to get MAC field");
|
|
||||||
errorMessage = "unable to get MAC field";
|
|
||||||
}
|
|
||||||
else if (!(macJstring = (jstring)env->GetObjectField(aEncryptedMsg, macFieldId)))
|
|
||||||
{
|
|
||||||
LOGE("## pkDecryptJni(): failure - no MAC");
|
|
||||||
errorMessage = "no MAC";
|
|
||||||
}
|
|
||||||
else if (!(macPtr = env->GetStringUTFChars(macJstring, 0)))
|
|
||||||
{
|
|
||||||
LOGE("## pkDecryptJni(): failure - MAC JNI allocation OOM");
|
|
||||||
errorMessage = "ciphertext JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else if (!(ephemeralKeyFieldId = env->GetFieldID(encryptedMsgJClass,"mEphemeralKey","Ljava/lang/String;")))
|
|
||||||
{
|
|
||||||
LOGE("## pkDecryptJni(): failure - unable to get ephemeral key field");
|
|
||||||
errorMessage = "unable to get ephemeral key field";
|
|
||||||
}
|
|
||||||
else if (!(ephemeralKeyJstring = (jstring)env->GetObjectField(aEncryptedMsg, ephemeralKeyFieldId)))
|
|
||||||
{
|
|
||||||
LOGE("## pkDecryptJni(): failure - no ephemeral key");
|
|
||||||
errorMessage = "no ephemeral key";
|
|
||||||
}
|
|
||||||
else if (!(ephemeralKeyPtr = env->GetStringUTFChars(ephemeralKeyJstring, 0)))
|
|
||||||
{
|
|
||||||
LOGE("## pkDecryptJni(): failure - ephemeral key JNI allocation OOM");
|
|
||||||
errorMessage = "ephemeral key JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t maxPlaintextLength = olm_pk_max_plaintext_length(
|
|
||||||
decryptionPtr,
|
|
||||||
(size_t)env->GetStringUTFLength(ciphertextJstring)
|
|
||||||
);
|
|
||||||
uint8_t *plaintextPtr = NULL;
|
|
||||||
uint8_t *tempCiphertextPtr = NULL;
|
|
||||||
size_t ciphertextLength = (size_t)env->GetStringUTFLength(ciphertextJstring);
|
|
||||||
if (!(plaintextPtr = (uint8_t*)malloc(maxPlaintextLength)))
|
|
||||||
{
|
|
||||||
LOGE("## pkDecryptJni(): failure - plaintext JNI allocation OOM");
|
|
||||||
errorMessage = "plaintext JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else if (!(tempCiphertextPtr = (uint8_t*)malloc(ciphertextLength)))
|
|
||||||
{
|
|
||||||
LOGE("## pkDecryptJni(): failure - temp ciphertext JNI allocation OOM");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
memcpy(tempCiphertextPtr, ciphertextPtr, ciphertextLength);
|
|
||||||
size_t plaintextLength = olm_pk_decrypt(
|
|
||||||
decryptionPtr,
|
|
||||||
ephemeralKeyPtr, (size_t)env->GetStringUTFLength(ephemeralKeyJstring),
|
|
||||||
macPtr, (size_t)env->GetStringUTFLength(macJstring),
|
|
||||||
tempCiphertextPtr, ciphertextLength,
|
|
||||||
plaintextPtr, maxPlaintextLength
|
|
||||||
);
|
|
||||||
if (plaintextLength == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_pk_decryption_last_error(decryptionPtr);
|
|
||||||
LOGE("## pkDecryptJni(): failure - olm_pk_decrypt Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
decryptedMsgRet = env->NewByteArray(plaintextLength);
|
|
||||||
env->SetByteArrayRegion(decryptedMsgRet, 0, plaintextLength, (jbyte*)plaintextPtr);
|
|
||||||
LOGD(
|
|
||||||
"## pkDecryptJni(): success returnedLg=%lu OK",
|
|
||||||
static_cast<long unsigned int>(plaintextLength)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tempCiphertextPtr)
|
|
||||||
{
|
|
||||||
free(tempCiphertextPtr);
|
|
||||||
}
|
|
||||||
if (plaintextPtr)
|
|
||||||
{
|
|
||||||
memset(plaintextPtr, 0, maxPlaintextLength);
|
|
||||||
free(plaintextPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ciphertextPtr)
|
|
||||||
{
|
|
||||||
env->ReleaseStringUTFChars(ciphertextJstring, ciphertextPtr);
|
|
||||||
}
|
|
||||||
if (macPtr)
|
|
||||||
{
|
|
||||||
env->ReleaseStringUTFChars(macJstring, macPtr);
|
|
||||||
}
|
|
||||||
if (ephemeralKeyPtr)
|
|
||||||
{
|
|
||||||
env->ReleaseStringUTFChars(ephemeralKeyJstring, ephemeralKeyPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return decryptedMsgRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
OlmPkSigning * initializePkSigningMemory()
|
|
||||||
{
|
|
||||||
size_t signingSize = olm_pk_signing_size();
|
|
||||||
OlmPkSigning *signingPtr = (OlmPkSigning *)malloc(signingSize);
|
|
||||||
|
|
||||||
if (signingPtr)
|
|
||||||
{
|
|
||||||
// init encryption object
|
|
||||||
signingPtr = olm_pk_signing(signingPtr);
|
|
||||||
LOGD(
|
|
||||||
"## initializePkSigningMemory(): success - OLM signing size=%lu",
|
|
||||||
static_cast<long unsigned int>(signingSize)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGE("## initializePkSigningMemory(): failure - OOM");
|
|
||||||
}
|
|
||||||
|
|
||||||
return signingPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jlong OLM_PK_SIGNING_FUNC_DEF(createNewPkSigningJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
OlmPkSigning *signingPtr = initializePkSigningMemory();
|
|
||||||
|
|
||||||
// init signing memory allocation
|
|
||||||
if (!signingPtr)
|
|
||||||
{
|
|
||||||
LOGE("## createNewPkSigningJni(): failure - init signing OOM");
|
|
||||||
errorMessage = "init signing OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD("## createNewPkSigningJni(): success - OLM signing created");
|
|
||||||
LOGD(
|
|
||||||
"## createNewPkSigningJni(): signingPtr=%p (jlong)(intptr_t)signingPtr=%lld",
|
|
||||||
signingPtr, (jlong)(intptr_t)signingPtr
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
// release the allocated data
|
|
||||||
if (signingPtr)
|
|
||||||
{
|
|
||||||
olm_clear_pk_signing(signingPtr);
|
|
||||||
free(signingPtr);
|
|
||||||
}
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (jlong)(intptr_t)signingPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT void OLM_PK_SIGNING_FUNC_DEF(releasePkSigningJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
LOGD("## releasePkSigningJni(): IN");
|
|
||||||
|
|
||||||
OlmPkSigning* signingPtr = getPkSigningInstanceId(env, thiz);
|
|
||||||
|
|
||||||
if (!signingPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## releasePkSigningJni(): failure - invalid Signing ptr=NULL");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## releasePkSigningJni(): signingPtr=%p", signingPtr);
|
|
||||||
olm_clear_pk_signing(signingPtr);
|
|
||||||
|
|
||||||
LOGD(" ## releasePkSigningJni(): IN");
|
|
||||||
// even if free(NULL) does not crash, logs are performed for debug
|
|
||||||
// purpose
|
|
||||||
free(signingPtr);
|
|
||||||
LOGD(" ## releasePkSigningJni(): OUT");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray OLM_PK_SIGNING_FUNC_DEF(generateSeedJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
size_t randomLength = olm_pk_signing_seed_length();
|
|
||||||
uint8_t *randomBuffPtr = NULL;
|
|
||||||
jbyteArray randomRet = 0;
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
|
|
||||||
if (!setRandomInBuffer(env, &randomBuffPtr, randomLength))
|
|
||||||
{
|
|
||||||
errorMessage = "random buffer init";
|
|
||||||
LOGE("## pkSigningGenerateSeedJni(): failure - %s", errorMessage);
|
|
||||||
}
|
|
||||||
else if (!(randomRet = env->NewByteArray(randomLength)))
|
|
||||||
{
|
|
||||||
errorMessage = "randomRet JNI allocation OOM";
|
|
||||||
LOGE(" ## pkSigningGenerateSeedJni(): falure - %s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
env->SetByteArrayRegion(
|
|
||||||
randomRet, 0, randomLength, (jbyte*)randomBuffPtr
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (randomBuffPtr)
|
|
||||||
{
|
|
||||||
memset(randomBuffPtr, 0, randomLength);
|
|
||||||
free(randomBuffPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return randomRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jint OLM_PK_SIGNING_FUNC_DEF(seedLength)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
return (jint) olm_pk_signing_seed_length();
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray OLM_PK_SIGNING_FUNC_DEF(setKeyFromSeedJni)(JNIEnv *env, jobject thiz, jbyteArray seed)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
OlmPkSigning *signingPtr = getPkSigningInstanceId(env, thiz);
|
|
||||||
|
|
||||||
jbyteArray publicKeyRet = 0;
|
|
||||||
jbyte *seedPtr = NULL;
|
|
||||||
jboolean seedWasCopied = JNI_FALSE;
|
|
||||||
|
|
||||||
if (!signingPtr)
|
|
||||||
{
|
|
||||||
errorMessage = "invalid Siging ptr=NULL";
|
|
||||||
LOGE(" ## setPkSigningKeyFromSeedJni(): failure - %s", errorMessage);
|
|
||||||
}
|
|
||||||
else if (!seed)
|
|
||||||
{
|
|
||||||
errorMessage = "invalid seed";
|
|
||||||
LOGE(" ## setPkSigningKeyFromSeedJni: failure - %s", errorMessage);
|
|
||||||
}
|
|
||||||
else if (!(seedPtr = env->GetByteArrayElements(seed, &seedWasCopied)))
|
|
||||||
{
|
|
||||||
errorMessage = "seed JNI allocation OOM";
|
|
||||||
LOGE(" ## setPkSigningKeyFromSeedJni(): failure - %s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t publicKeyLength = olm_pk_signing_public_key_length();
|
|
||||||
uint8_t *publicKeyPtr = NULL;
|
|
||||||
size_t seedLength = (size_t)env->GetArrayLength(seed);
|
|
||||||
if (!(publicKeyPtr = (uint8_t*)malloc(publicKeyLength)))
|
|
||||||
{
|
|
||||||
errorMessage = "public key JNI allocation OOM";
|
|
||||||
LOGE(" ## setPkSigningKeyFromSeedJni(): falure - %s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t returnValue = olm_pk_signing_key_from_seed(
|
|
||||||
signingPtr,
|
|
||||||
publicKeyPtr, publicKeyLength,
|
|
||||||
seedPtr, seedLength
|
|
||||||
);
|
|
||||||
if (returnValue == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_pk_signing_last_error(signingPtr);
|
|
||||||
LOGE(" ## setPkSigningKeyFromSeedJni: failure - olm_pk_signing_key_from_seed Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!(publicKeyRet = env->NewByteArray(publicKeyLength))) {
|
|
||||||
errorMessage = "publicKeyRet JNI allocation OOM";
|
|
||||||
LOGE(" ## setPkSigningKeyFromSeedJni(): falure - %s", errorMessage);
|
|
||||||
} else {
|
|
||||||
env->SetByteArrayRegion(
|
|
||||||
publicKeyRet, 0, publicKeyLength, (jbyte*)publicKeyPtr
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (seedPtr)
|
|
||||||
{
|
|
||||||
if (seedWasCopied)
|
|
||||||
{
|
|
||||||
memset(seedPtr, 0, (size_t)env->GetArrayLength(seed));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(seed, seedPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return publicKeyRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray OLM_PK_SIGNING_FUNC_DEF(pkSignJni)(JNIEnv *env, jobject thiz, jbyteArray aMessage)
|
|
||||||
{
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
OlmPkSigning *signingPtr = getPkSigningInstanceId(env, thiz);
|
|
||||||
|
|
||||||
jbyteArray signatureRet = 0;
|
|
||||||
jbyte *messagePtr = NULL;
|
|
||||||
jboolean messageWasCopied = JNI_FALSE;
|
|
||||||
|
|
||||||
if (!signingPtr)
|
|
||||||
{
|
|
||||||
errorMessage = "invalid Siging ptr=NULL";
|
|
||||||
LOGE(" ## setPkSignJni(): failure - %s", errorMessage);
|
|
||||||
}
|
|
||||||
else if (!aMessage)
|
|
||||||
{
|
|
||||||
errorMessage = "message seed";
|
|
||||||
LOGE(" ## setPkSignJni: failure - %s", errorMessage);
|
|
||||||
}
|
|
||||||
else if (!(messagePtr = env->GetByteArrayElements(aMessage, &messageWasCopied)))
|
|
||||||
{
|
|
||||||
errorMessage = "message JNI allocation OOM";
|
|
||||||
LOGE(" ## setPkSignJni(): failure - %s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t signatureLength = olm_pk_signature_length();
|
|
||||||
uint8_t *signaturePtr = NULL;
|
|
||||||
size_t messageLength = (size_t)env->GetArrayLength(aMessage);
|
|
||||||
if (!(signaturePtr = (uint8_t*)malloc(signatureLength)))
|
|
||||||
{
|
|
||||||
errorMessage = "signature JNI allocation OOM";
|
|
||||||
LOGE(" ## setPkSignJni(): falure - %s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t returnValue = olm_pk_sign(
|
|
||||||
signingPtr,
|
|
||||||
(uint8_t *)messagePtr, messageLength,
|
|
||||||
signaturePtr, signatureLength
|
|
||||||
);
|
|
||||||
if (returnValue == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = olm_pk_signing_last_error(signingPtr);
|
|
||||||
LOGE(" ## setPkSignJni: failure - olm_pk_sign Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!(signatureRet = env->NewByteArray(signatureLength))) {
|
|
||||||
errorMessage = "signatureRet JNI allocation OOM";
|
|
||||||
LOGE(" ## setPkSignJni(): falure - %s", errorMessage);
|
|
||||||
} else {
|
|
||||||
env->SetByteArrayRegion(
|
|
||||||
signatureRet, 0, signatureLength, (jbyte*)signaturePtr
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messagePtr)
|
|
||||||
{
|
|
||||||
if (messageWasCopied)
|
|
||||||
{
|
|
||||||
memset(messagePtr, 0, (size_t)env->GetArrayLength(aMessage));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(aMessage, messagePtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return signatureRet;
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2018,2019 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _OLMPK_H
|
|
||||||
#define _OLMPK_H
|
|
||||||
|
|
||||||
#include "olm_jni.h"
|
|
||||||
#include "olm/pk.h"
|
|
||||||
|
|
||||||
#define OLM_PK_ENCRYPTION_FUNC_DEF(func_name) FUNC_DEF(OlmPkEncryption,func_name)
|
|
||||||
#define OLM_PK_DECRYPTION_FUNC_DEF(func_name) FUNC_DEF(OlmPkDecryption,func_name)
|
|
||||||
#define OLM_PK_SIGNING_FUNC_DEF(func_name) FUNC_DEF(OlmPkSigning,func_name)
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
JNIEXPORT jlong OLM_PK_ENCRYPTION_FUNC_DEF(createNewPkEncryptionJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT void OLM_PK_ENCRYPTION_FUNC_DEF(releasePkEncryptionJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT void OLM_PK_ENCRYPTION_FUNC_DEF(setRecipientKeyJni)(JNIEnv *env, jobject thiz, jbyteArray aKeyBuffer);
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray OLM_PK_ENCRYPTION_FUNC_DEF(encryptJni)(JNIEnv *env, jobject thiz, jbyteArray aPlaintextBuffer, jobject aEncryptedMsg);
|
|
||||||
|
|
||||||
JNIEXPORT jlong OLM_PK_DECRYPTION_FUNC_DEF(createNewPkDecryptionJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT void OLM_PK_DECRYPTION_FUNC_DEF(releasePkDecryptionJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT jint OLM_PK_DECRYPTION_FUNC_DEF(privateKeyLength)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(setPrivateKeyJni)(JNIEnv *env, jobject thiz, jbyteArray key);
|
|
||||||
JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(generateKeyJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(privateKeyJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT jbyteArray OLM_PK_DECRYPTION_FUNC_DEF(decryptJni)(JNIEnv *env, jobject thiz, jobject aEncryptedMsg);
|
|
||||||
|
|
||||||
JNIEXPORT jlong OLM_PK_SIGNING_FUNC_DEF(createNewPkSigningJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT void OLM_PK_SIGNING_FUNC_DEF(releasePkSigningJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT jint OLM_PK_SIGNING_FUNC_DEF(seedLength)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT jbyteArray OLM_PK_SIGNING_FUNC_DEF(generateSeedJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT jbyteArray OLM_PK_SIGNING_FUNC_DEF(setKeyFromSeedJni)(JNIEnv *env, jobject thiz, jbyteArray seed);
|
|
||||||
JNIEXPORT jbyteArray OLM_PK_SIGNING_FUNC_DEF(pkSignJni)(JNIEnv *env, jobject thiz, jbyteArray aMessage);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,470 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2019 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "olm_sas.h"
|
|
||||||
|
|
||||||
#include "olm/olm.h"
|
|
||||||
|
|
||||||
using namespace AndroidOlmSdk;
|
|
||||||
|
|
||||||
JNIEXPORT jlong OLM_SAS_FUNC_DEF(createNewSASJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
|
|
||||||
size_t sasSize = olm_sas_size();
|
|
||||||
OlmSAS *sasPtr = (OlmSAS *) malloc(sasSize);
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
|
|
||||||
if (!sasPtr)
|
|
||||||
{
|
|
||||||
LOGE("## createNewSASJni(): failure - init SAS OOM");
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), "init sas OOM");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sasPtr = olm_sas(sasPtr)
|
|
||||||
LOGD(" ## createNewSASJni(): success - sasPtr=%p (jlong)(intptr_t)accountPtr=%lld",sasPtr,(jlong)(intptr_t)sasPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t randomSize = olm_create_sas_random_length(sasPtr);
|
|
||||||
uint8_t *randomBuffPtr = NULL;
|
|
||||||
|
|
||||||
LOGD("## createNewSASJni(): randomSize=%lu",static_cast<long unsigned int>(randomSize));
|
|
||||||
|
|
||||||
if ( (0 != randomSize) && !setRandomInBuffer(env, &randomBuffPtr, randomSize))
|
|
||||||
{
|
|
||||||
LOGE("## createNewSASJni(): failure - random buffer init");
|
|
||||||
errorMessage = "Failed to init private key";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t result = olm_create_sas(sasPtr, randomBuffPtr, randomSize);
|
|
||||||
if (result == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = (const char *)olm_sas_last_error(sasPtr);
|
|
||||||
LOGE("## createNewSASJni(): failure - error creating SAS Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (randomBuffPtr)
|
|
||||||
{
|
|
||||||
memset(randomBuffPtr, 0, randomSize);
|
|
||||||
free(randomBuffPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (jlong)(intptr_t)sasPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT void OLM_SAS_FUNC_DEF(releaseSASJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
LOGD("## releaseSASJni(): IN");
|
|
||||||
OlmSAS* sasPtr = getOlmSasInstanceId(env, thiz);
|
|
||||||
|
|
||||||
if (!sasPtr)
|
|
||||||
{
|
|
||||||
LOGE("## releaseSessionJni(): failure - invalid Session ptr=NULL");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
olm_clear_sas(sasPtr);
|
|
||||||
// even if free(NULL) does not crash, logs are performed for debug purpose
|
|
||||||
free(sasPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(getPubKeyJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
LOGD("## getPubKeyJni(): IN");
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
jbyteArray returnValue = 0;
|
|
||||||
OlmSAS* sasPtr = getOlmSasInstanceId(env, thiz);
|
|
||||||
|
|
||||||
if (!sasPtr)
|
|
||||||
{
|
|
||||||
LOGE("## getPubKeyJni(): failure - invalid SAS ptr=NULL");
|
|
||||||
errorMessage = "invalid SAS ptr=NULL";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t pubKeyLength = olm_sas_pubkey_length(sasPtr);
|
|
||||||
void *pubkey = malloc(pubKeyLength*sizeof(uint8_t));
|
|
||||||
size_t result = olm_sas_get_pubkey(sasPtr, pubkey, pubKeyLength);
|
|
||||||
if (result == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = (const char *)olm_sas_last_error(sasPtr);
|
|
||||||
LOGE("## getPubKeyJni(): failure - error getting pub key Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
returnValue = env->NewByteArray(pubKeyLength);
|
|
||||||
env->SetByteArrayRegion(returnValue, 0 , pubKeyLength, (jbyte*)pubkey);
|
|
||||||
}
|
|
||||||
if (pubkey) {
|
|
||||||
free(pubkey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT void OLM_SAS_FUNC_DEF(setTheirPubKey)(JNIEnv *env, jobject thiz,jbyteArray pubKeyBuffer) {
|
|
||||||
|
|
||||||
OlmSAS* sasPtr = getOlmSasInstanceId(env, thiz);
|
|
||||||
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
jbyte *pubKeyPtr = NULL;
|
|
||||||
jboolean pubKeyWasCopied = JNI_FALSE;
|
|
||||||
|
|
||||||
if (!sasPtr)
|
|
||||||
{
|
|
||||||
LOGE("## setTheirPubKey(): failure - invalid SAS ptr=NULL");
|
|
||||||
errorMessage = "invalid SAS ptr=NULL";
|
|
||||||
} else if(!pubKeyBuffer) {
|
|
||||||
LOGE("## setTheirPubKey(): failure - invalid info");
|
|
||||||
errorMessage = "invalid pubKey";
|
|
||||||
}
|
|
||||||
else if (!(pubKeyPtr = env->GetByteArrayElements(pubKeyBuffer, &pubKeyWasCopied)))
|
|
||||||
{
|
|
||||||
LOGE(" ## setTheirPubKey(): failure - info JNI allocation OOM");
|
|
||||||
errorMessage = "info JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t pubKeyLength = (size_t)env->GetArrayLength(pubKeyBuffer);
|
|
||||||
size_t result = olm_sas_set_their_key(sasPtr,pubKeyPtr,pubKeyLength);
|
|
||||||
if (result == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = (const char *)olm_sas_last_error(sasPtr);
|
|
||||||
LOGE("## setTheirPubKey(): failure - error setting their key Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// free alloc
|
|
||||||
if (pubKeyPtr)
|
|
||||||
{
|
|
||||||
if (pubKeyWasCopied)
|
|
||||||
{
|
|
||||||
memset(pubKeyPtr, 0, (size_t)env->GetArrayLength(pubKeyBuffer));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(pubKeyBuffer, pubKeyPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(generateShortCodeJni)(JNIEnv *env, jobject thiz, jbyteArray infoStringBytes, jint byteNb) {
|
|
||||||
LOGD("## generateShortCodeJni(): IN");
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
jbyteArray returnValue = 0;
|
|
||||||
OlmSAS* sasPtr = getOlmSasInstanceId(env, thiz);
|
|
||||||
|
|
||||||
jbyte *infoPtr = NULL;
|
|
||||||
jboolean infoWasCopied = JNI_FALSE;
|
|
||||||
|
|
||||||
if (!sasPtr)
|
|
||||||
{
|
|
||||||
LOGE("## generateShortCodeJni(): failure - invalid SAS ptr=NULL");
|
|
||||||
errorMessage = "invalid SAS ptr=NULL";
|
|
||||||
} else if(!infoStringBytes) {
|
|
||||||
LOGE("## generateShortCodeJni(): failure - invalid info");
|
|
||||||
errorMessage = "invalid info";
|
|
||||||
}
|
|
||||||
else if (!(infoPtr = env->GetByteArrayElements(infoStringBytes, &infoWasCopied)))
|
|
||||||
{
|
|
||||||
LOGE(" ## generateShortCodeJni(): failure - info JNI allocation OOM");
|
|
||||||
errorMessage = "info JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
size_t shortBytesCodeLength = (size_t) byteNb;
|
|
||||||
void *shortBytesCode = malloc(shortBytesCodeLength * sizeof(uint8_t));
|
|
||||||
size_t infoLength = (size_t)env->GetArrayLength(infoStringBytes);
|
|
||||||
olm_sas_generate_bytes(sasPtr, infoPtr, infoLength, shortBytesCode, shortBytesCodeLength);
|
|
||||||
returnValue = env->NewByteArray(shortBytesCodeLength);
|
|
||||||
env->SetByteArrayRegion(returnValue, 0 , shortBytesCodeLength, (jbyte*)shortBytesCode);
|
|
||||||
free(shortBytesCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
// free alloc
|
|
||||||
if (infoPtr)
|
|
||||||
{
|
|
||||||
if (infoWasCopied)
|
|
||||||
{
|
|
||||||
memset(infoPtr, 0, (size_t)env->GetArrayLength(infoStringBytes));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(infoStringBytes, infoPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(calculateMacJni)(JNIEnv *env, jobject thiz,jbyteArray messageBuffer,jbyteArray infoBuffer) {
|
|
||||||
LOGD("## calculateMacJni(): IN");
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
jbyteArray returnValue = 0;
|
|
||||||
OlmSAS* sasPtr = getOlmSasInstanceId(env, thiz);
|
|
||||||
|
|
||||||
jbyte *messagePtr = NULL;
|
|
||||||
jboolean messageWasCopied = JNI_FALSE;
|
|
||||||
|
|
||||||
jbyte *infoPtr = NULL;
|
|
||||||
jboolean infoWasCopied = JNI_FALSE;
|
|
||||||
|
|
||||||
if (!sasPtr)
|
|
||||||
{
|
|
||||||
LOGE("## calculateMacJni(): failure - invalid SAS ptr=NULL");
|
|
||||||
errorMessage = "invalid SAS ptr=NULL";
|
|
||||||
} else if(!messageBuffer) {
|
|
||||||
LOGE("## calculateMacJni(): failure - invalid message");
|
|
||||||
errorMessage = "invalid info";
|
|
||||||
}
|
|
||||||
else if (!(messagePtr = env->GetByteArrayElements(messageBuffer, &messageWasCopied)))
|
|
||||||
{
|
|
||||||
LOGE(" ## calculateMacJni(): failure - message JNI allocation OOM");
|
|
||||||
errorMessage = "message JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else if (!(infoPtr = env->GetByteArrayElements(infoBuffer, &infoWasCopied)))
|
|
||||||
{
|
|
||||||
LOGE(" ## calculateMacJni(): failure - info JNI allocation OOM");
|
|
||||||
errorMessage = "info JNI allocation OOM";
|
|
||||||
} else {
|
|
||||||
|
|
||||||
size_t infoLength = (size_t)env->GetArrayLength(infoBuffer);
|
|
||||||
size_t messageLength = (size_t)env->GetArrayLength(messageBuffer);
|
|
||||||
size_t macLength = olm_sas_mac_length(sasPtr);
|
|
||||||
|
|
||||||
void *macPtr = malloc(macLength*sizeof(uint8_t));
|
|
||||||
|
|
||||||
size_t result = olm_sas_calculate_mac(sasPtr,messagePtr,messageLength,infoPtr,infoLength,macPtr,macLength);
|
|
||||||
if (result == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = (const char *)olm_sas_last_error(sasPtr);
|
|
||||||
LOGE("## calculateMacJni(): failure - error calculating SAS mac Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
returnValue = env->NewByteArray(macLength);
|
|
||||||
env->SetByteArrayRegion(returnValue, 0 , macLength, (jbyte*)macPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (macPtr) {
|
|
||||||
free(macPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// free alloc
|
|
||||||
if (infoPtr)
|
|
||||||
{
|
|
||||||
if (infoWasCopied)
|
|
||||||
{
|
|
||||||
memset(infoPtr, 0, (size_t)env->GetArrayLength(infoBuffer));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(infoBuffer, infoPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
if (messagePtr)
|
|
||||||
{
|
|
||||||
if (messageWasCopied)
|
|
||||||
{
|
|
||||||
memset(messagePtr, 0, (size_t)env->GetArrayLength(messageBuffer));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(messageBuffer, messagePtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(calculateMacFixedBase64Jni)(JNIEnv *env, jobject thiz,jbyteArray messageBuffer,jbyteArray infoBuffer) {
|
|
||||||
LOGD("## calculateMacFixedBase64Jni(): IN");
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
jbyteArray returnValue = 0;
|
|
||||||
OlmSAS* sasPtr = getOlmSasInstanceId(env, thiz);
|
|
||||||
|
|
||||||
jbyte *messagePtr = NULL;
|
|
||||||
jboolean messageWasCopied = JNI_FALSE;
|
|
||||||
|
|
||||||
jbyte *infoPtr = NULL;
|
|
||||||
jboolean infoWasCopied = JNI_FALSE;
|
|
||||||
|
|
||||||
if (!sasPtr)
|
|
||||||
{
|
|
||||||
LOGE("## calculateMacFixedBase64Jni(): failure - invalid SAS ptr=NULL");
|
|
||||||
errorMessage = "invalid SAS ptr=NULL";
|
|
||||||
} else if(!messageBuffer) {
|
|
||||||
LOGE("## calculateMacFixedBase64Jni(): failure - invalid message");
|
|
||||||
errorMessage = "invalid info";
|
|
||||||
}
|
|
||||||
else if (!(messagePtr = env->GetByteArrayElements(messageBuffer, &messageWasCopied)))
|
|
||||||
{
|
|
||||||
LOGE(" ## calculateMacFixedBase64Jni(): failure - message JNI allocation OOM");
|
|
||||||
errorMessage = "message JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else if (!(infoPtr = env->GetByteArrayElements(infoBuffer, &infoWasCopied)))
|
|
||||||
{
|
|
||||||
LOGE(" ## calculateMacFixedBase64Jni(): failure - info JNI allocation OOM");
|
|
||||||
errorMessage = "info JNI allocation OOM";
|
|
||||||
} else {
|
|
||||||
|
|
||||||
size_t infoLength = (size_t)env->GetArrayLength(infoBuffer);
|
|
||||||
size_t messageLength = (size_t)env->GetArrayLength(messageBuffer);
|
|
||||||
size_t macLength = olm_sas_mac_length(sasPtr);
|
|
||||||
|
|
||||||
void *macPtr = malloc(macLength*sizeof(uint8_t));
|
|
||||||
|
|
||||||
size_t result = olm_sas_calculate_mac_fixed_base64(sasPtr,messagePtr,messageLength,infoPtr,infoLength,macPtr,macLength);
|
|
||||||
if (result == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = (const char *)olm_sas_last_error(sasPtr);
|
|
||||||
LOGE("## calculateMacFixedBase64Jni(): failure - error calculating SAS mac Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
returnValue = env->NewByteArray(macLength);
|
|
||||||
env->SetByteArrayRegion(returnValue, 0 , macLength, (jbyte*)macPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (macPtr) {
|
|
||||||
free(macPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// free alloc
|
|
||||||
if (infoPtr)
|
|
||||||
{
|
|
||||||
if (infoWasCopied)
|
|
||||||
{
|
|
||||||
memset(infoPtr, 0, (size_t)env->GetArrayLength(infoBuffer));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(infoBuffer, infoPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
if (messagePtr)
|
|
||||||
{
|
|
||||||
if (messageWasCopied)
|
|
||||||
{
|
|
||||||
memset(messagePtr, 0, (size_t)env->GetArrayLength(messageBuffer));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(messageBuffer, messagePtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(calculateMacLongKdfJni)(JNIEnv *env, jobject thiz,jbyteArray messageBuffer,jbyteArray infoBuffer) {
|
|
||||||
LOGD("## calculateMacLongKdfJni(): IN");
|
|
||||||
const char* errorMessage = NULL;
|
|
||||||
jbyteArray returnValue = 0;
|
|
||||||
OlmSAS* sasPtr = getOlmSasInstanceId(env, thiz);
|
|
||||||
|
|
||||||
jbyte *messagePtr = NULL;
|
|
||||||
jboolean messageWasCopied = JNI_FALSE;
|
|
||||||
|
|
||||||
jbyte *infoPtr = NULL;
|
|
||||||
jboolean infoWasCopied = JNI_FALSE;
|
|
||||||
|
|
||||||
if (!sasPtr)
|
|
||||||
{
|
|
||||||
LOGE("## calculateMacLongKdfJni(): failure - invalid SAS ptr=NULL");
|
|
||||||
errorMessage = "invalid SAS ptr=NULL";
|
|
||||||
} else if(!messageBuffer) {
|
|
||||||
LOGE("## calculateMacLongKdfJni(): failure - invalid message");
|
|
||||||
errorMessage = "invalid info";
|
|
||||||
}
|
|
||||||
else if (!(messagePtr = env->GetByteArrayElements(messageBuffer, &messageWasCopied)))
|
|
||||||
{
|
|
||||||
LOGE(" ## calculateMacLongKdfJni(): failure - message JNI allocation OOM");
|
|
||||||
errorMessage = "message JNI allocation OOM";
|
|
||||||
}
|
|
||||||
else if (!(infoPtr = env->GetByteArrayElements(infoBuffer, &infoWasCopied)))
|
|
||||||
{
|
|
||||||
LOGE(" ## calculateMacLongKdfJni(): failure - info JNI allocation OOM");
|
|
||||||
errorMessage = "info JNI allocation OOM";
|
|
||||||
} else {
|
|
||||||
|
|
||||||
size_t infoLength = (size_t)env->GetArrayLength(infoBuffer);
|
|
||||||
size_t messageLength = (size_t)env->GetArrayLength(messageBuffer);
|
|
||||||
size_t macLength = olm_sas_mac_length(sasPtr);
|
|
||||||
|
|
||||||
void *macPtr = malloc(macLength*sizeof(uint8_t));
|
|
||||||
|
|
||||||
size_t result = olm_sas_calculate_mac_long_kdf(sasPtr,messagePtr,messageLength,infoPtr,infoLength,macPtr,macLength);
|
|
||||||
if (result == olm_error())
|
|
||||||
{
|
|
||||||
errorMessage = (const char *)olm_sas_last_error(sasPtr);
|
|
||||||
LOGE("## calculateMacLongKdfJni(): failure - error calculating SAS mac Msg=%s", errorMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
returnValue = env->NewByteArray(macLength);
|
|
||||||
env->SetByteArrayRegion(returnValue, 0 , macLength, (jbyte*)macPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (macPtr) {
|
|
||||||
free(macPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// free alloc
|
|
||||||
if (infoPtr)
|
|
||||||
{
|
|
||||||
if (infoWasCopied)
|
|
||||||
{
|
|
||||||
memset(infoPtr, 0, (size_t)env->GetArrayLength(infoBuffer));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(infoBuffer, infoPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
if (messagePtr)
|
|
||||||
{
|
|
||||||
if (messageWasCopied)
|
|
||||||
{
|
|
||||||
memset(messagePtr, 0, (size_t)env->GetArrayLength(messageBuffer));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(messageBuffer, messagePtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage)
|
|
||||||
{
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2019 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _OMLSAS_H
|
|
||||||
#define _OMLSAS_H
|
|
||||||
|
|
||||||
#include "olm_jni.h"
|
|
||||||
#include "olm/sas.h"
|
|
||||||
|
|
||||||
#define OLM_SAS_FUNC_DEF(func_name) FUNC_DEF(OlmSAS,func_name)
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
JNIEXPORT jlong OLM_SAS_FUNC_DEF(createNewSASJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT void OLM_SAS_FUNC_DEF(releaseSASJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(getPubKeyJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT void OLM_SAS_FUNC_DEF(setTheirPubKey)(JNIEnv *env, jobject thiz,jbyteArray pubKey);
|
|
||||||
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(generateShortCodeJni)(JNIEnv *env, jobject thiz, jbyteArray infoStringBytes, jint byteNb);
|
|
||||||
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(calculateMacJni)(JNIEnv *env, jobject thiz, jbyteArray messageBuffer, jbyteArray infoBuffer);
|
|
||||||
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(calculateMacFixedBase64Jni)(JNIEnv *env, jobject thiz, jbyteArray messageBuffer, jbyteArray infoBuffer);
|
|
||||||
JNIEXPORT jbyteArray OLM_SAS_FUNC_DEF(calculateMacLongKdfJni)(JNIEnv *env, jobject thiz, jbyteArray messageBuffer, jbyteArray infoBuffer);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,60 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _OLMSESSION_H
|
|
||||||
#define _OLMSESSION_H
|
|
||||||
|
|
||||||
#include "olm_jni.h"
|
|
||||||
#include "olm/olm.h"
|
|
||||||
|
|
||||||
#define OLM_SESSION_FUNC_DEF(func_name) FUNC_DEF(OlmSession,func_name)
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// session creation/destruction
|
|
||||||
JNIEXPORT void OLM_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT jlong OLM_SESSION_FUNC_DEF(createNewSessionJni)(JNIEnv *env, jobject thiz);
|
|
||||||
|
|
||||||
// outbound session
|
|
||||||
JNIEXPORT void OLM_SESSION_FUNC_DEF(initOutboundSessionJni)(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jbyteArray aTheirIdentityKey, jbyteArray aTheirOneTimeKey);
|
|
||||||
|
|
||||||
// inbound sessions: establishment based on PRE KEY message
|
|
||||||
JNIEXPORT void OLM_SESSION_FUNC_DEF(initInboundSessionJni)(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jbyteArray aOneTimeKeyMsg);
|
|
||||||
JNIEXPORT void OLM_SESSION_FUNC_DEF(initInboundSessionFromIdKeyJni)(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jbyteArray aTheirIdentityKey, jbyteArray aOneTimeKeyMsg);
|
|
||||||
|
|
||||||
// match inbound sessions: based on PRE KEY message
|
|
||||||
JNIEXPORT jboolean OLM_SESSION_FUNC_DEF(matchesInboundSessionJni)(JNIEnv *env, jobject thiz, jbyteArray aOneTimeKeyMsg);
|
|
||||||
JNIEXPORT jboolean OLM_SESSION_FUNC_DEF(matchesInboundSessionFromIdKeyJni)(JNIEnv *env, jobject thiz, jbyteArray aTheirIdentityKey, jbyteArray aOneTimeKeyMsg);
|
|
||||||
|
|
||||||
// encrypt/decrypt
|
|
||||||
JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobject thiz, jbyteArray aClearMsg, jobject aEncryptedMsg);
|
|
||||||
JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(decryptMessageJni)(JNIEnv *env, jobject thiz, jobject aEncryptedMsg);
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(getSessionIdentifierJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(olmSessionDescribeJni)(JNIEnv *env, jobject thiz);
|
|
||||||
|
|
||||||
// serialization
|
|
||||||
JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(serializeJni)(JNIEnv *env, jobject thiz, jbyteArray aKey);
|
|
||||||
JNIEXPORT jlong OLM_SESSION_FUNC_DEF(deserializeJni)(JNIEnv *env, jobject thiz, jbyteArray aSerializedData, jbyteArray aKey);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,236 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "olm_utility.h"
|
|
||||||
|
|
||||||
using namespace AndroidOlmSdk;
|
|
||||||
|
|
||||||
OlmUtility* initializeUtilityMemory()
|
|
||||||
{
|
|
||||||
size_t utilitySize = olm_utility_size();
|
|
||||||
OlmUtility* utilityPtr = (OlmUtility*)malloc(utilitySize);
|
|
||||||
|
|
||||||
if (utilityPtr)
|
|
||||||
{
|
|
||||||
utilityPtr = olm_utility(utilityPtr);
|
|
||||||
LOGD("## initializeUtilityMemory(): success - OLM utility size=%lu",static_cast<long unsigned int>(utilitySize));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGE("## initializeUtilityMemory(): failure - OOM");
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilityPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jlong OLM_UTILITY_FUNC_DEF(createUtilityJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
OlmUtility* utilityPtr = initializeUtilityMemory();
|
|
||||||
|
|
||||||
LOGD("## createUtilityJni(): IN");
|
|
||||||
|
|
||||||
// init account memory allocation
|
|
||||||
if (!utilityPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## createUtilityJni(): failure - init OOM");
|
|
||||||
env->ThrowNew(env->FindClass("java/lang/Exception"), "init OOM");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD(" ## createUtilityJni(): success");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (jlong)(intptr_t)utilityPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
JNIEXPORT void OLM_UTILITY_FUNC_DEF(releaseUtilityJni)(JNIEnv *env, jobject thiz)
|
|
||||||
{
|
|
||||||
OlmUtility* utilityPtr = getUtilityInstanceId(env, thiz);
|
|
||||||
|
|
||||||
LOGD("## releaseUtilityJni(): IN");
|
|
||||||
|
|
||||||
if (!utilityPtr)
|
|
||||||
{
|
|
||||||
LOGE("## releaseUtilityJni(): failure - utility ptr=NULL");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
olm_clear_utility(utilityPtr);
|
|
||||||
free(utilityPtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify an ed25519 signature.
|
|
||||||
* @param aSignature the base64-encoded message signature to be checked.
|
|
||||||
* @param aKey the ed25519 key (fingerprint key)
|
|
||||||
* @param aMessage the message which was signed
|
|
||||||
* @return 0 if validation succeed, an error message string if operation failed
|
|
||||||
*/
|
|
||||||
JNIEXPORT jstring OLM_UTILITY_FUNC_DEF(verifyEd25519SignatureJni)(JNIEnv *env, jobject thiz, jbyteArray aSignatureBuffer, jbyteArray aKeyBuffer, jbyteArray aMessageBuffer)
|
|
||||||
{
|
|
||||||
jstring errorMessageRetValue = 0;
|
|
||||||
OlmUtility* utilityPtr = getUtilityInstanceId(env, thiz);
|
|
||||||
jbyte* signaturePtr = NULL;
|
|
||||||
jbyte* keyPtr = NULL;
|
|
||||||
jbyte* messagePtr = NULL;
|
|
||||||
jboolean messageWasCopied = JNI_FALSE;
|
|
||||||
|
|
||||||
LOGD("## verifyEd25519SignatureJni(): IN");
|
|
||||||
|
|
||||||
if (!utilityPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## verifyEd25519SignatureJni(): failure - invalid utility ptr=NULL");
|
|
||||||
}
|
|
||||||
else if (!aSignatureBuffer || !aKeyBuffer || !aMessageBuffer)
|
|
||||||
{
|
|
||||||
LOGE(" ## verifyEd25519SignatureJni(): failure - invalid input parameters ");
|
|
||||||
}
|
|
||||||
else if (!(signaturePtr = env->GetByteArrayElements(aSignatureBuffer, 0)))
|
|
||||||
{
|
|
||||||
LOGE(" ## verifyEd25519SignatureJni(): failure - signature JNI allocation OOM");
|
|
||||||
}
|
|
||||||
else if (!(keyPtr = env->GetByteArrayElements(aKeyBuffer, 0)))
|
|
||||||
{
|
|
||||||
LOGE(" ## verifyEd25519SignatureJni(): failure - key JNI allocation OOM");
|
|
||||||
}
|
|
||||||
else if (!(messagePtr = env->GetByteArrayElements(aMessageBuffer, &messageWasCopied)))
|
|
||||||
{
|
|
||||||
LOGE(" ## verifyEd25519SignatureJni(): failure - message JNI allocation OOM");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t signatureLength = (size_t)env->GetArrayLength(aSignatureBuffer);
|
|
||||||
size_t keyLength = (size_t)env->GetArrayLength(aKeyBuffer);
|
|
||||||
size_t messageLength = (size_t)env->GetArrayLength(aMessageBuffer);
|
|
||||||
LOGD(" ## verifyEd25519SignatureJni(): signatureLength=%lu keyLength=%lu messageLength=%lu",static_cast<long unsigned int>(signatureLength),static_cast<long unsigned int>(keyLength),static_cast<long unsigned int>(messageLength));
|
|
||||||
LOGD(" ## verifyEd25519SignatureJni(): key=%.*s", static_cast<int>(keyLength), keyPtr);
|
|
||||||
|
|
||||||
size_t result = olm_ed25519_verify(utilityPtr,
|
|
||||||
(void const *)keyPtr,
|
|
||||||
keyLength,
|
|
||||||
(void const *)messagePtr,
|
|
||||||
messageLength,
|
|
||||||
(void*)signaturePtr,
|
|
||||||
signatureLength);
|
|
||||||
if (result == olm_error()) {
|
|
||||||
const char *errorMsgPtr = olm_utility_last_error(utilityPtr);
|
|
||||||
errorMessageRetValue = env->NewStringUTF(errorMsgPtr);
|
|
||||||
LOGE("## verifyEd25519SignatureJni(): failure - olm_ed25519_verify Msg=%s",errorMsgPtr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD("## verifyEd25519SignatureJni(): success - result=%lu", static_cast<long unsigned int>(result));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// free alloc
|
|
||||||
if (signaturePtr)
|
|
||||||
{
|
|
||||||
env->ReleaseByteArrayElements(aSignatureBuffer, signaturePtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyPtr)
|
|
||||||
{
|
|
||||||
env->ReleaseByteArrayElements(aKeyBuffer, keyPtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messagePtr)
|
|
||||||
{
|
|
||||||
if (messageWasCopied) {
|
|
||||||
memset(messagePtr, 0, (size_t)env->GetArrayLength(aMessageBuffer));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(aMessageBuffer, messagePtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
return errorMessageRetValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute the digest (SHA 256) for the message passed in parameter.<br>
|
|
||||||
* The digest value is the function return value.
|
|
||||||
* An exception is thrown if the operation fails.
|
|
||||||
* @param aMessage the message
|
|
||||||
* @return digest of the message.
|
|
||||||
**/
|
|
||||||
JNIEXPORT jbyteArray OLM_UTILITY_FUNC_DEF(sha256Jni)(JNIEnv *env, jobject thiz, jbyteArray aMessageToHashBuffer)
|
|
||||||
{
|
|
||||||
jbyteArray sha256Ret = 0;
|
|
||||||
|
|
||||||
OlmUtility* utilityPtr = getUtilityInstanceId(env, thiz);
|
|
||||||
jbyte* messagePtr = NULL;
|
|
||||||
jboolean messageWasCopied = JNI_FALSE;
|
|
||||||
|
|
||||||
LOGD("## sha256Jni(): IN");
|
|
||||||
|
|
||||||
if (!utilityPtr)
|
|
||||||
{
|
|
||||||
LOGE(" ## sha256Jni(): failure - invalid utility ptr=NULL");
|
|
||||||
}
|
|
||||||
else if(!aMessageToHashBuffer)
|
|
||||||
{
|
|
||||||
LOGE(" ## sha256Jni(): failure - invalid message parameters ");
|
|
||||||
}
|
|
||||||
else if(!(messagePtr = env->GetByteArrayElements(aMessageToHashBuffer, &messageWasCopied)))
|
|
||||||
{
|
|
||||||
LOGE(" ## sha256Jni(): failure - message JNI allocation OOM");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// get lengths
|
|
||||||
size_t messageLength = (size_t)env->GetArrayLength(aMessageToHashBuffer);
|
|
||||||
size_t hashLength = olm_sha256_length(utilityPtr);
|
|
||||||
void* hashValuePtr = malloc((hashLength)*sizeof(uint8_t));
|
|
||||||
|
|
||||||
if (!hashValuePtr)
|
|
||||||
{
|
|
||||||
LOGE("## sha256Jni(): failure - hash value allocation OOM");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t result = olm_sha256(utilityPtr,
|
|
||||||
(void const *)messagePtr,
|
|
||||||
messageLength,
|
|
||||||
(void *)hashValuePtr,
|
|
||||||
hashLength);
|
|
||||||
if (result == olm_error())
|
|
||||||
{
|
|
||||||
LOGE("## sha256Jni(): failure - hash creation Msg=%s",(const char *)olm_utility_last_error(utilityPtr));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGD("## sha256Jni(): success - result=%lu hashValue=%.*s",static_cast<long unsigned int>(result), static_cast<int>(result), (char*)hashValuePtr);
|
|
||||||
sha256Ret = env->NewByteArray(result);
|
|
||||||
env->SetByteArrayRegion(sha256Ret, 0 , result, (jbyte*)hashValuePtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(hashValuePtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messagePtr)
|
|
||||||
{
|
|
||||||
if (messageWasCopied) {
|
|
||||||
memset(messagePtr, 0, (size_t)env->GetArrayLength(aMessageToHashBuffer));
|
|
||||||
}
|
|
||||||
env->ReleaseByteArrayElements(aMessageToHashBuffer, messagePtr, JNI_ABORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sha256Ret;
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 OpenMarket Ltd
|
|
||||||
* Copyright 2016 Vector Creations Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _OLMUTILITY_H
|
|
||||||
#define _OLMUTILITY_H
|
|
||||||
|
|
||||||
#include "olm_jni.h"
|
|
||||||
#include "olm/olm.h"
|
|
||||||
|
|
||||||
#define OLM_UTILITY_FUNC_DEF(func_name) FUNC_DEF(OlmUtility,func_name)
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
JNIEXPORT jlong OLM_UTILITY_FUNC_DEF(createUtilityJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT void OLM_UTILITY_FUNC_DEF(releaseUtilityJni)(JNIEnv *env, jobject thiz);
|
|
||||||
JNIEXPORT jstring OLM_UTILITY_FUNC_DEF(verifyEd25519SignatureJni)(JNIEnv *env, jobject thiz, jbyteArray aSignature, jbyteArray aKey, jbyteArray aMessage);
|
|
||||||
JNIEXPORT jbyteArray OLM_UTILITY_FUNC_DEF(sha256Jni)(JNIEnv *env, jobject thiz, jbyteArray aMessageToHash);
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1 +0,0 @@
|
||||||
include ':olm-sdk'
|
|
|
@ -1,11 +0,0 @@
|
||||||
get_filename_component(Olm_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
|
||||||
include(CMakeFindDependencyMacro)
|
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH ${Olm_CMAKE_DIR})
|
|
||||||
list(REMOVE_AT CMAKE_MODULE_PATH -1)
|
|
||||||
|
|
||||||
if(NOT TARGET Olm::olm)
|
|
||||||
include("${Olm_CMAKE_DIR}/OlmTargets.cmake")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(Olm_LIBRARIES Olm::olm)
|
|
|
@ -1,4 +0,0 @@
|
||||||
|
|
||||||
MAJOR := 3
|
|
||||||
MINOR := 2
|
|
||||||
PATCH := 16
|
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 48 KiB |
|
@ -1,39 +0,0 @@
|
||||||
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]//""
|
|
Binary file not shown.
File diff suppressed because it is too large
Load diff
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 34 KiB |
374
docs/megolm.md
374
docs/megolm.md
|
@ -1,374 +0,0 @@
|
||||||
# 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
Normal file
362
docs/megolm.rst
Normal file
|
@ -0,0 +1,362 @@
|
||||||
|
.. 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
331
docs/olm.md
|
@ -1,331 +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 $`\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
|
|
357
docs/olm.rst
Normal file
357
docs/olm.rst
Normal file
|
@ -0,0 +1,357 @@
|
||||||
|
Olm: A Cryptographic Ratchet
|
||||||
|
============================
|
||||||
|
|
||||||
|
An implementation of the double cryptographic ratchet described by
|
||||||
|
https://github.com/trevp/double_ratchet/wiki.
|
||||||
|
|
||||||
|
Notation
|
||||||
|
--------
|
||||||
|
|
||||||
|
This document uses :math:`\parallel` to represent string concatenation. When
|
||||||
|
:math:`\parallel` appears on the right hand side of an :math:`=` it means that
|
||||||
|
the inputs are concatenated. When :math:`\parallel` appears on the left hand
|
||||||
|
side of an :math:`=` it means that the output is split.
|
||||||
|
|
||||||
|
When this document uses :math:`ECDH\left(K_A,\,K_B\right)` it means that each
|
||||||
|
party computes a Diffie-Hellman agreement using their private key and the
|
||||||
|
remote party's public key.
|
||||||
|
So party :math:`A` computes :math:`ECDH\left(K_B_public,\,K_A_private\right)`
|
||||||
|
and party :math:`B` computes :math:`ECDH\left(K_A_public,\,K_B_private\right)`.
|
||||||
|
|
||||||
|
Where this document uses :math:`HKDF\left(salt,\,IKM,\,info,\,L\right)` it
|
||||||
|
refers to the `HMAC-based key derivation function`_ with a salt value of
|
||||||
|
:math:`salt`, input key material of :math:`IKM`, context string :math:`info`,
|
||||||
|
and output keying material length of :math:`L` bytes.
|
||||||
|
|
||||||
|
The Olm Algorithm
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Initial setup
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The setup takes four Curve25519_ inputs: Identity keys for Alice and Bob,
|
||||||
|
:math:`I_A` and :math:`I_B`, and 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-i}`. The next
|
||||||
|
chain key, :math:`C_{i,j}`, is the HMAC-SHA-256_ of ``"\x02"`` using the
|
||||||
|
previous chain key as the key.
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
\begin{align}
|
||||||
|
C_{i,j}&=HMAC\left(C_{i,j-1},\,\text{"\textbackslash x02"}\right)
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
Creating a message key
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Creating a message key takes the current chain key, :math:`C_{i,j}`. The
|
||||||
|
message key, :math:`M_{i,j}`, is the HMAC-SHA-256_ of ``"\x01"`` using the
|
||||||
|
current chain key as the key. The message keys where :math:`i` is even are used
|
||||||
|
by Alice to encrypt messages. The message keys where :math:`i` is odd are used
|
||||||
|
by Bob to encrypt messages.
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
\begin{align}
|
||||||
|
M_{i,j}&=HMAC\left(C_{i,j},\,\text{"\textbackslash x01"}\right)
|
||||||
|
\end{align}
|
||||||
|
|
||||||
|
|
||||||
|
The Olm Protocol
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Creating an outbound session
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Bob publishes the public parts of his identity key, :math:`I_B`, and some
|
||||||
|
single-use one-time keys :math:`E_B`.
|
||||||
|
|
||||||
|
Alice downloads Bob's identity key, :math:`I_B`, and a one-time key,
|
||||||
|
:math:`E_B`. She generates a new single-use key, :math:`E_A`, and computes a
|
||||||
|
root key, :math:`R_0`, and a chain key :math:`C_{0,0}`. She also generates a
|
||||||
|
new ratchet key :math:`T_0`.
|
||||||
|
|
||||||
|
Sending the first pre-key messages
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Alice computes a message key, :math:`M_{0,j}`, and a new chain key,
|
||||||
|
:math:`C_{0,j+1}`, using the current chain key. She replaces the current chain
|
||||||
|
key with the new one.
|
||||||
|
|
||||||
|
Alice encrypts her plain-text with the message key, :math:`M_{0,j}`, using an
|
||||||
|
authenticated encryption scheme (see below) to get a cipher-text,
|
||||||
|
:math:`X_{0,j}`.
|
||||||
|
|
||||||
|
She then sends the following to Bob:
|
||||||
|
* The public part of her identity key, :math:`I_A`
|
||||||
|
* The public part of her single-use key, :math:`E_A`
|
||||||
|
* The public part of Bob's single-use key, :math:`E_B`
|
||||||
|
* The current chain index, :math:`j`
|
||||||
|
* The public part of her ratchet key, :math:`T_0`
|
||||||
|
* The cipher-text, :math:`X_{0,j}`
|
||||||
|
|
||||||
|
Alice will continue to send pre-key messages until she receives a message from
|
||||||
|
Bob.
|
||||||
|
|
||||||
|
Creating an inbound session from a pre-key message
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Bob receives a pre-key message as above.
|
||||||
|
|
||||||
|
Bob looks up the private part of his single-use key, :math:`E_B`. He can now
|
||||||
|
compute the root key, :math:`R_0`, and the chain key, :math:`C_{0,0}`, from
|
||||||
|
:math:`I_A`, :math:`E_A`, :math:`I_B`, and :math:`E_B`.
|
||||||
|
|
||||||
|
Bob then advances the chain key :math:`j` times, to compute the chain key used
|
||||||
|
by the message, :math:`C_{0,j}`. He now creates the
|
||||||
|
message key, :math:`M_{0,j}`, and attempts to decrypt the cipher-text,
|
||||||
|
:math:`X_{0,j}`. If the cipher-text's authentication is correct then Bob can
|
||||||
|
discard the private part of his single-use one-time key, :math:`E_B`.
|
||||||
|
|
||||||
|
Bob stores Alice's initial ratchet key, :math:`T_0`, until he wants to
|
||||||
|
send a message.
|
||||||
|
|
||||||
|
Sending normal messages
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Once a message has been received from the other side, a session is considered
|
||||||
|
established, and a more compact form is used.
|
||||||
|
|
||||||
|
To send a message, the user checks if they have a sender chain key,
|
||||||
|
:math:`C_{i,j}`. Alice uses chain keys where :math:`i` is even. Bob uses chain
|
||||||
|
keys where :math:`i` is odd. If the chain key doesn't exist then a new ratchet
|
||||||
|
key :math:`T_i` is generated and a new root key :math:`R_i` and chain key
|
||||||
|
:math:`C_{i,0}` are computed using :math:`R_{i-1}`, :math:`T_{i-1}` and
|
||||||
|
:math:`T_i`.
|
||||||
|
|
||||||
|
A message key,
|
||||||
|
:math:`M_{i,j}` is computed from the current chain key, :math:`C_{i,j}`, and
|
||||||
|
the chain key is replaced with the next chain key, :math:`C_{i,j+1}`. The
|
||||||
|
plain-text is encrypted with :math:`M_{i,j}`, using an authenticated encryption
|
||||||
|
scheme (see below) to get a cipher-text, :math:`X_{i,j}`.
|
||||||
|
|
||||||
|
The user then sends the following to the recipient:
|
||||||
|
* The current chain index, :math:`j`
|
||||||
|
* The public part of the current ratchet key, :math:`T_i`
|
||||||
|
* The cipher-text, :math:`X_{i,j}`
|
||||||
|
|
||||||
|
Receiving messages
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The user receives a message as above with the sender's current chain index, :math:`j`,
|
||||||
|
the sender's ratchet key, :math:`T_i`, and the cipher-text, :math:`X_{i,j}`.
|
||||||
|
|
||||||
|
The user checks if they have a receiver chain with the correct
|
||||||
|
:math:`i` by comparing the ratchet key, :math:`T_i`. If the chain doesn't exist
|
||||||
|
then they compute a new root key, :math:`R_i`, and a new receiver chain, with
|
||||||
|
chain key :math:`C_{i,0}`, using :math:`R_{i-1}`, :math:`T_{i-1}` and
|
||||||
|
:math:`T_i`.
|
||||||
|
|
||||||
|
If the :math:`j` of the message is less than
|
||||||
|
the current chain index on the receiver then the message may only be decrypted
|
||||||
|
if the receiver has stored a copy of the message key :math:`M_{i,j}`. Otherwise
|
||||||
|
the receiver computes the chain key, :math:`C_{i,j}`. The receiver computes the
|
||||||
|
message key, :math:`M_{i,j}`, from the chain key and attempts to decrypt the
|
||||||
|
cipher-text, :math:`X_{i,j}`.
|
||||||
|
|
||||||
|
If the decryption succeeds the receiver updates the chain key for :math:`T_i`
|
||||||
|
with :math:`C_{i,j+1}` and stores the message keys that were skipped in the
|
||||||
|
process so that they can decode out of order messages. If the receiver created
|
||||||
|
a new receiver chain then they discard their current sender chain so that
|
||||||
|
they will create a new chain when they next send a message.
|
||||||
|
|
||||||
|
The Olm Message Format
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Olm uses two types of messages. The underlying transport protocol must provide
|
||||||
|
a means for recipients to distinguish between them.
|
||||||
|
|
||||||
|
Normal Messages
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Olm messages start with a one byte version followed by a variable length
|
||||||
|
payload followed by a fixed length message authentication code.
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
+--------------+------------------------------------+-----------+
|
||||||
|
| Version Byte | Payload Bytes | MAC Bytes |
|
||||||
|
+--------------+------------------------------------+-----------+
|
||||||
|
|
||||||
|
The version byte is ``"\x03"``.
|
||||||
|
|
||||||
|
The payload consists of key-value pairs where the keys are integers and the
|
||||||
|
values are integers and strings. The keys are encoded as a variable length
|
||||||
|
integer tag where the 3 lowest bits indicates the type of the value:
|
||||||
|
0 for integers, 2 for strings. If the value is an integer then the tag is
|
||||||
|
followed by the value encoded as a variable length integer. If the value is
|
||||||
|
a string then the tag is followed by the length of the string encoded as
|
||||||
|
a variable length integer followed by the string itself.
|
||||||
|
|
||||||
|
Olm uses a variable length encoding for integers. Each integer is encoded as a
|
||||||
|
sequence of bytes with the high bit set followed by a byte with the high bit
|
||||||
|
clear. The seven low bits of each byte store the bits of the integer. The least
|
||||||
|
significant bits are stored in the first byte.
|
||||||
|
|
||||||
|
=========== ===== ======== ================================================
|
||||||
|
Name Tag Type Meaning
|
||||||
|
=========== ===== ======== ================================================
|
||||||
|
Ratchet-Key 0x0A String The public part of the ratchet key, :math:`T_{i}`,
|
||||||
|
of the message
|
||||||
|
Chain-Index 0x10 Integer The chain index, :math:`j`, of the message
|
||||||
|
Cipher-Text 0x22 String The cipher-text, :math:`X_{i,j}`, of the message
|
||||||
|
=========== ===== ======== ================================================
|
||||||
|
|
||||||
|
The length of the MAC is determined by the authenticated encryption algorithm
|
||||||
|
being used. (Olm version 1 uses HMAC-SHA-256, truncated to 8 bytes). The
|
||||||
|
MAC protects all of the bytes preceding the MAC.
|
||||||
|
|
||||||
|
Pre-Key Messages
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Olm pre-key messages start with a one byte version followed by a variable
|
||||||
|
length payload.
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
+--------------+------------------------------------+
|
||||||
|
| Version Byte | Payload Bytes |
|
||||||
|
+--------------+------------------------------------+
|
||||||
|
|
||||||
|
The version byte is ``"\x03"``.
|
||||||
|
|
||||||
|
The payload uses the same key-value format as for normal messages.
|
||||||
|
|
||||||
|
============ ===== ======== ================================================
|
||||||
|
Name Tag Type Meaning
|
||||||
|
============ ===== ======== ================================================
|
||||||
|
One-Time-Key 0x0A String The public part of Bob's single-use key,
|
||||||
|
:math:`E_b`.
|
||||||
|
Base-Key 0x12 String The public part of Alice's single-use key,
|
||||||
|
:math:`E_a`.
|
||||||
|
Identity-Key 0x1A String The public part of Alice's identity key,
|
||||||
|
:math:`I_a`.
|
||||||
|
Message 0x22 String An embedded Olm message with its own version and
|
||||||
|
MAC.
|
||||||
|
============ ===== ======== ================================================
|
||||||
|
|
||||||
|
Olm Authenticated Encryption
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
Version 1
|
||||||
|
~~~~~~~~~
|
||||||
|
|
||||||
|
Version 1 of Olm uses AES-256_ in CBC_ mode with `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 mark at matrix.org.
|
||||||
|
|
||||||
|
Acknowledgements
|
||||||
|
----------------
|
||||||
|
|
||||||
|
The ratchet that Olm implements was designed by Trevor Perrin and Moxie
|
||||||
|
Marlinspike - details at https://github.com/trevp/double_ratchet/wiki. Olm is
|
||||||
|
an entirely new implementation written by the Matrix.org team.
|
||||||
|
|
||||||
|
.. _`Curve25519`: http://cr.yp.to/ecdh.html
|
||||||
|
.. _`Triple Diffie-Hellman`: https://whispersystems.org/blog/simplifying-otr-deniability/
|
||||||
|
.. _`HMAC-based key derivation function`: https://tools.ietf.org/html/rfc5869
|
||||||
|
.. _`HKDF-SHA-256`: https://tools.ietf.org/html/rfc5869
|
||||||
|
.. _`HMAC-SHA-256`: https://tools.ietf.org/html/rfc2104
|
||||||
|
.. _`SHA-256`: https://tools.ietf.org/html/rfc6234
|
||||||
|
.. _`AES-256`: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
|
||||||
|
.. _`CBC`: http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
|
||||||
|
.. _`PKCS#7`: https://tools.ietf.org/html/rfc2315
|
|
@ -1,4 +1,20 @@
|
||||||
# Signature keys and user identity in libolm
|
.. 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.
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
@ -7,13 +23,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][] protocol, it is sufficient to compare the public
|
In the basic `Olm <olm.html>`_ 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, $`S`$,
|
complete the triple-DH calculation to compute the shared secret, :math:`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.
|
||||||
|
@ -25,7 +41,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.
|
||||||
|
@ -34,7 +50,8 @@ 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
|
||||||
|
@ -43,21 +60,19 @@ 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, $`E_B`$, but Eve
|
an Olm session with Bob. Bob uploads his one-time keys, :math:`E_B`, but Eve
|
||||||
replaces them with ones she controls, $`E_E`$. Alice downloads one of the
|
replaces them with ones she controls, :math:`E_E`. Alice downloads one of the
|
||||||
compromised keys, and sends a pre-key message using a shared secret $`S`$,
|
compromised keys, and sends a pre-key message using a shared secret :math:`S`,
|
||||||
where:
|
where:
|
||||||
|
|
||||||
```math
|
.. math::
|
||||||
S = \operatorname{ECDH}\left(I_A,E_E\right)\;\parallel\;
|
S = ECDH\left(I_A,\,E_E\right)\;\parallel\;ECDH\left(E_A,\,I_B\right)\;
|
||||||
\operatorname{ECDH}\left(E_A,I_B\right)\;\parallel\;
|
\parallel\;ECDH\left(E_A,\,E_E\right)
|
||||||
\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 $`E_A`$ nor $`I_B`$, so cannot calculate
|
either :math:`E_A` nor :math:`I_B`, so cannot calculate
|
||||||
$`ECDH\left(E_A,I_B\right)`$. However, suppose she later compromises
|
:math:`ECDH\left(E_A,\,I_B\right)`. However, suppose she later compromises
|
||||||
Bob's identity key $`I_B`$. This would give her the ability to decrypt any
|
Bob's identity key :math:`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
|
||||||
|
@ -66,39 +81,38 @@ 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 = \operatorname{ECDH}\left(I_A,E_B\right)\;\parallel\;
|
S = ECDH\left(I_A,\,E_B\right)\;\parallel\;ECDH\left(E_A,\,I_B\right)\;
|
||||||
\operatorname{ECDH}\left(E_A,I_B\right)\;\parallel\;
|
\parallel\;ECDH\left(E_A,\,E_B\right)
|
||||||
\operatorname{ECDH}\left(E_A,E_B\right)
|
|
||||||
```
|
|
||||||
|
|
||||||
If keys are unsigned, a forger can make up values of $`E_A`$ and
|
If keys are unsigned, a forger can make up values of :math:`E_A` and
|
||||||
$`E_B`$, and construct a transcript of a conversation which looks like it
|
:math:`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
|
||||||
participation in any conversation even if they are both forced to divulge their
|
partition 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 $`E_B`$ is signed, it is no longer possible to construct arbitrary
|
If :math:`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 $`\operatorname{ECDH}\left(I_A,E_B\right)`$ requires
|
because the ability to calculate :math:`ECDH\left(I_A,\,E_B\right)` requires
|
||||||
knowledge of the private parts of either $`I_A`$ (proving Alice's
|
knowledge of the private parts of either :math:`I_A` (proving Alice's
|
||||||
involvement) or $`E_B`$ (proving Bob's involvement, via the
|
involvement) or :math:`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 richard 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
|
|
18
exports.py
18
exports.py
|
@ -1,18 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import re
|
|
||||||
import json
|
|
||||||
|
|
||||||
expr = re.compile(r"(_*olm_[^( ]*)\(")
|
|
||||||
|
|
||||||
exports = {'_free', '_malloc'}
|
|
||||||
|
|
||||||
for f in sys.argv[1:]:
|
|
||||||
with open(f) as fp:
|
|
||||||
for line in fp:
|
|
||||||
matches = expr.search(line)
|
|
||||||
if matches is not None:
|
|
||||||
exports.add('_%s' % (matches.group(1),))
|
|
||||||
|
|
||||||
json.dump(sorted(exports), sys.stdout)
|
|
60
flake.lock
60
flake.lock
|
@ -1,60 +0,0 @@
|
||||||
{
|
|
||||||
"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
40
flake.nix
|
@ -1,40 +0,0 @@
|
||||||
{
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
));
|
|
||||||
}
|
|
|
@ -19,7 +19,8 @@ Usage notes:
|
||||||
make fuzzers
|
make fuzzers
|
||||||
|
|
||||||
3. Some of the tests (eg ``fuzz_decrypt`` and ``fuzz_group_decrypt``) require a
|
3. Some of the tests (eg ``fuzz_decrypt`` and ``fuzz_group_decrypt``) require a
|
||||||
session file. You can create one by pickling an Olm session.
|
session file. You can use the ones generated by the python test script
|
||||||
|
(``python/test.sh``).
|
||||||
|
|
||||||
4. Make some work directories:
|
4. Make some work directories:
|
||||||
|
|
|
@ -11,6 +11,4 @@ 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,10 +3,11 @@
|
||||||
#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";
|
||||||
(void)write(STDERR_FILENO, message, strlen(message));
|
ignored = write(STDERR_FILENO, message, strlen(message));
|
||||||
exit(3);
|
exit(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,12 +59,7 @@ int main(int argc, const char *argv[]) {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
(void)write(STDOUT_FILENO, plaintext, length);
|
ignored = write(STDOUT_FILENO, plaintext, length);
|
||||||
(void)write(STDOUT_FILENO, "\n", 1);
|
ignored = write(STDOUT_FILENO, "\n", 1);
|
||||||
|
return ignored;
|
||||||
free(session_buffer);
|
|
||||||
free(message_buffer);
|
|
||||||
free(tmp_buffer);
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
}
|
73
fuzzers/fuzz_group_decrypt.cpp
Normal file
73
fuzzers/fuzz_group_decrypt.cpp
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
#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;
|
||||||
|
}
|
14
fuzzers/fuzz_unpickle_account.cpp
Normal file
14
fuzzers/fuzz_unpickle_account.cpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#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;
|
||||||
|
}
|
|
@ -11,6 +11,4 @@ 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;
|
|
||||||
}
|
}
|
|
@ -15,43 +15,28 @@ ssize_t read_file(
|
||||||
uint8_t **buffer
|
uint8_t **buffer
|
||||||
) {
|
) {
|
||||||
size_t buffer_size = 4096;
|
size_t buffer_size = 4096;
|
||||||
size_t buffer_pos = 0;
|
|
||||||
uint8_t * current_buffer = (uint8_t *) malloc(buffer_size);
|
uint8_t * current_buffer = (uint8_t *) malloc(buffer_size);
|
||||||
if (!current_buffer) return -1;
|
if (current_buffer == NULL) return -1;
|
||||||
|
size_t buffer_pos = 0;
|
||||||
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) {
|
||||||
// Nothing more left to read. We downsize the buffer to fit the
|
uint8_t * return_buffer = (uint8_t *) realloc(current_buffer, buffer_pos);
|
||||||
// data exactly, unless no data was read at all, in which case we
|
if (return_buffer == NULL) break;
|
||||||
// skip the downsizing.
|
*buffer = return_buffer;
|
||||||
|
|
||||||
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) break;
|
if (new_buffer == NULL) break;
|
||||||
current_buffer = new_buffer;
|
current_buffer = new_buffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(current_buffer);
|
free(current_buffer);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -77,12 +62,13 @@ size_t check_error(
|
||||||
) {
|
) {
|
||||||
if (value == olm_error()) {
|
if (value == olm_error()) {
|
||||||
const char * olm_message = f(object);
|
const char * olm_message = f(object);
|
||||||
(void)write(STDERR_FILENO, message, strlen(message));
|
ssize_t ignored;
|
||||||
(void)write(STDERR_FILENO, ": ", 2);
|
ignored = write(STDERR_FILENO, message, strlen(message));
|
||||||
(void)write(STDERR_FILENO, olm_message, strlen(olm_message));
|
ignored = write(STDERR_FILENO, ": ", 2);
|
||||||
(void)write(STDERR_FILENO, "\n", 1);
|
ignored = write(STDERR_FILENO, olm_message, strlen(olm_message));
|
||||||
|
ignored = write(STDERR_FILENO, "\n", 1);
|
||||||
exit(2);
|
exit(2);
|
||||||
|
return ignored;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
|
@ -1,10 +0,0 @@
|
||||||
# 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.
|
|
|
@ -1,102 +0,0 @@
|
||||||
#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;
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
#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;
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
#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;
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
#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);
|
|
||||||
}
|
|
|
@ -1,118 +0,0 @@
|
||||||
#!/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
|
|
|
@ -1,17 +0,0 @@
|
||||||
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
|
|
|
@ -1,4 +0,0 @@
|
||||||
module libolm {
|
|
||||||
header "olm/olm.h"
|
|
||||||
export *
|
|
||||||
}
|
|
|
@ -43,14 +43,11 @@ 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() const;
|
std::size_t new_account_random_length();
|
||||||
|
|
||||||
/** 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 */
|
||||||
|
@ -59,7 +56,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() const;
|
std::size_t get_identity_json_length();
|
||||||
|
|
||||||
/** 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:
|
||||||
|
@ -78,7 +75,7 @@ struct Account {
|
||||||
/**
|
/**
|
||||||
* The length of an ed25519 signature in bytes.
|
* The length of an ed25519 signature in bytes.
|
||||||
*/
|
*/
|
||||||
std::size_t signature_length() const;
|
std::size_t signature_length();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signs a message with the ed25519 key for this account.
|
* Signs a message with the ed25519 key for this account.
|
||||||
|
@ -89,7 +86,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() const;
|
std::size_t get_one_time_keys_json_length();
|
||||||
|
|
||||||
/** 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:
|
||||||
*
|
*
|
||||||
|
@ -107,20 +104,18 @@ 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 and the current fallback key as
|
/** Mark the current list of one_time_keys as being published. They
|
||||||
* being published. The current one time keys will no longer be returned by
|
* will no longer be returned by get_one_time_keys_json_length(). */
|
||||||
* 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() const;
|
std::size_t max_number_of_one_time_keys();
|
||||||
|
|
||||||
/** 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
|
||||||
|
@ -131,49 +126,6 @@ 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,10 +22,6 @@
|
||||||
#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
|
||||||
|
@ -34,7 +30,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.
|
||||||
*/
|
*/
|
||||||
OLM_EXPORT size_t _olm_encode_base64_length(
|
size_t _olm_encode_base64_length(
|
||||||
size_t input_length
|
size_t input_length
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -46,7 +42,7 @@ OLM_EXPORT size_t _olm_encode_base64_length(
|
||||||
*
|
*
|
||||||
* Returns number of bytes encoded
|
* Returns number of bytes encoded
|
||||||
*/
|
*/
|
||||||
OLM_EXPORT size_t _olm_encode_base64(
|
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
|
||||||
);
|
);
|
||||||
|
@ -55,7 +51,7 @@ OLM_EXPORT 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.
|
||||||
*/
|
*/
|
||||||
OLM_EXPORT size_t _olm_decode_base64_length(
|
size_t _olm_decode_base64_length(
|
||||||
size_t input_length
|
size_t input_length
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -67,7 +63,7 @@ OLM_EXPORT size_t _olm_decode_base64_length(
|
||||||
*
|
*
|
||||||
* Returns number of bytes decoded
|
* Returns number of bytes decoded
|
||||||
*/
|
*/
|
||||||
OLM_EXPORT size_t _olm_decode_base64(
|
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,16 +18,12 @@
|
||||||
#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.
|
||||||
*/
|
*/
|
||||||
OLM_EXPORT std::size_t encode_base64_length(
|
std::size_t encode_base64_length(
|
||||||
std::size_t input_length
|
std::size_t input_length
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -37,7 +33,7 @@ OLM_EXPORT 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.
|
||||||
*/
|
*/
|
||||||
OLM_EXPORT std::uint8_t * encode_base64(
|
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
|
||||||
);
|
);
|
||||||
|
@ -46,7 +42,7 @@ OLM_EXPORT 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.
|
||||||
*/
|
*/
|
||||||
OLM_EXPORT std::size_t decode_base64_length(
|
std::size_t decode_base64_length(
|
||||||
std::size_t input_length
|
std::size_t input_length
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -55,12 +51,8 @@ OLM_EXPORT 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.
|
|
||||||
*/
|
*/
|
||||||
OLM_EXPORT std::size_t decode_base64(
|
std::uint8_t const * 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,10 +19,6 @@
|
||||||
#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
|
||||||
|
@ -115,7 +111,7 @@ struct _olm_cipher_aes_sha_256 {
|
||||||
size_t kdf_info_length;
|
size_t kdf_info_length;
|
||||||
};
|
};
|
||||||
|
|
||||||
OLM_EXPORT extern const struct _olm_cipher_ops _olm_cipher_aes_sha_256_ops;
|
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.
|
||||||
|
@ -127,9 +123,9 @@ OLM_EXPORT extern const struct _olm_cipher_ops _olm_cipher_aes_sha_256_ops;
|
||||||
* struct _olm_cipher *cipher = OLM_CIPHER_BASE(&MY_CIPHER);
|
* struct _olm_cipher *cipher = OLM_CIPHER_BASE(&MY_CIPHER);
|
||||||
*/
|
*/
|
||||||
#define OLM_CIPHER_INIT_AES_SHA_256(KDF_INFO) { \
|
#define OLM_CIPHER_INIT_AES_SHA_256(KDF_INFO) { \
|
||||||
/*.base_cipher = */{ &_olm_cipher_aes_sha_256_ops },\
|
.base_cipher = { &_olm_cipher_aes_sha_256_ops },\
|
||||||
/*.kdf_info = */(uint8_t *)(KDF_INFO), \
|
.kdf_info = (uint8_t *)(KDF_INFO), \
|
||||||
/*.kdf_info_length = */sizeof(KDF_INFO) - 1 \
|
.kdf_info_length = sizeof(KDF_INFO) - 1 \
|
||||||
}
|
}
|
||||||
#define OLM_CIPHER_BASE(CIPHER) \
|
#define OLM_CIPHER_BASE(CIPHER) \
|
||||||
(&((CIPHER)->base_cipher))
|
(&((CIPHER)->base_cipher))
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue