pax_global_header00006660000000000000000000000064131175507350014521gustar00rootroot0000000000000052 comment=4cb522e53cbf746491fd37f1cc058f9d3a633362 olm-2.2.2+git20170526.0fd768e+dfsg/000077500000000000000000000000001311755073500157225ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/.gitignore000066400000000000000000000005471311755073500177200ustar00rootroot00000000000000/build /CHANGELOG.html /docs/megolm.html /docs/olm.html /docs/signing.html /olm-*.tgz /README.html /tracing/README.html # Xcode build/ DerivedData/ *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata/ *.moved-aside *.xcuserstate *.hmap *.ipa *.dSYM.zip *.dSYM Pods/ *.xcworkspaceolm-2.2.2+git20170526.0fd768e+dfsg/CHANGELOG.rst000066400000000000000000000135541311755073500177530ustar00rootroot00000000000000Changes in `2.2.2 `_ =============================================================== Objective-C wrapper: * Fixed type of ``messageIndex`` argument in ``exportSessionAtMessageIndex``. Thanks to Greg Hughes. Changes in `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 `_ =============================================================== 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 `_ =============================================================== 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 `_ =============================================================== This release includes the following changes since 1.3.0: * Fix a buffer bounds check when decoding group messages. * Update ``olm_group_decrypt`` to return the ratchet index for decrypted messages. * Fix ``olm_pickle_account``, ``olm_pickle_session``, ``olm_pickle_inbound_group_session`` and ``olm_pickle_outbound_group_session`` to correctly return the length of the pickled object. * Add a `specification <./docs/megolm.rst>`_ of the Megolm ratchet, and add some information on mitigating unknown key-share attacks to the `Olm specification <./docs/olm.rst>`_. * Add an ``install-headers`` target to the Makefile (and run it when installing the library). (Credit to Emmanuel Gil Peyrot). Changes in `1.3.0 `_ =============================================================== This release updates the group session identifier to avoid collisions. Group sessions are now identified by their ed25519 public key. These changes alter the pickle format of outbound group sessions, attempting to unpickle an outbound group session created with a previous version of olm will give ``OLM_CORRUPTED_PICKLE``. Inbound sessions are unaffected. This release alters the format of group session_key messages to include the ratchet counter. The session_key messages are now self signed with their ed25519 key. No attempt was made to preserve backwards-compatibility. Attempting to send session_keys between old and new versions will give ``OLM_BAD_SESSION_KEY``. Changes in `1.2.0 `_ =============================================================== This release updates the implementation of group session communications, to include Ed25519 signatures on group messages, to ensure that participants in group sessions cannot masquerade as each other. These changes necessitate changes to the pickle format of inbound and outbound group sessions, as well as the session_keys exchanged between them. No attempt has been made to preserve backwards-compatibility: * Attempting to restore old pickles will give ``OLM_CORRUPTED_PICKLE``. * Attempting to send session_keys between old and new versions will give ``OLM_BAD_SESSION_KEY``. * Attempting to send messages between old and new versions will give one of a number of errors. There were also a number of implementation changes made as part of this release, aimed at making the codebase more consistent, and to help with the implementation of the group message signatures. Changes in `1.1.0 `_ =============================================================== This release includes a fix to a bug which caused Ed25519 keypairs to be generated and used insecurely. Any Ed25519 keys generated by libolm 1.0.0 or earlier should be considered compromised. The fix necessitates a change to the format of the OlmAccount pickle; since existing OlmAccounts should in any case be considered compromised (as above), the library refuses to load them, returning OLM_BAD_LEGACY_ACCOUNT_PICKLE. Changes in `1.0.0 `_ =============================================================== This release includes a fix to a bug which had the potential to leak sensitive data to the application: see https://github.com/vector-im/vector-web/issues/1719. Users of pre-1.x.x versions of the Olm library should upgrade. Our thanks to `Dmitry Luyciv `_ for bringing our attention to the bug. Other changes since 0.1.0: * *Experimental* implementation of the primitives for group sessions. This implementation has not yet been used in an application and developers are advised not to rely on its stability. * Replace custom build scripts with a Makefile. * Include the major version number in the soname of libolm.so (credit to Emmanuel Gil Peyrot). olm-2.2.2+git20170526.0fd768e+dfsg/CONTRIBUTING.rst000066400000000000000000000057351311755073500203750ustar00rootroot00000000000000Contributing 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 ``richard@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 ``_). 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 `_, `Docker `_, and many other projects use: the DCO (`Developer Certificate of Origin `_). 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 ...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 :) olm-2.2.2+git20170526.0fd768e+dfsg/LICENSE000066400000000000000000000236761311755073500167450ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS olm-2.2.2+git20170526.0fd768e+dfsg/Makefile000066400000000000000000000175431311755073500173740ustar00rootroot00000000000000#!/usr/bin/make -f include common.mk VERSION := $(MAJOR).$(MINOR).$(PATCH) PREFIX ?= /usr/local BUILD_DIR := build RELEASE_OPTIMIZE_FLAGS ?= -g -O3 DEBUG_OPTIMIZE_FLAGS ?= -g -O0 JS_OPTIMIZE_FLAGS ?= -O3 FUZZING_OPTIMIZE_FLAGS ?= -O3 CC = gcc EMCC = emcc AFL_CC = afl-gcc AFL_CXX = afl-g++ AR = ar RELEASE_TARGET := $(BUILD_DIR)/libolm.so.$(VERSION) STATIC_RELEASE_TARGET := $(BUILD_DIR)/libolm.a DEBUG_TARGET := $(BUILD_DIR)/libolm_debug.so.$(VERSION) JS_TARGET := javascript/olm.js JS_EXPORTED_FUNCTIONS := javascript/exported_functions.json 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) \ lib/crypto-algorithms/sha256.c \ lib/crypto-algorithms/aes.c \ lib/curve25519-donna/curve25519-donna.c FUZZER_SOURCES := $(wildcard fuzzers/fuzz_*.cpp) $(wildcard fuzzers/fuzz_*.c) TEST_SOURCES := $(wildcard tests/test_*.cpp) $(wildcard tests/test_*.c) OBJECTS := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCES))) RELEASE_OBJECTS := $(addprefix $(BUILD_DIR)/release/,$(OBJECTS)) DEBUG_OBJECTS := $(addprefix $(BUILD_DIR)/debug/,$(OBJECTS)) FUZZER_OBJECTS := $(addprefix $(BUILD_DIR)/fuzzers/objects/,$(OBJECTS)) FUZZER_BINARIES := $(addprefix $(BUILD_DIR)/,$(basename $(FUZZER_SOURCES))) FUZZER_DEBUG_BINARIES := $(patsubst $(BUILD_DIR)/fuzzers/fuzz_%,$(BUILD_DIR)/fuzzers/debug_%,$(FUZZER_BINARIES)) TEST_BINARIES := $(patsubst tests/%,$(BUILD_DIR)/tests/%,$(basename $(TEST_SOURCES))) JS_OBJECTS := $(addprefix $(BUILD_DIR)/javascript/,$(OBJECTS)) JS_PRE := $(wildcard javascript/*pre.js) JS_POST := javascript/olm_outbound_group_session.js \ javascript/olm_inbound_group_session.js \ javascript/olm_post.js DOCS := tracing/README.html \ docs/megolm.html \ docs/olm.html \ docs/signing.html \ README.html \ CHANGELOG.html CPPFLAGS += -Iinclude -Ilib \ -DOLMLIB_VERSION_MAJOR=$(MAJOR) -DOLMLIB_VERSION_MINOR=$(MINOR) \ -DOLMLIB_VERSION_PATCH=$(PATCH) # we rely on , which was introduced in C99 CFLAGS += -Wall -Werror -std=c99 -fPIC CXXFLAGS += -Wall -Werror -std=c++11 -fPIC LDFLAGS += -Wall -Werror EMCCFLAGS = --closure 1 --memory-init-file 0 -s NO_FILESYSTEM=1 -s INVOKE_RUN=0 # NO_BROWSER is kept for compatibility with emscripten 1.35.24, but is no # longer needed. EMCCFLAGS += -s NO_BROWSER=1 EMCC.c = $(EMCC) $(CFLAGS) $(CPPFLAGS) -c EMCC.cc = $(EMCC) $(CXXFLAGS) $(CPPFLAGS) -c EMCC_LINK = $(EMCC) $(LDFLAGS) $(EMCCFLAGS) AFL.c = $(AFL_CC) $(CFLAGS) $(CPPFLAGS) -c AFL.cc = $(AFL_CXX) $(CXXFLAGS) $(CPPFLAGS) -c AFL_LINK.c = $(AFL_CC) $(LDFLAGS) $(CFLAGS) $(CPPFLAGS) AFL_LINK.cc = $(AFL_CXX) $(LDFLAGS) $(CXXFLAGS) $(CPPFLAGS) # generate .d files when compiling CPPFLAGS += -MMD ### per-target variables $(RELEASE_OBJECTS): CFLAGS += $(RELEASE_OPTIMIZE_FLAGS) $(RELEASE_OBJECTS): CXXFLAGS += $(RELEASE_OPTIMIZE_FLAGS) $(RELEASE_TARGET): LDFLAGS += $(RELEASE_OPTIMIZE_FLAGS) $(DEBUG_OBJECTS): CFLAGS += $(DEBUG_OPTIMIZE_FLAGS) $(DEBUG_OBJECTS): CXXFLAGS += $(DEBUG_OPTIMIZE_FLAGS) $(DEBUG_TARGET): LDFLAGS += $(DEBUG_OPTIMIZE_FLAGS) $(TEST_BINARIES): CPPFLAGS += -Itests/include $(TEST_BINARIES): LDFLAGS += $(DEBUG_OPTIMIZE_FLAGS) -L$(BUILD_DIR) $(FUZZER_OBJECTS): CFLAGS += $(FUZZER_OPTIMIZE_FLAGS) $(FUZZER_OBJECTS): CXXFLAGS += $(FUZZER_OPTIMIZE_FLAGS) $(FUZZER_BINARIES): CPPFLAGS += -Ifuzzers/include $(FUZZER_BINARIES): LDFLAGS += $(FUZZER_OPTIMIZE_FLAGS) -L$(BUILD_DIR) $(FUZZER_DEBUG_BINARIES): CPPFLAGS += -Ifuzzers/include $(FUZZER_DEBUG_BINARIES): LDFLAGS += $(DEBUG_OPTIMIZE_FLAGS) $(JS_OBJECTS): CFLAGS += $(JS_OPTIMIZE_FLAGS) $(JS_OBJECTS): CXXFLAGS += $(JS_OPTIMIZE_FLAGS) $(JS_TARGET): LDFLAGS += $(JS_OPTIMIZE_FLAGS) ### top-level targets lib: $(RELEASE_TARGET) .PHONY: lib $(RELEASE_TARGET): $(RELEASE_OBJECTS) $(CXX) $(LDFLAGS) --shared -fPIC \ -Wl,-soname,libolm.so.$(MAJOR) \ -Wl,--version-script,version_script.ver \ $(OUTPUT_OPTION) $(RELEASE_OBJECTS) ln -sf libolm.so.$(VERSION) $(BUILD_DIR)/libolm.so.$(MAJOR) debug: $(DEBUG_TARGET) .PHONY: debug $(DEBUG_TARGET): $(DEBUG_OBJECTS) $(CXX) $(LDFLAGS) --shared -fPIC \ -Wl,-soname,libolm_debug.so.$(MAJOR) \ -Wl,--version-script,version_script.ver \ $(OUTPUT_OPTION) $(DEBUG_OBJECTS) ln -sf libolm_debug.so.$(VERSION) $(BUILD_DIR)/libolm_debug.so.$(MAJOR) static: $(STATIC_RELEASE_TARGET) .PHONY: static $(STATIC_RELEASE_TARGET): $(RELEASE_OBJECTS) $(AR) rcs $@ $^ js: $(JS_TARGET) .PHONY: js $(JS_TARGET): $(JS_OBJECTS) $(JS_PRE) $(JS_POST) $(JS_EXPORTED_FUNCTIONS) $(EMCC_LINK) \ $(foreach f,$(JS_PRE),--pre-js $(f)) \ $(foreach f,$(JS_POST),--post-js $(f)) \ -s "EXPORTED_FUNCTIONS=@$(JS_EXPORTED_FUNCTIONS)" \ $(JS_OBJECTS) -o $@ build_tests: $(TEST_BINARIES) test: build_tests for i in $(TEST_BINARIES); do \ echo $$i; \ $$i || exit $$?; \ done fuzzers: $(FUZZER_BINARIES) $(FUZZER_DEBUG_BINARIES) .PHONY: fuzzers $(JS_EXPORTED_FUNCTIONS): $(PUBLIC_HEADERS) perl -MJSON -ne '$$f{"_$$1"}=1 if /(olm_[^( ]*)\(/; END { @f=sort keys %f; print encode_json \@f }' $^ > $@.tmp mv $@.tmp $@ all: test js lib debug doc .PHONY: all install-headers: $(PUBLIC_HEADERS) test -d $(DESTDIR)$(PREFIX)/include/olm || mkdir -p $(DESTDIR)$(PREFIX)/include/olm install -Dm644 $(PUBLIC_HEADERS) $(DESTDIR)$(PREFIX)/include/olm/ .PHONY: install-headers install-debug: debug install-headers test -d $(DESTDIR)$(PREFIX)/lib || mkdir -p $(DESTDIR)$(PREFIX)/lib install -Dm755 $(DEBUG_TARGET) $(DESTDIR)$(PREFIX)/lib/libolm_debug.so.$(VERSION) ln -s libolm_debug.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm_debug.so.$(MAJOR) ln -s libolm_debug.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm_debug.so .PHONY: install-debug install: lib install-headers test -d $(DESTDIR)$(PREFIX)/lib || mkdir -p $(DESTDIR)$(PREFIX)/lib install -Dm755 $(RELEASE_TARGET) $(DESTDIR)$(PREFIX)/lib/libolm.so.$(VERSION) ln -s libolm.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm.so.$(MAJOR) ln -s libolm.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib/libolm.so .PHONY: install clean:; rm -rf $(BUILD_DIR) $(DOCS) .PHONY: clean doc: $(DOCS) .PHONY: doc ### rules for building objects $(BUILD_DIR)/release/%.o: %.c mkdir -p $(dir $@) $(COMPILE.c) $(OUTPUT_OPTION) $< $(BUILD_DIR)/release/%.o: %.cpp mkdir -p $(dir $@) $(COMPILE.cc) $(OUTPUT_OPTION) $< $(BUILD_DIR)/debug/%.o: %.c mkdir -p $(dir $@) $(COMPILE.c) $(OUTPUT_OPTION) $< $(BUILD_DIR)/debug/%.o: %.cpp mkdir -p $(dir $@) $(COMPILE.cc) $(OUTPUT_OPTION) $< $(BUILD_DIR)/javascript/%.o: %.c mkdir -p $(dir $@) $(EMCC.c) $(OUTPUT_OPTION) $< $(BUILD_DIR)/javascript/%.o: %.cpp mkdir -p $(dir $@) $(EMCC.cc) $(OUTPUT_OPTION) $< $(BUILD_DIR)/tests/%: tests/%.c $(DEBUG_OBJECTS) mkdir -p $(dir $@) $(LINK.c) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@ $(BUILD_DIR)/tests/%: tests/%.cpp $(DEBUG_OBJECTS) mkdir -p $(dir $@) $(LINK.cc) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@ $(BUILD_DIR)/fuzzers/objects/%.o: %.c mkdir -p $(dir $@) $(AFL.c) $(OUTPUT_OPTION) $< $(BUILD_DIR)/fuzzers/objects/%.o: %.cpp mkdir -p $(dir $@) $(AFL.cc) $(OUTPUT_OPTION) $< $(BUILD_DIR)/fuzzers/fuzz_%: fuzzers/fuzz_%.c $(FUZZER_OBJECTS) $(AFL_LINK.c) $< $(FUZZER_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@ $(BUILD_DIR)/fuzzers/fuzz_%: fuzzers/fuzz_%.cpp $(FUZZER_OBJECTS) $(AFL_LINK.cc) $< $(FUZZER_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@ $(BUILD_DIR)/fuzzers/debug_%: fuzzers/fuzz_%.c $(DEBUG_OBJECTS) $(LINK.c) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@ $(BUILD_DIR)/fuzzers/debug_%: fuzzers/fuzz_%.cpp $(DEBUG_OBJECTS) $(LINK.cc) $< $(DEBUG_OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@ %.html: %.rst rst2html $< $@ ### dependencies -include $(RELEASE_OBJECTS:.o=.d) -include $(DEBUG_OBJECTS:.o=.d) -include $(JS_OBJECTS:.o=.d) -include $(TEST_BINARIES:=.d) -include $(FUZZER_OBJECTS:.o=.d) -include $(FUZZER_BINARIES:=.d) -include $(FUZZER_DEBUG_BINARIES:=.d) olm-2.2.2+git20170526.0fd768e+dfsg/OLMKit.podspec000066400000000000000000000042601311755073500204020ustar00rootroot00000000000000Pod::Spec.new do |s| # The libolm version MAJOR = 2 MINOR = 2 PATCH = 2 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 = "http://matrix.org/git/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 = "5.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://matrix.org/git/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 olm-2.2.2+git20170526.0fd768e+dfsg/README.rst000066400000000000000000000111031311755073500174050ustar00rootroot00000000000000Olm === 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 ``_. This library also includes an implementation of the Megolm cryptographic ratchet, as specified in ``_. 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 To build the android project for Android bindings, run: .. code:: bash cd android ./gradlew clean assembleRelease To build the Xcode workspace for Objective-C bindings, run: .. code:: bash cd xcode pod install open OLMKit.xcworkspace To build olm as a static library (which still needs libstdc++ dynamically) run: .. code:: bash make static Release process --------------- First: bump version numbers in ``common.mk``, ``javascript/package.json``, ``OLMKit.podspec``, and ``android/olm-sdk/build.gradle`` (``versionCode``, ``versionName`` and ``version``). Also, ensure the changelog is up to date, and that everyting is committed to git. It's probably sensible to do the above on a release branch (``release-vx.y.z`` by convention), and merge back to master once the release is complete. .. code:: bash make clean # build and test C library make test # build and test JS wrapper make js (cd javascript && npm run test) npm pack javascript VERSION=x.y.z scp olm-$VERSION.tgz packages@ldc-prd-matrix-001:/sites/matrix/packages/npm/olm/ git tag $VERSION -s git push --tags # OLMKit CocoaPod release # Make sure the version OLMKit.podspec is the same as the git tag # (this must be checked before git tagging) pod spec lint OLMKit.podspec --use-libraries --allow-warnings pod trunk push OLMKit.podspec --use-libraries --allow-warnings # Check the pod has been successully published with: pod search OLMKit Design ------ Olm is designed to be easy port to different platforms and to be easy to write bindings for. It was originally implemented in C++, with a plain-C layer providing the public API. As development has progressed, it has become clear that C++ gives little advantage, and new functionality is being added in C, with C++ parts being rewritten as the need ariases. Error Handling ~~~~~~~~~~~~~~ All C functions in the API for olm return ``olm_error()`` on error. This makes it easy to check for error conditions within the language bindings. Random Numbers ~~~~~~~~~~~~~~ Olm doesn't generate random numbers itself. Instead the caller must provide the random data. This makes it easier to port the library to different platforms since the caller can use whatever cryptographic random number generator their platform provides. Memory ~~~~~~ Olm avoids calling malloc or allocating memory on the heap itself. Instead the library calculates how much memory will be needed to hold the output and the caller supplies a buffer of the appropriate size. Output Encoding ~~~~~~~~~~~~~~~ Binary output is encoded as base64 so that languages that prefer unicode strings will find it easier to handle the output. Dependencies ~~~~~~~~~~~~ Olm uses pure C implementations of the cryptographic primitives used by the ratchet. While this decreases the performance it makes it much easier to compile the library for different architectures. Contributing ------------ Please see ``_ when making contributions to the library. Security assessment ------------------- Olm 1.3.0 was independently assessed by NCC Group's Cryptography Services Practive in September 2016 to check for security issues: you can read all about it at https://www.nccgroup.trust/us/our-research/matrix-olm-cryptographic-review/ and https://matrix.org/blog/2016/11/21/matrixs-olm-end-to-end-encryption-security-assessment-released-and-implemented-cross-platform-on-riot-at-last/ Bug reports ----------- Please file bug reports at https://github.com/matrix-org/olm/issues What's an olm? -------------- It's a really cool species of European troglodytic salamander. http://www.postojnska-jama.eu/en/come-and-visit-us/vivarium-proteus/ Legal Notice ------------ The software may be subject to the U.S. export control laws and regulations and by downloading the software the user certifies that he/she/it is authorized to do so in accordance with those export control laws and regulations. olm-2.2.2+git20170526.0fd768e+dfsg/android/000077500000000000000000000000001311755073500173425ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/README.rst000066400000000000000000000011121311755073500210240ustar00rootroot00000000000000OlmLibSdk ========= OlmLibSdk exposes an android wrapper to libolm. Installation ------------ Create a libs directory in your project directory Copy the olm-sdk.aar into it. In your build.gradle file, add in the android section:: repositories { flatDir { dir 'libs' } } Add in the dependencies category:: compile(name: 'olm-sdk', ext: 'aar') Development ----------- import the project from the ``android/`` path. The project contains some JNI files and some Java wraper files. The project contains some tests under AndroidTests package. olm-2.2.2+git20170526.0fd768e+dfsg/android/build.gradle000066400000000000000000000007621311755073500216260ustar00rootroot00000000000000// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.1.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { jcenter() } } task clean(type: Delete) { delete rootProject.buildDir } olm-2.2.2+git20170526.0fd768e+dfsg/android/gradle.properties000066400000000000000000000015711311755073500227220ustar00rootroot00000000000000## 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 olm-2.2.2+git20170526.0fd768e+dfsg/android/gradlew000077500000000000000000000115531311755073500207220ustar00rootroot00000000000000#!/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 "$@" olm-2.2.2+git20170526.0fd768e+dfsg/android/gradlew.bat000066400000000000000000000044121311755073500214600ustar00rootroot00000000000000@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 olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/000077500000000000000000000000001311755073500207105ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/build.gradle000066400000000000000000000102551311755073500231720ustar00rootroot00000000000000import org.apache.tools.ant.taskdefs.condition.Os apply plugin: 'com.android.library' android { compileSdkVersion 21 buildToolsVersion '21.1.2' defaultConfig { minSdkVersion 11 targetSdkVersion 21 versionCode 222 versionName "2.2.2" version "2.2.2" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } 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 } clean.dependsOn cleanNative libraryVariants.all { variant -> variant.outputs.each { output -> def outputFile = output.outputFile if (outputFile != null && outputFile.name.endsWith('.aar')) { def fileName = outputFile.name.replace(".aar", "-${version}.aar") output.outputFile = new File(outputFile.parent, fileName) } } } } 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 { compile fileTree(include: ['*.jar'], dir: 'libs') compile 'com.android.support:appcompat-v7:21.+' testCompile 'junit:junit:4.12' androidTestCompile 'junit:junit:4.12' androidTestCompile 'com.android.support:support-annotations:21.0.0' androidTestCompile 'com.android.support.test:runner:0.5' androidTestCompile 'com.android.support.test:rules:0.5' } olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/000077500000000000000000000000001311755073500214775ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/androidTest/000077500000000000000000000000001311755073500237575ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/androidTest/java/000077500000000000000000000000001311755073500247005ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/androidTest/java/org/000077500000000000000000000000001311755073500254675ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/androidTest/java/org/matrix/000077500000000000000000000000001311755073500267735ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/androidTest/java/org/matrix/olm/000077500000000000000000000000001311755073500275625ustar00rootroot00000000000000OlmAccountTest.java000066400000000000000000000406011311755073500332530ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/androidTest/java/org/matrix/olm/* * 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.support.test.runner.AndroidJUnit4; import android.text.TextUtils; import android.util.Log; import org.json.JSONException; import org.json.JSONObject; 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 android.support.test.InstrumentationRegistry.getInstrumentation; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @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(getInstrumentation().getContext()); 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(); assertTrue("OlmAccount failed " + e.getMessage(), false); } assertNotNull(mOlmAccount); mOlmAccount.releaseAccount(); assertTrue(0 == mOlmAccount.getOlmAccountId()); } @Test public void test02CreateAccount() { try { mOlmAccount = new OlmAccount(); } catch (OlmException e) { e.printStackTrace(); assertTrue("OlmAccount failed " + e.getMessage(), false); } 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 identityKeys = null; try { identityKeys = mOlmAccount.identityKeys(); } catch (Exception e) { assertTrue("identityKeys failed " + e.getMessage(), false); } assertNotNull(identityKeys); Log.d(LOG_TAG,"## testIdentityKeys Keys="+identityKeys); // is JSON_KEY_FINGER_PRINT_KEY present? String fingerPrintKey = TestHelper.getFingerprintKey(identityKeys); assertTrue("fingerprint key missing",!TextUtils.isEmpty(fingerPrintKey)); // is JSON_KEY_IDENTITY_KEY present? String identityKey = TestHelper.getIdentityKey(identityKeys); assertTrue("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(); } assertTrue(null == error); } /** * Test the generated amount of one time keys = GENERATION_ONE_TIME_KEYS_NUMBER. */ @Test public void test08OneTimeKeysJsonFormat() { int oneTimeKeysCount = 0; Map> oneTimeKeysJson = null; try { oneTimeKeysJson = mOlmAccount.oneTimeKeys(); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(oneTimeKeysJson); try { Map map = oneTimeKeysJson.get(OlmAccount.JSON_KEY_ONE_TIME_KEY); assertTrue(OlmAccount.JSON_KEY_ONE_TIME_KEY +" object is missing", null!=map); // test the count of the generated one time keys: oneTimeKeysCount = map.size(); assertTrue("Expected count="+GENERATION_ONE_TIME_KEYS_NUMBER+" found="+oneTimeKeysCount,GENERATION_ONE_TIME_KEYS_NUMBER==oneTimeKeysCount); } catch (Exception e) { assertTrue("Exception MSg="+e.getMessage(), false); } } @Test public void test10RemoveOneTimeKeysForSession() { OlmSession olmSession = null; try { olmSession = new OlmSession(); } catch (OlmException e) { assertTrue("Exception Msg="+e.getMessage(), false); } long sessionId = olmSession.getOlmSessionId(); assertTrue(0 != sessionId); String errorMessage = null; try { mOlmAccount.removeOneTimeKeys(olmSession); } catch (Exception e) { errorMessage = e.getMessage(); } assertTrue(null != errorMessage); olmSession.releaseSession(); sessionId = olmSession.getOlmSessionId(); assertTrue(0 == sessionId); } @Test public void test11MarkOneTimeKeysAsPublished() { try { mOlmAccount.markOneTimeKeysAsPublished(); } catch (Exception e) { assertTrue(e.getMessage(), false); } } @Test public void test12SignMessage() { String clearMsg = "String to be signed by olm"; String signedMsg = null; try { signedMsg = mOlmAccount.signMessage(clearMsg); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(signedMsg); // additional tests are performed in test01VerifyEd25519Signing() } // ******************************************************** // ************* SERIALIZATION TEST *********************** // ******************************************************** @Test public void test13Serialization() { FileOutputStream fileOutput; ObjectOutputStream objectOutput; OlmAccount accountRef = null; OlmAccount accountDeserial = null; try { accountRef = new OlmAccount(); } catch (OlmException e) { assertTrue(e.getMessage(),false); } try { accountRef.generateOneTimeKeys(GENERATION_ONE_TIME_KEYS_NUMBER); } catch (Exception e) { assertTrue(e.getMessage(),false); } // get keys references Map identityKeysRef = null; try { identityKeysRef = accountRef.identityKeys(); } catch (Exception e) { assertTrue("identityKeys failed " + e.getMessage(), false); } Map> oneTimeKeysRef = null; try { oneTimeKeysRef = accountRef.oneTimeKeys(); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(identityKeysRef); assertNotNull(oneTimeKeysRef); try { Context context = getInstrumentation().getContext(); //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 identityKeysDeserial = accountDeserial.identityKeys(); Map> oneTimeKeysDeserial = accountDeserial.oneTimeKeys(); assertNotNull(identityKeysDeserial); assertNotNull(oneTimeKeysDeserial); // compare identity keys assertTrue(identityKeysDeserial.toString().equals(identityKeysRef.toString())); // compare onetime keys assertTrue(oneTimeKeysDeserial.toString().equals(oneTimeKeysRef.toString())); accountRef.releaseAccount(); accountDeserial.releaseAccount(); } catch (FileNotFoundException e) { Log.e(LOG_TAG, "## test13Serialization(): Exception FileNotFoundException Msg=="+e.getMessage()); assertTrue("test13Serialization failed " + e.getMessage(), false); } catch (ClassNotFoundException e) { Log.e(LOG_TAG, "## test13Serialization(): Exception ClassNotFoundException Msg==" + e.getMessage()); assertTrue("test13Serialization failed " + e.getMessage(), false); } catch (IOException e) { Log.e(LOG_TAG, "## test13Serialization(): Exception IOException Msg==" + e.getMessage()); assertTrue("test13Serialization failed " + e.getMessage(), false); } /*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()); assertTrue("test13Serialization failed " + e.getMessage(), false); } } // **************************************************** // *************** 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(); } assertTrue(null == errorMessage); // keys number = negative value errorMessage = null; try { mOlmAccount.generateOneTimeKeys(-50); } catch (Exception e) { errorMessage = e.getMessage(); } assertTrue(null != errorMessage); } @Test public void test15RemoveOneTimeKeysForSessionError() { OlmAccount olmAccount = null; try { olmAccount = new OlmAccount(); } catch (OlmException e) { assertTrue(e.getMessage(),false); } try { olmAccount.removeOneTimeKeys(null); } catch (Exception e) { assertTrue(e.getMessage(), false); } olmAccount.releaseAccount(); } @Test public void test16SignMessageError() { OlmAccount olmAccount = null; try { olmAccount = new OlmAccount(); } catch (OlmException e) { assertTrue(e.getMessage(),false); } 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 identityKeys1 = account1.identityKeys(); Map identityKeys2 = account2.identityKeys(); Map identityKeys3 = account3.identityKeys(); Map identityKeys4 = account4.identityKeys(); Map identityKeys5 = account5.identityKeys(); Map identityKeys6 = account6.identityKeys(); Map identityKeys7 = account7.identityKeys(); Map identityKeys8 = account8.identityKeys(); Map identityKeys9 = account9.identityKeys(); Map identityKeys10 = account10.identityKeys(); String identityKey1 = TestHelper.getIdentityKey(identityKeys1); String identityKey2 = TestHelper.getIdentityKey(identityKeys2); assertFalse(identityKey1.equals(identityKey2)); String identityKey3 = TestHelper.getIdentityKey(identityKeys3); assertFalse(identityKey2.equals(identityKey3)); String identityKey4 = TestHelper.getIdentityKey(identityKeys4); assertFalse(identityKey3.equals(identityKey4)); String identityKey5 = TestHelper.getIdentityKey(identityKeys5); assertFalse(identityKey4.equals(identityKey5)); String identityKey6 = TestHelper.getIdentityKey(identityKeys6); assertFalse(identityKey5.equals(identityKey6)); String identityKey7 = TestHelper.getIdentityKey(identityKeys7); assertFalse(identityKey6.equals(identityKey7)); String identityKey8 = TestHelper.getIdentityKey(identityKeys8); assertFalse(identityKey7.equals(identityKey8)); String identityKey9 = TestHelper.getIdentityKey(identityKeys9); assertFalse(identityKey8.equals(identityKey9)); String identityKey10 = TestHelper.getIdentityKey(identityKeys10); assertFalse(identityKey9.equals(identityKey10)); account1.releaseAccount(); account2.releaseAccount(); account3.releaseAccount(); account4.releaseAccount(); account5.releaseAccount(); account6.releaseAccount(); account7.releaseAccount(); account8.releaseAccount(); account9.releaseAccount(); account10.releaseAccount(); } catch (OlmException e) { assertTrue(e.getMessage(),false); } } } OlmGroupSessionTest.java000066400000000000000000000626701311755073500343310ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/androidTest/java/org/matrix/olm/* * 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.support.test.runner.AndroidJUnit4; import android.text.TextUtils; import android.util.Log; 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 android.support.test.InstrumentationRegistry.getInstrumentation; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @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) { assertTrue("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode(), false); } } @Test public void test02GetOutboundGroupSessionIdentifier() { // test session ID mAliceSessionIdentifier = null; try { mAliceSessionIdentifier = mAliceOutboundGroupSession.sessionIdentifier(); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(mAliceSessionIdentifier); assertTrue(mAliceSessionIdentifier.length() > 0); } @Test public void test03GetOutboundGroupSessionKey() { // test session Key mAliceOutboundSessionKey = null; try { mAliceOutboundSessionKey = mAliceOutboundGroupSession.sessionKey(); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(mAliceOutboundSessionKey); assertTrue(mAliceOutboundSessionKey.length() > 0); } @Test public void test04GetOutboundGroupMessageIndex() { // test message index before any encryption mAliceMessageIndex = mAliceOutboundGroupSession.messageIndex(); assertTrue(0 == mAliceMessageIndex); } @Test public void test05OutboundGroupEncryptMessage() { // alice encrypts a message to bob try { mAliceToBobMessage = mAliceOutboundGroupSession.encryptMessage(CLEAR_MESSAGE1); } catch (Exception e) { assertTrue("Exception in bob encryptMessage, Exception code=" + e.getMessage(), false); } assertFalse(TextUtils.isEmpty(mAliceToBobMessage)); // test message index after encryption is incremented mAliceMessageIndex = mAliceOutboundGroupSession.messageIndex(); assertTrue(1 == mAliceMessageIndex); } @Test public void test06CreateInboundGroupSession() { // bob creates INBOUND GROUP SESSION with alice outbound key try { mBobInboundGroupSession = new OlmInboundGroupSession(mAliceOutboundSessionKey); } catch (OlmException e) { assertTrue("Exception in bob OlmInboundGroupSession, Exception code=" + e.getExceptionCode(), false); } } @Test public void test08GetInboundGroupSessionIdentifier() { // check both session identifiers are equals mBobSessionIdentifier = null; try { mBobSessionIdentifier = mBobInboundGroupSession.sessionIdentifier(); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertFalse(TextUtils.isEmpty(mBobSessionIdentifier)); } @Test public void test09SessionIdentifiersAreIdentical() { // check both session identifiers are equals: alice vs bob assertTrue(mAliceSessionIdentifier.equals(mBobSessionIdentifier)); } @Test public void test10InboundDecryptMessage() { mBobDecryptedMessage = null; OlmInboundGroupSession.DecryptMessageResult result = null; try { result = mBobInboundGroupSession.decryptMessage(mAliceToBobMessage); } catch (Exception e) { assertTrue(e.getMessage(), false); } // test decrypted message mBobDecryptedMessage = result.mDecryptedMessage; assertFalse(TextUtils.isEmpty(mBobDecryptedMessage)); assertTrue(0 == result.mIndex); } @Test public void test11InboundDecryptedMessageIdentical() { // test decrypted message assertTrue(mBobDecryptedMessage.equals(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) { assertTrue("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode(), false); } assertNotNull(outboundGroupSessionRef); // serialize alice session Context context = getInstrumentation().getContext(); 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 assertTrue(sessionKeyRef.equals(sessionKeySerial)); // get sessions IDs String sessionIdRef = outboundGroupSessionRef.sessionIdentifier(); String sessionIdSerial = outboundGroupSessionSerial.sessionIdentifier(); assertFalse(TextUtils.isEmpty(sessionIdRef)); assertFalse(TextUtils.isEmpty(sessionIdSerial)); // session IDs comparison assertTrue(sessionIdRef.equals(sessionIdSerial)); outboundGroupSessionRef.releaseSession(); outboundGroupSessionSerial.releaseSession(); assertTrue(outboundGroupSessionRef.isReleased()); assertTrue(outboundGroupSessionSerial.isReleased()); } catch (FileNotFoundException e) { Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception FileNotFoundException Msg=="+e.getMessage()); assertTrue(e.getMessage(), false); } catch (ClassNotFoundException e) { Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception ClassNotFoundException Msg==" + e.getMessage()); assertTrue(e.getMessage(), false); } catch (OlmException e) { Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception OlmException Msg==" + e.getMessage()); assertTrue(e.getMessage(), false); } catch (IOException e) { Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception IOException Msg==" + e.getMessage()); assertTrue(e.getMessage(), false); } catch (Exception e) { Log.e(LOG_TAG, "## test15SerializeOutboundSession(): Exception Msg==" + e.getMessage()); assertTrue(e.getMessage(), false); } } @Test public void test16SerializeInboundSession() { OlmOutboundGroupSession aliceOutboundGroupSession=null; OlmInboundGroupSession bobInboundGroupSessionRef=null; OlmInboundGroupSession bobInboundGroupSessionSerial; // alice creates OUTBOUND GROUP SESSION try { aliceOutboundGroupSession = new OlmOutboundGroupSession(); } catch (OlmException e) { assertTrue("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode(), false); } assertNotNull(aliceOutboundGroupSession); // get the session key from the outbound group session String sessionKeyRef = null; try { sessionKeyRef = aliceOutboundGroupSession.sessionKey(); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(sessionKeyRef); // bob creates INBOUND GROUP SESSION try { bobInboundGroupSessionRef = new OlmInboundGroupSession(sessionKeyRef); } catch (OlmException e) { assertTrue("Exception in OlmInboundGroupSession, Exception code=" + e.getExceptionCode(), false); } assertNotNull(bobInboundGroupSessionRef); // serialize alice session Context context = getInstrumentation().getContext(); 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 assertTrue(aliceSessionId.equals(sessionIdSerial)); assertTrue(sessionIdRef.equals(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()); assertTrue(e.getMessage(), false); } catch (ClassNotFoundException e) { Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception ClassNotFoundException Msg==" + e.getMessage()); assertTrue(e.getMessage(), false); } catch (OlmException e) { Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception OlmException Msg==" + e.getMessage()); assertTrue(e.getMessage(), false); } catch (IOException e) { Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception IOException Msg==" + e.getMessage()); assertTrue(e.getMessage(), false); } catch (Exception e) { Log.e(LOG_TAG, "## test16SerializeInboundSession(): Exception Msg==" + e.getMessage()); assertTrue(e.getMessage(), false); } } /** * 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(); assertFalse(sessionKey1.equals(sessionKey2)); String sessionKey3 = outboundGroupSession3.sessionKey(); assertFalse(sessionKey2.equals(sessionKey3)); String sessionKey4 = outboundGroupSession4.sessionKey(); assertFalse(sessionKey3.equals(sessionKey4)); String sessionKey5 = outboundGroupSession5.sessionKey(); assertFalse(sessionKey4.equals(sessionKey5)); String sessionKey6 = outboundGroupSession6.sessionKey(); assertFalse(sessionKey5.equals(sessionKey6)); String sessionKey7 = outboundGroupSession7.sessionKey(); assertFalse(sessionKey6.equals(sessionKey7)); String sessionKey8 = outboundGroupSession8.sessionKey(); assertFalse(sessionKey7.equals(sessionKey8)); // get the session IDs from the outbound group sessions String sessionId1 = outboundGroupSession1.sessionIdentifier(); String sessionId2 = outboundGroupSession2.sessionIdentifier(); assertFalse(sessionId1.equals(sessionId2)); String sessionId3 = outboundGroupSession3.sessionKey(); assertFalse(sessionId2.equals(sessionId3)); String sessionId4 = outboundGroupSession4.sessionKey(); assertFalse(sessionId3.equals(sessionId4)); String sessionId5 = outboundGroupSession5.sessionKey(); assertFalse(sessionId4.equals(sessionId5)); String sessionId6 = outboundGroupSession6.sessionKey(); assertFalse(sessionId5.equals(sessionId6)); String sessionId7 = outboundGroupSession7.sessionKey(); assertFalse(sessionId6.equals(sessionId7)); String sessionId8 = outboundGroupSession8.sessionKey(); assertFalse(sessionId7.equals(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) { assertTrue("Exception in OlmOutboundGroupSession, Exception code=" + e.getExceptionCode(), false); } } /** * 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".
* 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) { assertTrue("Exception in test18TestBadCharacterCrashInDecrypt, Exception code=" + e.getExceptionCode(), false); } OlmInboundGroupSession.DecryptMessageResult result = null; try { result = bobInboundGroupSession.decryptMessage(msgToDecryptWithEmoji); } catch (Exception e) { assertTrue("Exception in test18TestBadCharacterCrashInDecrypt, Exception code=" + e.getMessage(), false); } assertNotNull(result.mDecryptedMessage); assertTrue(13 == result.mIndex); } /** * Specific test to check an error message is returned by decryptMessage() API.
* 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) { assertTrue("Exception in test19TestErrorMessageReturnedInDecrypt, Exception code=" + e.getExceptionCode(), false); } String exceptionMessage = null; try { bobInboundGroupSession.decryptMessage(corruptedEncryptedMsg); } catch (OlmException e) { exceptionMessage = e.getMessage(); } assertTrue(0!=EXPECTED_ERROR_MESSAGE.length()); assertTrue(EXPECTED_ERROR_MESSAGE.equals(exceptionMessage)); } /** * Test the import/export functions.
**/ @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) { assertTrue("OlmInboundGroupSession failed " + e.getMessage(), false); } boolean isVerified = false; try { isVerified = inboundGroupSession.isVerified(); } catch (Exception e) { assertTrue("isVerified failed " + e.getMessage(), false); } assertTrue(isVerified); OlmInboundGroupSession.DecryptMessageResult result = null; try { result = inboundGroupSession.decryptMessage(message); } catch (Exception e) { assertTrue("decryptMessage failed " + e.getMessage(), false); } assertTrue(TextUtils.equals(result.mDecryptedMessage, "Message")); assertTrue(0 == result.mIndex); String export = null; try { export = inboundGroupSession.export(0); } catch (Exception e) { assertTrue("export failed " + e.getMessage(), false); } assertTrue(!TextUtils.isEmpty(export)); long index = -1; try { index = inboundGroupSession.getFirstKnownIndex(); } catch (Exception e) { assertTrue("getFirstKnownIndex failed " + e.getMessage(), false); } assertTrue(index >=0); inboundGroupSession.releaseSession(); inboundGroupSession = null; OlmInboundGroupSession inboundGroupSession2 = null; try { inboundGroupSession2 = inboundGroupSession.importSession(export); } catch (Exception e) { assertTrue("OlmInboundGroupSession failed " + e.getMessage(), false); } try { isVerified = inboundGroupSession2.isVerified(); } catch (Exception e) { assertTrue("isVerified failed " + e.getMessage(), false); } assertFalse(isVerified); result = null; try { result = inboundGroupSession2.decryptMessage(message); } catch (Exception e) { assertTrue("decryptMessage failed " + e.getMessage(), false); } assertTrue(TextUtils.equals(result.mDecryptedMessage, "Message")); assertTrue(0 == result.mIndex); try { isVerified = inboundGroupSession2.isVerified(); } catch (Exception e) { assertTrue("isVerified failed " + e.getMessage(), false); } assertTrue(isVerified); inboundGroupSession2.releaseSession(); } } OlmSessionTest.java000066400000000000000000001027211311755073500333040ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/androidTest/java/org/matrix/olm/* * 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.support.test.runner.AndroidJUnit4; import android.text.TextUtils; import android.util.Log; import org.json.JSONObject; 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 android.support.test.InstrumentationRegistry.getInstrumentation; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @RunWith(AndroidJUnit4.class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class OlmSessionTest { private static final String LOG_TAG = "OlmSessionTest"; private final String INVALID_PRE_KEY = "invalid PRE KEY hu hu!"; private final String FILE_NAME_SERIAL_SESSION = "SerialSession"; private final int ONE_TIME_KEYS_NUMBER = 4; 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); } /** * Basic test: * - alice creates an account * - bob creates an account * - alice creates an outbound session with bob (bobIdentityKey & bobOneTimeKey) * - alice encrypts a message with its session * - bob creates an inbound session based on alice's encrypted message * - bob decrypts the encrypted message with its session */ @Test public void test01AliceToBob() { final int ONE_TIME_KEYS_NUMBER = 5; String bobIdentityKey = null; String bobOneTimeKey=null; OlmAccount bobAccount = null; OlmAccount aliceAccount = null; // ALICE & BOB ACCOUNTS CREATION try { aliceAccount = new OlmAccount(); bobAccount = new OlmAccount(); } catch (OlmException e) { assertTrue(e.getMessage(),false); } // test accounts creation assertTrue(0!=bobAccount.getOlmAccountId()); assertTrue(0!=aliceAccount.getOlmAccountId()); // get bob identity key Map bobIdentityKeys = null; try { bobIdentityKeys = bobAccount.identityKeys(); } catch (Exception e) { assertTrue("identityKeys failed " + e.getMessage(), false); } bobIdentityKey = TestHelper.getIdentityKey(bobIdentityKeys); assertTrue(null!=bobIdentityKey); // get bob one time keys try { bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER); } catch (Exception e) { assertTrue(e.getMessage(), false); } Map> bobOneTimeKeys = null; try { bobOneTimeKeys = bobAccount.oneTimeKeys(); } catch (Exception e) { assertTrue(e.getMessage(), false); } bobOneTimeKey = TestHelper.getOneTimeKey(bobOneTimeKeys,1); assertNotNull(bobOneTimeKey); // CREATE ALICE SESSION OlmSession aliceSession = null; try { aliceSession = new OlmSession(); } catch (OlmException e) { assertTrue("Exception Msg="+e.getMessage(), false); } assertTrue(0!=aliceSession.getOlmSessionId()); // CREATE ALICE OUTBOUND SESSION and encrypt message to bob try { aliceSession.initOutboundSession(aliceAccount, bobIdentityKey, bobOneTimeKey); } catch (Exception e) { assertTrue(e.getMessage(), false); } String clearMsg = "Heloo bob , this is alice!"; OlmMessage encryptedMsgToBob = null; try { encryptedMsgToBob = aliceSession.encryptMessage(clearMsg); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(encryptedMsgToBob); assertNotNull(encryptedMsgToBob.mCipherText); Log.d(LOG_TAG,"## test01AliceToBob(): encryptedMsg="+encryptedMsgToBob.mCipherText); // CREATE BOB INBOUND SESSION and decrypt message from alice OlmSession bobSession = null; try { bobSession = new OlmSession(); } catch (OlmException e) { assertTrue("Exception Msg="+e.getMessage(), false); } assertTrue(0!=bobSession.getOlmSessionId()); try { bobSession.initInboundSession(bobAccount, encryptedMsgToBob.mCipherText); } catch (Exception e) { assertTrue("initInboundSessionWithAccount failed " + e.getMessage(), false); } String decryptedMsg = null; try { decryptedMsg = bobSession.decryptMessage(encryptedMsgToBob); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(decryptedMsg); // MESSAGE COMPARISON: decrypted vs encrypted assertTrue(clearMsg.equals(decryptedMsg)); // clean objects.. try { bobAccount.removeOneTimeKeys(bobSession); } catch (Exception e) { assertTrue(e.getMessage(), false); } // release accounts bobAccount.releaseAccount(); aliceAccount.releaseAccount(); assertTrue(bobAccount.isReleased()); assertTrue(aliceAccount.isReleased()); // release sessions bobSession.releaseSession(); aliceSession.releaseSession(); assertTrue(bobSession.isReleased()); assertTrue(aliceSession.isReleased()); } /** * Same as test01AliceToBob but with bob who's encrypting messages * to alice and alice decrypt them.
* - alice creates an account * - bob creates an account * - alice creates an outbound session with bob (bobIdentityKey & bobOneTimeKey) * - alice encrypts a message with its own session * - bob creates an inbound session based on alice's encrypted message * - bob decrypts the encrypted message with its own session * - bob encrypts messages with its own session * - alice decrypts bob's messages with its own message * - alice encrypts a message * - bob decrypts the encrypted message */ @Test public void test02AliceToBobBackAndForth() { String bobIdentityKey; String bobOneTimeKey; OlmAccount aliceAccount = null; OlmAccount bobAccount = null; // creates alice & bob accounts try { aliceAccount = new OlmAccount(); bobAccount = new OlmAccount(); } catch (OlmException e) { assertTrue(e.getMessage(),false); } // test accounts creation assertTrue(0!=bobAccount.getOlmAccountId()); assertTrue(0!=aliceAccount.getOlmAccountId()); // get bob identity key Map bobIdentityKeys = null; try { bobIdentityKeys = bobAccount.identityKeys(); } catch (Exception e) { assertTrue("identityKeys failed " + e.getMessage(), false); } bobIdentityKey = TestHelper.getIdentityKey(bobIdentityKeys); assertTrue(null!=bobIdentityKey); // get bob one time keys try { bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER); } catch (Exception e) { assertTrue(e.getMessage(), false); } Map> bobOneTimeKeys = null; try { bobOneTimeKeys = bobAccount.oneTimeKeys(); } catch (Exception e) { assertTrue(e.getMessage(), false); } bobOneTimeKey = TestHelper.getOneTimeKey(bobOneTimeKeys,1); assertNotNull(bobOneTimeKey); // CREATE ALICE SESSION OlmSession aliceSession = null; try { aliceSession = new OlmSession(); } catch (OlmException e) { assertTrue("Exception Msg="+e.getMessage(), false); } assertTrue(0!=aliceSession.getOlmSessionId()); // CREATE ALICE OUTBOUND SESSION and encrypt message to bob try { aliceSession.initOutboundSession(aliceAccount, bobIdentityKey, bobOneTimeKey); } catch (Exception e) { assertTrue(e.getMessage(), false); } String helloClearMsg = "Hello I'm Alice!"; OlmMessage encryptedAliceToBobMsg1 = null; try { encryptedAliceToBobMsg1 = aliceSession.encryptMessage(helloClearMsg); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(encryptedAliceToBobMsg1); assertNotNull(encryptedAliceToBobMsg1.mCipherText); // CREATE BOB INBOUND SESSION and decrypt message from alice OlmSession bobSession = null; try { bobSession = new OlmSession(); } catch (OlmException e) { assertTrue("Exception Msg="+e.getMessage(), false); } assertTrue(0!=bobSession.getOlmSessionId()); try { bobSession.initInboundSession(bobAccount, encryptedAliceToBobMsg1.mCipherText); } catch (Exception e) { assertTrue("initInboundSessionWithAccount failed " + e.getMessage(), false); } // DECRYPT MESSAGE FROM ALICE String decryptedMsg01 = null; try { decryptedMsg01 = bobSession.decryptMessage(encryptedAliceToBobMsg1); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(decryptedMsg01); // MESSAGE COMPARISON: decrypted vs encrypted assertTrue(helloClearMsg.equals(decryptedMsg01)); // BACK/FORTH MESSAGE COMPARISON String clearMsg1 = "Hello I'm Bob!"; String clearMsg2 = "Isn't life grand?"; String clearMsg3 = "Let's go to the opera."; // bob encrypts messages OlmMessage encryptedMsg1 = null; try { encryptedMsg1 = bobSession.encryptMessage(clearMsg1); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(encryptedMsg1); OlmMessage encryptedMsg2 = null; try { encryptedMsg2 = bobSession.encryptMessage(clearMsg2); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(encryptedMsg2); OlmMessage encryptedMsg3 = null; try { encryptedMsg3 = bobSession.encryptMessage(clearMsg3); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(encryptedMsg3); // alice decrypts bob's messages String decryptedMsg1 = null; try { decryptedMsg1 = aliceSession.decryptMessage(encryptedMsg1); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(decryptedMsg1); String decryptedMsg2 = null; try { decryptedMsg2 = aliceSession.decryptMessage(encryptedMsg2); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(decryptedMsg2); String decryptedMsg3 = null; try { decryptedMsg3 = aliceSession.decryptMessage(encryptedMsg3); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(decryptedMsg3); // comparison tests assertTrue(clearMsg1.equals(decryptedMsg1)); assertTrue(clearMsg2.equals(decryptedMsg2)); assertTrue(clearMsg3.equals(decryptedMsg3)); // and one more from alice to bob clearMsg1 = "another message from Alice to Bob!!"; encryptedMsg1 = null; try { encryptedMsg1 = aliceSession.encryptMessage(clearMsg1); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(encryptedMsg1); decryptedMsg1 = null; try { decryptedMsg1 = bobSession.decryptMessage(encryptedMsg1); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(decryptedMsg1); assertTrue(clearMsg1.equals(decryptedMsg1)); // comparison test assertTrue(clearMsg1.equals(decryptedMsg1)); // clean objects.. try { bobAccount.removeOneTimeKeys(bobSession); } catch (Exception e) { assertTrue(e.getMessage(), false); } bobAccount.releaseAccount(); aliceAccount.releaseAccount(); assertTrue(bobAccount.isReleased()); assertTrue(aliceAccount.isReleased()); bobSession.releaseSession(); aliceSession.releaseSession(); assertTrue(bobSession.isReleased()); assertTrue(aliceSession.isReleased()); } @Test public void test03AliceBobSessionId() { // creates alice & bob accounts OlmAccount aliceAccount = null; OlmAccount bobAccount = null; try { aliceAccount = new OlmAccount(); bobAccount = new OlmAccount(); } catch (OlmException e) { assertTrue(e.getMessage(),false); } // test accounts creation assertTrue(0!=bobAccount.getOlmAccountId()); assertTrue(0!=aliceAccount.getOlmAccountId()); // CREATE ALICE SESSION OlmSession aliceSession = null; try { aliceSession = new OlmSession(); } catch (OlmException e) { assertTrue("Exception Msg="+e.getMessage(), false); } assertTrue(0!=aliceSession.getOlmSessionId()); // CREATE ALICE SESSION OlmSession bobSession = null; try { bobSession = new OlmSession(); } catch (OlmException e) { e.printStackTrace(); assertTrue(e.getMessage(), false); } assertTrue(0!=bobSession.getOlmSessionId()); String aliceSessionId = null; try { aliceSessionId = aliceSession.sessionIdentifier(); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(aliceSessionId); String bobSessionId = null; try { bobSessionId = bobSession.sessionIdentifier(); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(bobSessionId); // must be the same for both ends of the conversation assertTrue(aliceSessionId.equals(bobSessionId)); aliceAccount.releaseAccount(); bobAccount.releaseAccount(); assertTrue(aliceAccount.isReleased()); assertTrue(bobAccount.isReleased()); bobSession.releaseSession(); aliceSession.releaseSession(); assertTrue(bobSession.isReleased()); assertTrue(aliceSession.isReleased()); } @Test public void test04MatchInboundSession() { OlmAccount aliceAccount=null, bobAccount=null; OlmSession aliceSession = null, bobSession = null; // ACCOUNTS CREATION try { aliceAccount = new OlmAccount(); bobAccount = new OlmAccount(); } catch (OlmException e) { assertTrue(e.getMessage(), false); } // CREATE ALICE SESSION try { aliceSession = new OlmSession(); bobSession = new OlmSession(); } catch (OlmException e) { assertTrue("Exception Msg=" + e.getMessage(), false); } // get bob/luke identity key Map bobIdentityKeys = null; try { bobIdentityKeys = bobAccount.identityKeys(); } catch (Exception e) { assertTrue("identityKeys failed " + e.getMessage(), false); } Map aliceIdentityKeys = null; try { aliceIdentityKeys = aliceAccount.identityKeys(); } catch (Exception e) { assertTrue("identityKeys failed " + e.getMessage(), false); } String bobIdentityKey = TestHelper.getIdentityKey(bobIdentityKeys); String aliceIdentityKey = TestHelper.getIdentityKey(aliceIdentityKeys); // get bob/luke one time keys try { bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER); } catch (Exception e) { assertTrue(e.getMessage(), false); } try { aliceAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER); } catch (Exception e) { assertTrue(e.getMessage(), false); } Map> bobOneTimeKeys = null; try { bobOneTimeKeys = bobAccount.oneTimeKeys(); } catch (Exception e) { assertTrue(e.getMessage(), false); } String bobOneTimeKey1 = TestHelper.getOneTimeKey(bobOneTimeKeys, 1); // create alice inbound session for bob try { aliceSession.initOutboundSession(aliceAccount, bobIdentityKey, bobOneTimeKey1); } catch (Exception e) { assertTrue(e.getMessage(), false); } String aliceClearMsg = "hello helooo to bob!"; OlmMessage encryptedAliceToBobMsg1 = null; try { encryptedAliceToBobMsg1 = aliceSession.encryptMessage(aliceClearMsg); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertFalse(bobSession.matchesInboundSession(encryptedAliceToBobMsg1.mCipherText)); // init bob session with alice PRE KEY try { bobSession.initInboundSession(bobAccount, encryptedAliceToBobMsg1.mCipherText); } catch (Exception e) { assertTrue("initInboundSessionWithAccount failed " + e.getMessage(), false); } // test matchesInboundSession() and matchesInboundSessionFrom() assertTrue(bobSession.matchesInboundSession(encryptedAliceToBobMsg1.mCipherText)); assertTrue(bobSession.matchesInboundSessionFrom(aliceIdentityKey, encryptedAliceToBobMsg1.mCipherText)); // following requires olm native lib new version with https://github.com/matrix-org/olm-backup/commit/7e9f3bebb8390f975a76c0188ce4cb460fe6692e //assertTrue(false==bobSession.matchesInboundSessionFrom(bobIdentityKey, encryptedAliceToBobMsg1.mCipherText)); // release objects try { bobAccount.removeOneTimeKeys(bobSession); } catch (Exception e) { assertTrue(e.getMessage(), false); } aliceAccount.releaseAccount(); bobAccount.releaseAccount(); assertTrue(aliceAccount.isReleased()); assertTrue(bobAccount.isReleased()); aliceSession.releaseSession(); bobSession.releaseSession(); assertTrue(aliceSession.isReleased()); assertTrue(bobSession.isReleased()); } // ******************************************************** // ************* SERIALIZATION TEST *********************** // ******************************************************** /** * Same as {@link #test02AliceToBobBackAndForth()}, but alice's session * is serialized and de-serialized before performing the final * comparison (encrypt vs ) */ @Test public void test05SessionSerialization() { final int ONE_TIME_KEYS_NUMBER = 1; String bobIdentityKey; String bobOneTimeKey; OlmAccount aliceAccount = null; OlmAccount bobAccount = null; OlmSession aliceSessionDeserial = null; // creates alice & bob accounts try { aliceAccount = new OlmAccount(); bobAccount = new OlmAccount(); } catch (OlmException e) { assertTrue(e.getMessage(),false); } // test accounts creation assertTrue(0!=bobAccount.getOlmAccountId()); assertTrue(0!=aliceAccount.getOlmAccountId()); // get bob identity key Map bobIdentityKeys = null; try { bobIdentityKeys = bobAccount.identityKeys(); } catch (Exception e) { assertTrue("identityKeys failed " + e.getMessage(), false); } bobIdentityKey = TestHelper.getIdentityKey(bobIdentityKeys); assertTrue(null!=bobIdentityKey); // get bob one time keys try { bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER); } catch (Exception e) { assertTrue(e.getMessage(), false); } Map> bobOneTimeKeys = null; try { bobOneTimeKeys = bobAccount.oneTimeKeys(); } catch (Exception e) { assertTrue(e.getMessage(), false); } bobOneTimeKey = TestHelper.getOneTimeKey(bobOneTimeKeys,1); assertNotNull(bobOneTimeKey); // CREATE ALICE SESSION OlmSession aliceSession = null; try { aliceSession = new OlmSession(); } catch (OlmException e) { assertTrue("Exception Msg="+e.getMessage(), false); } assertTrue(0!=aliceSession.getOlmSessionId()); // CREATE ALICE OUTBOUND SESSION and encrypt message to bob try { aliceSession.initOutboundSession(aliceAccount, bobIdentityKey, bobOneTimeKey); } catch (Exception e) { assertTrue(e.getMessage(), false); } String helloClearMsg = "Hello I'm Alice!"; OlmMessage encryptedAliceToBobMsg1 = null; try { encryptedAliceToBobMsg1 = aliceSession.encryptMessage(helloClearMsg); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(encryptedAliceToBobMsg1); assertNotNull(encryptedAliceToBobMsg1.mCipherText); // CREATE BOB INBOUND SESSION and decrypt message from alice OlmSession bobSession = null; try { bobSession = new OlmSession(); } catch (OlmException e) { assertTrue("Exception Msg="+e.getMessage(), false); } assertTrue(0!=bobSession.getOlmSessionId()); // init bob session with alice PRE KEY try { bobSession.initInboundSession(bobAccount, encryptedAliceToBobMsg1.mCipherText); } catch (Exception e) { assertTrue("initInboundSessionWithAccount failed " + e.getMessage(), false); } // DECRYPT MESSAGE FROM ALICE String decryptedMsg01 = null; try { decryptedMsg01 = bobSession.decryptMessage(encryptedAliceToBobMsg1); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(decryptedMsg01); // MESSAGE COMPARISON: decrypted vs encrypted assertTrue(helloClearMsg.equals(decryptedMsg01)); // BACK/FORTH MESSAGE COMPARISON String clearMsg1 = "Hello I'm Bob!"; String clearMsg2 = "Isn't life grand?"; String clearMsg3 = "Let's go to the opera."; // bob encrypts messages OlmMessage encryptedMsg1 = null; try { encryptedMsg1 = bobSession.encryptMessage(clearMsg1); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(encryptedMsg1); OlmMessage encryptedMsg2 = null; try { encryptedMsg2 = bobSession.encryptMessage(clearMsg2); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(encryptedMsg2); OlmMessage encryptedMsg3 = null; try { encryptedMsg3 = bobSession.encryptMessage(clearMsg3); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(encryptedMsg3); // serialize alice session Context context = getInstrumentation().getContext(); try { FileOutputStream fileOutput = context.openFileOutput(FILE_NAME_SERIAL_SESSION, Context.MODE_PRIVATE); ObjectOutputStream objectOutput = new ObjectOutputStream(fileOutput); objectOutput.writeObject(aliceSession); objectOutput.flush(); objectOutput.close(); // deserialize session FileInputStream fileInput = context.openFileInput(FILE_NAME_SERIAL_SESSION); ObjectInputStream objectInput = new ObjectInputStream(fileInput); aliceSessionDeserial = (OlmSession) objectInput.readObject(); objectInput.close(); // test deserialize return value assertNotNull(aliceSessionDeserial); // de-serialized alice session decrypts bob's messages String decryptedMsg1 = aliceSessionDeserial.decryptMessage(encryptedMsg1); assertNotNull(decryptedMsg1); String decryptedMsg2 = aliceSessionDeserial.decryptMessage(encryptedMsg2); assertNotNull(decryptedMsg2); String decryptedMsg3 = aliceSessionDeserial.decryptMessage(encryptedMsg3); assertNotNull(decryptedMsg3); // comparison tests assertTrue(clearMsg1.equals(decryptedMsg1)); assertTrue(clearMsg2.equals(decryptedMsg2)); assertTrue(clearMsg3.equals(decryptedMsg3)); // clean objects.. try { bobAccount.removeOneTimeKeys(bobSession); } catch (Exception e) { assertTrue(e.getMessage(), false); } bobAccount.releaseAccount(); aliceAccount.releaseAccount(); assertTrue(bobAccount.isReleased()); assertTrue(aliceAccount.isReleased()); bobSession.releaseSession(); aliceSession.releaseSession(); aliceSessionDeserial.releaseSession(); assertTrue(bobSession.isReleased()); assertTrue(aliceSession.isReleased()); assertTrue(aliceSessionDeserial.isReleased()); } catch (FileNotFoundException e) { Log.e(LOG_TAG, "## test03SessionSerialization(): Exception FileNotFoundException Msg=="+e.getMessage()); assertTrue(e.getMessage(), false); } catch (ClassNotFoundException e) { Log.e(LOG_TAG, "## test03SessionSerialization(): Exception ClassNotFoundException Msg==" + e.getMessage()); assertTrue(e.getMessage(), false); } catch (IOException e) { Log.e(LOG_TAG, "## test03SessionSerialization(): Exception IOException Msg==" + e.getMessage()); assertTrue(e.getMessage(), false); } /*catch (OlmException e) { Log.e(LOG_TAG, "## test03SessionSerialization(): Exception OlmException Msg==" + e.getMessage()); }*/ catch (Exception e) { Log.e(LOG_TAG, "## test03SessionSerialization(): Exception Msg==" + e.getMessage()); assertTrue(e.getMessage(), false); } } // **************************************************** // *************** SANITY CHECK TESTS ***************** // **************************************************** @Test public void test06SanityCheckErrors() { final int ONE_TIME_KEYS_NUMBER = 5; OlmAccount bobAccount = null; OlmAccount aliceAccount = null; // ALICE & BOB ACCOUNTS CREATION try { aliceAccount = new OlmAccount(); bobAccount = new OlmAccount(); } catch (OlmException e) { assertTrue(e.getMessage(), false); } // get bob identity key Map bobIdentityKeys = null; try { bobIdentityKeys = bobAccount.identityKeys(); } catch (Exception e) { assertTrue("identityKeys failed " + e.getMessage(), false); } String bobIdentityKey = TestHelper.getIdentityKey(bobIdentityKeys); assertTrue(null != bobIdentityKey); // get bob one time keys try { bobAccount.generateOneTimeKeys(ONE_TIME_KEYS_NUMBER); } catch (Exception e) { assertTrue(e.getMessage(), false); } Map> bobOneTimeKeys = null; try { bobOneTimeKeys = bobAccount.oneTimeKeys(); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(bobOneTimeKeys); String bobOneTimeKey = TestHelper.getOneTimeKey(bobOneTimeKeys,1); assertNotNull(bobOneTimeKey); // CREATE ALICE SESSION OlmSession aliceSession = null; try { aliceSession = new OlmSession(); } catch (OlmException e) { assertTrue("Exception Msg=" + e.getMessage(), false); } // SANITY CHECK TESTS FOR: initOutboundSessionWithAccount() String errorMessage = null; try { aliceSession.initOutboundSession(null, bobIdentityKey, bobOneTimeKey); } catch (Exception e) { errorMessage = e.getMessage(); } assertTrue(null != errorMessage); errorMessage = null; try { aliceSession.initOutboundSession(aliceAccount, null, bobOneTimeKey); } catch (Exception e) { errorMessage = e.getMessage(); } assertTrue(null != errorMessage); errorMessage = null; try { aliceSession.initOutboundSession(aliceAccount, bobIdentityKey, null); } catch (Exception e) { errorMessage = e.getMessage(); } assertTrue(null != errorMessage); errorMessage = null; try { aliceSession.initOutboundSession(null, null, null); } catch (Exception e) { errorMessage = e.getMessage(); } assertTrue(null != errorMessage); // init properly errorMessage = null; try { aliceSession.initOutboundSession(aliceAccount, bobIdentityKey, bobOneTimeKey); } catch (Exception e) { errorMessage = e.getMessage(); } assertTrue(null == errorMessage); // SANITY CHECK TESTS FOR: encryptMessage() OlmMessage message = null; try { message = aliceSession.encryptMessage(null); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertTrue(null==message); // encrypt properly OlmMessage encryptedMsgToBob = null; try { encryptedMsgToBob = aliceSession.encryptMessage("A message for bob"); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(encryptedMsgToBob); // SANITY CHECK TESTS FOR: initInboundSessionWithAccount() OlmSession bobSession = null; try { bobSession = new OlmSession(); errorMessage = null; try { bobSession.initInboundSession(null, encryptedMsgToBob.mCipherText); } catch (Exception e) { errorMessage = e.getMessage(); } assertTrue(!TextUtils.isEmpty(errorMessage)); errorMessage = null; try { bobSession.initInboundSession(bobAccount, null); } catch (Exception e) { errorMessage = e.getMessage(); } assertTrue(!TextUtils.isEmpty(errorMessage)); errorMessage = null; try { bobSession.initInboundSession(bobAccount, INVALID_PRE_KEY); } catch (Exception e) { errorMessage = e.getMessage(); } assertTrue(!TextUtils.isEmpty(errorMessage)); // init properly errorMessage = null; try { bobSession.initInboundSession(bobAccount, encryptedMsgToBob.mCipherText); } catch (Exception e) { errorMessage = e.getMessage(); } assertTrue(TextUtils.isEmpty(errorMessage)); } catch (OlmException e) { assertTrue("Exception Msg="+e.getMessage(), false); } // SANITY CHECK TESTS FOR: decryptMessage() String decryptedMsg = null; try { decryptedMsg = aliceSession.decryptMessage(null); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertTrue(null==decryptedMsg); // SANITY CHECK TESTS FOR: matchesInboundSession() assertTrue(!aliceSession.matchesInboundSession(null)); // SANITY CHECK TESTS FOR: matchesInboundSessionFrom() assertTrue(!aliceSession.matchesInboundSessionFrom(null,null)); // release objects try { bobAccount.removeOneTimeKeys(bobSession); } catch (Exception e) { assertTrue(e.getMessage(), false); } aliceAccount.releaseAccount(); bobAccount.releaseAccount(); assertTrue(aliceAccount.isReleased()); assertTrue(bobAccount.isReleased()); aliceSession.releaseSession(); bobSession.releaseSession(); assertTrue(aliceSession.isReleased()); assertTrue(bobSession.isReleased()); } } OlmUtilityTest.java000066400000000000000000000121411311755073500333200ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/androidTest/java/org/matrix/olm/* * 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.support.test.runner.AndroidJUnit4; import android.text.TextUtils; import android.util.Log; import org.json.JSONObject; 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; @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) { assertTrue(e.getMessage(),false); } assertNotNull(account); // sign message String messageSignature = null; try { messageSignature = account.signMessage(message); } catch (Exception e) { assertTrue(e.getMessage(), false); } assertNotNull(messageSignature); // get identities key (finger print key) Map identityKeys = null; try { identityKeys = account.identityKeys(); } catch (Exception e) { assertTrue("identityKeys failed " + e.getMessage(), false); } assertNotNull(identityKeys); fingerPrintKey = TestHelper.getFingerprintKey(identityKeys); assertTrue("fingerprint key missing",!TextUtils.isEmpty(fingerPrintKey)); // instantiate utility object OlmUtility utility = null; try { utility = new OlmUtility(); } catch (Exception e) { assertTrue("failed to create OlmUtility", false); } // 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(); } assertTrue(!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(); } assertTrue(!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) { assertTrue("OlmUtility creation failed", false); } 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()); } } TestHelper.java000066400000000000000000000057521311755073500324360ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/androidTest/java/org/matrix/olm/* * 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; /** * 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 aIdentityKeysMap){ String idKey = null; try { idKey = aIdentityKeysMap.get(OlmAccount.JSON_KEY_IDENTITY_KEY); } catch (Exception e) { assertTrue("Exception MSg=" + e.getMessage(), false); } 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 aIdentityKeysMap) { String fingerprintKey = null; try { fingerprintKey = aIdentityKeysMap.get(OlmAccount.JSON_KEY_FINGER_PRINT_KEY); } catch (Exception e) { assertTrue("Exception MSg=" + e.getMessage(), false); } 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> aIdentityKeysMap, int aKeyPosition) { String firstOneTimeKey = null; try { Map 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) { assertTrue("Exception Msg=" + e.getMessage(), false); } return firstOneTimeKey; } } olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/000077500000000000000000000000001311755073500224235ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/AndroidManifest.xml000066400000000000000000000003411311755073500262120ustar00rootroot00000000000000 olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/java/000077500000000000000000000000001311755073500233445ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/java/org/000077500000000000000000000000001311755073500241335ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/java/org/matrix/000077500000000000000000000000001311755073500254375ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/java/org/matrix/olm/000077500000000000000000000000001311755073500262265ustar00rootroot00000000000000CommonSerializeUtils.java000066400000000000000000000053531311755073500331410ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/java/org/matrix/olm/* * 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; } olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/java/org/matrix/olm/OlmAccount.java000066400000000000000000000365611311755073500311500ustar00rootroot00000000000000/* * 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.Map; /** * Account class used to create Olm sessions in conjunction with {@link OlmSession} class.
* OlmAccount provides APIs to retrieve the Olm keys. *

Detailed implementation guide is available at Implementing End-to-End Encryption in Matrix clients. */ 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.
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.
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.
* 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.
* Public API for {@link #releaseAccountJni()}. */ public void releaseAccount() { if (0 != mNativeId) { releaseAccountJni(); } mNativeId = 0; } /** * Destroy the corresponding OLM account native object.
* 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.
* @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.
* Public API for {@link #identityKeysJni()}.
* Ex: * { * "curve25519":"Vam++zZPMqDQM6ANKpO/uAl5ViJSHxV9hd+b0/fwRAg", * "ed25519":"+v8SOlOASFTMrX3MCKBM4iVnYoZ+JIjpNt1fi8Z9O2I" * } * @return identity keys dictionary if operation succeeds, null otherwise * @exception OlmException the failure reason */ public Map 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).
* 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.
If total number of keys stored * by this account exceeds {@link #maxOneTimeKeys()}, the old keys are discarded.
* 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.
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.
* @param aNumberOfKeys number of keys to generate */ private native void generateOneTimeKeysJni(int aNumberOfKeys); /** * Return the "one time keys" in a dictionary.
* The number of "one time keys", is specified by {@link #generateOneTimeKeys(int)}
* Ex: * { "curve25519": * { * "AAAABQ":"qefVZd8qvjOpsFzoKSAdfUnJVkIreyxWFlipCHjSQQg", * "AAAABA":"/X8szMU+p+lsTnr56wKjaLgjTMQQkCk8EIWEAilZtQ8", * "AAAAAw":"qxNxxFHzevFntaaPdT0fhhO7tc7pco4+xB/5VRG81hA", * } * }
* Public API for {@link #oneTimeKeysJni()}.
* Note: these keys are to be published on the server. * @return one time keys in string dictionary. * @exception OlmException the failure reason */ public Map> 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.
* The returned data is a JSON-formatted object with the single property * curve25519, which is itself an object mapping key id to * base64-encoded Curve25519 key.
* @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.
* 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) { try { byte[] 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()); } } return result; } /** * Sign a message with the ed25519 fingerprint key for this account.
* 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.
* 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.
* @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.
* 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.
* @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); } olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/java/org/matrix/olm/OlmException.java000066400000000000000000000076671311755073500315170ustar00rootroot00000000000000/* * 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 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_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_UTILITY_CREATION = 500; public static final int EXCEPTION_CODE_UTILITY_VERIFY_SIGNATURE = 501; // 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; } } OlmInboundGroupSession.java000066400000000000000000000322571311755073500334520ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/java/org/matrix/olm/* * 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; /** * Class used to create an inbound Megolm session.
* Counter part of the outbound group session {@link OlmOutboundGroupSession}, this class decrypts the messages sent by the outbound side. * *

Detailed implementation guide is available at Implementing End-to-End Encryption in Matrix clients. */ 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.
* 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.
* 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.
* 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 { try { mNativeId = createNewSessionJni(aSessionKey.getBytes("UTF-8"), isImported); } catch (Exception e) { throw new OlmException(OlmException.EXCEPTION_CODE_INIT_INBOUND_GROUP_SESSION, e.getMessage()); } } } /** * Initialize a new inbound group session and return it to JAVA side.
* 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.
* Public API for {@link #releaseSessionJni()}. */ public void releaseSession(){ if (0 != mNativeId) { releaseSessionJni(); } mNativeId = 0; } /** * Destroy the corresponding OLM inbound group session native object.
* 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.
* @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"); } } 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.
* 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 teh 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"); } } 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.
* 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"); aErrorMsg.append("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.
* 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.
* 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); } olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/java/org/matrix/olm/OlmManager.java000066400000000000000000000044271311755073500311220ustar00rootroot00000000000000/* * 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.
An OlmManager instance must be created at first to enable native library load. *

Detailed implementation guide is available at Implementing End-to-End Encryption in Matrix clients. */ 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.VERSION_NAME; } /** * 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(); } olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/java/org/matrix/olm/OlmMessage.java000066400000000000000000000026271311755073500311340ustar00rootroot00000000000000/* * 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.
* See {@link OlmSession#decryptMessage(OlmMessage)} and {@link OlmSession#encryptMessage(String)}. *
Detailed implementation guide is available at Implementing End-to-End Encryption in Matrix clients. */ 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; } OlmOutboundGroupSession.java000066400000000000000000000244161311755073500336510ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/java/org/matrix/olm/* * 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; /** * Class used to create an outbound a Megolm session.
* 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. * *

Detailed implementation guide is available at Implementing End-to-End Encryption in Matrix clients. */ 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.
* This value uniquely identifies the native outbound group session instance. */ private transient long mNativeId; /** * Constructor.
* Create and save a new session native instance ID and * initialise a new outbound group session.
* @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.
* 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.
* Public API for {@link #releaseSessionJni()}. */ public void releaseSession() { if (0 != mNativeId) { releaseSessionJni(); } mNativeId = 0; } /** * Destroy the corresponding OLM outbound group session native object.
* 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.
* @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.
* 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.
* 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.
* 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 { return new String(sessionKeyJni(), "UTF-8"); } 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.
* 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[] encryptedBuffer = encryptMessageJni(aClearMsg.getBytes("UTF-8")); 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.
* 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.
* 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.
* 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); } olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/java/org/matrix/olm/OlmSession.java000066400000000000000000000446001311755073500311700ustar00rootroot00000000000000/* * 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; /** * Session class used to create Olm sessions in conjunction with {@link OlmAccount} class.
* Olm session is used to encrypt data between devices, especially to create Olm group sessions (see {@link OlmOutboundGroupSession} and {@link OlmInboundGroupSession}).
* 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)}. *

Detailed implementation guide is available at Implementing End-to-End Encryption in Matrix clients. */ 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.
* 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.
* 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.
* Public API for {@link #releaseSessionJni()}. */ public void releaseSession() { if (0 != mNativeId) { releaseSessionJni(); } mNativeId = 0; } /** * Return true the object resources have been released.
* @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.
* @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.
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}).
* 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.
* 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.
* 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.
* 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.
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(); /** * Checks if the PRE_KEY({@link OlmMessage#MESSAGE_TYPE_PRE_KEY}) message is for this in-bound session.
* 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.
* 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.
* 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.
* 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.
* 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[] encryptedMessageBuffer = encryptMessageJni(aClearMsg.getBytes("UTF-8"), encryptedMsgRetValue); 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.
* 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.
* 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 { return new String(decryptMessageJni(aEncryptedMsg), "UTF-8"); } 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.
* 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.
* 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.
* 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.
* 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.
* 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); } olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/java/org/matrix/olm/OlmUtility.java000066400000000000000000000170151311755073500312100ustar00rootroot00000000000000/* * 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.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.
* Public API for {@link #releaseUtilityJni()}. */ public void releaseUtility() { if (0 != mNativeId) { releaseUtilityJni(); } mNativeId = 0; } private native void releaseUtilityJni(); /** * Verify an ed25519 signature.
* 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; 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 { errorMessage = verifyEd25519SignatureJni(aSignature.getBytes("UTF-8"), aFingerprintKey.getBytes("UTF-8"), aMessage.getBytes("UTF-8")); } } catch (Exception e) { Log.e(LOG_TAG, "## verifyEd25519Signature(): failed " + e.getMessage()); errorMessage = e.getMessage(); } 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).
* 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) { try { hashRetValue = new String(sha256Jni(aMessageToHash.getBytes("UTF-8")), "UTF-8"); } catch (Exception e) { Log.e(LOG_TAG, "## sha256(): failed " + e.getMessage()); } } return hashRetValue; } /** * Compute the digest (SHA 256) for the message passed in parameter.
* 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.
* @return true the object resources have been released */ public boolean isReleased() { return (0 == mNativeId); } /** * Build a string-string dictionary from a jsonObject.
* @param jsonObject the object to parse * @return the map */ public static Map toStringMap(JSONObject jsonObject) { if (null != jsonObject) { HashMap map = new HashMap<>(); Iterator 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.
* @param jsonObject the object to parse * @return the map */ public static Map> toStringMapMap(JSONObject jsonObject) { if (null != jsonObject) { HashMap> map = new HashMap<>(); Iterator 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; } } olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/jni/000077500000000000000000000000001311755073500232035ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/jni/Android.mk000066400000000000000000000031331311755073500251140ustar00rootroot00000000000000LOCAL_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_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/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 LOCAL_LDLIBS := -llog include $(BUILD_SHARED_LIBRARY) olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/jni/Application.mk000066400000000000000000000001501311755073500257730ustar00rootroot00000000000000APP_PLATFORM := android-16 APP_ABI := arm64-v8a armeabi-v7a armeabi x86_64 x86 APP_STL := gnustl_staticolm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/jni/olm_account.cpp000066400000000000000000000555701311755073500262260ustar00rootroot00000000000000/* * 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(accountSize)); } else { LOGE("## initializeAccountMemory(): failure - OOM"); } return accountPtr; } /** * Create a new account and return it to JAVA side.
* 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(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().
* 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.
* 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(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.
* The returned data is a JSON-formatted object with the single property * curve25519, which is itself an object mapping key id to * base64-encoded Curve25519 key.
* @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(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(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(result)); } } if (randomBufferPtr) { memset(randomBufferPtr, 0, randomLength); free(randomBufferPtr); } } if (errorMessage) { env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage); } } /** * Get "one time keys".
* 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(result)); } } if (errorMessage) { env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage); } } /** * Sign a message with the ed25519 key (fingerprint) for this account.
* 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(resultSign), static_cast(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.
* @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; 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, NULL))) { 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(pickledLength), static_cast(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(result), static_cast(pickledLength), static_cast(pickledPtr)); pickledDataRetValue = env->NewByteArray(pickledLength); env->SetByteArrayRegion(pickledDataRetValue, 0 , pickledLength, (jbyte*)pickledPtr); } free(pickledPtr); } } // free alloc if (keyPtr) { 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.
* @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; 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, 0))) { 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(pickledLength), static_cast(keyLength)); LOGD(" ## deserializeJni(): pickled=%.*s", static_cast (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(result)); } } // free alloc if (keyPtr) { 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; }olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/jni/olm_account.h000066400000000000000000000041321311755073500256570ustar00rootroot00000000000000/* * 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 _OMLACCOUNT_H #define _OMLACCOUNT_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); // 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 olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/jni/olm_inbound_group_session.cpp000066400000000000000000000566061311755073500312100ustar00rootroot00000000000000/* * 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().
* 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(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.
* 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; 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, 0))) { 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(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(sessionResult)); } } if (sessionKeyPtr) { 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(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(result), static_cast(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(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(encryptedMsgLength), static_cast(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(maxPlainTextLength)); uint32_t messageIndex = 0; // allocate output decrypted message uint8_t *plainTextMsgPtr = static_cast(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(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(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(result), static_cast(length), static_cast(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.
* 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; 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, 0))) { 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(pickledLength), static_cast(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(result), static_cast(pickledLength), static_cast(pickledPtr)); pickledDataRet = env->NewByteArray(pickledLength); env->SetByteArrayRegion(pickledDataRet, 0 , pickledLength, (jbyte*)pickledPtr); } free(pickledPtr); } } // free alloc if (keyPtr) { 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.
* 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; 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, 0))) { 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(pickledLength), static_cast(keyLength)); LOGD(" ## deserializeJni(): pickled=%.*s", static_cast(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(result)); } } // free alloc if (keyPtr) { 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; } olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/jni/olm_inbound_group_session.h000066400000000000000000000040551311755073500306440ustar00rootroot00000000000000/* * 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 _OMLINBOUND_GROUP_SESSION_H #define _OMLINBOUND_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 olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/jni/olm_jni.h000066400000000000000000000040261311755073500250050ustar00rootroot00000000000000/* * 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 _OMLJNI_H #define _OMLJNI_H #include #include #include #include #include #include #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); #ifdef __cplusplus } #endif #endif olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/jni/olm_jni_helper.cpp000066400000000000000000000162551311755073500267060ustar00rootroot00000000000000/* * 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_jni_helper.h" #include "olm/olm.h" #include 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(aRandomSize)); // use the secureRandom class jclass cls = env->FindClass("java/security/SecureRandom"); if (cls) { jobject newObj = 0; jmethodID constructor = env->GetMethodID(cls, "", "()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).
* @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).
* @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).
* @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).
* @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); } olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/jni/olm_jni_helper.h000066400000000000000000000021421311755073500263410ustar00rootroot00000000000000/* * 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_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"; } olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/jni/olm_manager.cpp000066400000000000000000000022671311755073500261770ustar00rootroot00000000000000/* * 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; }olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/jni/olm_manager.h000066400000000000000000000017031311755073500256360ustar00rootroot00000000000000/* * 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 _OMLMANAGER_H #define _OMLMANAGER_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 olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/jni/olm_outbound_group_session.cpp000066400000000000000000000471631311755073500314070ustar00rootroot00000000000000/* * 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().
* 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(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.
* 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(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(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(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(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(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(result), static_cast(result), reinterpret_cast(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.
* 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(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(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(result), static_cast(result), reinterpret_cast(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; 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, NULL))) { 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(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(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(encryptedLength), static_cast(encryptedLength), reinterpret_cast(encryptedMsgPtr)); encryptedMsgRet = env->NewByteArray(encryptedLength); env->SetByteArrayRegion(encryptedMsgRet, 0 , encryptedLength, (jbyte*)encryptedMsgPtr); } free(encryptedMsgPtr); } } // free alloc if (clearMsgPtr) { 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.
* 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; 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, 0))) { 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(pickledLength), static_cast(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(result), static_cast(result), static_cast(pickledPtr)); returnValue = env->NewByteArray(pickledLength); env->SetByteArrayRegion(returnValue, 0 , pickledLength, (jbyte*)pickledPtr); } } free(pickledPtr); } // free alloc if (keyPtr) { 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.
* 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; 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, 0))) { 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(pickledLength), static_cast(keyLength)); LOGD(" ## deserializeJni(): pickled=%.*s", static_cast(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(result)); } } // free alloc if (keyPtr) { 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; } olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/jni/olm_outbound_group_session.h000066400000000000000000000035741311755073500310520ustar00rootroot00000000000000/* * 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 _OMLOUTBOUND_GROUP_SESSION_H #define _OMLOUTBOUND_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 olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/jni/olm_session.cpp000066400000000000000000001101251311755073500262410ustar00rootroot00000000000000/* * 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_session.h" using namespace AndroidOlmSdk; /** * Init memory allocation for a session creation.
* Make sure releaseSessionJni() is called when one is done with the session instance. * @return valid memory allocation, NULL otherwise **/ OlmSession* initializeSessionMemory() { size_t sessionSize = olm_session_size(); OlmSession* sessionPtr = (OlmSession*)malloc(sessionSize); if (sessionPtr) { // init session object sessionPtr = olm_session(sessionPtr); LOGD("## initializeSessionMemory(): success - OLM session size=%lu",static_cast(sessionSize)); } else { LOGE("## initializeSessionMemory(): failure - OOM"); } return sessionPtr; } JNIEXPORT jlong OLM_SESSION_FUNC_DEF(createNewSessionJni)(JNIEnv *env, jobject thiz) { LOGD("## createNewSessionJni(): IN"); OlmSession* accountPtr = initializeSessionMemory(); if (!accountPtr) { LOGE("## initNewAccount(): failure - init session OOM"); env->ThrowNew(env->FindClass("java/lang/Exception"), "init session OOM"); } else { LOGD(" ## createNewSessionJni(): success - accountPtr=%p (jlong)(intptr_t)accountPtr=%lld",accountPtr,(jlong)(intptr_t)accountPtr); } return (jlong)(intptr_t)accountPtr; } JNIEXPORT void OLM_SESSION_FUNC_DEF(releaseSessionJni)(JNIEnv *env, jobject thiz) { LOGD("## releaseSessionJni(): IN"); OlmSession* sessionPtr = getSessionInstanceId(env, thiz); if (!sessionPtr) { LOGE("## releaseSessionJni(): failure - invalid Session ptr=NULL"); } else { olm_clear_session(sessionPtr); // even if free(NULL) does not crash, logs are performed for debug purpose free(sessionPtr); } } // ********************************************************************* // ********************** OUTBOUND SESSION ***************************** // ********************************************************************* /** * Create a new in-bound session for sending/receiving messages from an * incoming PRE_KEY message.
The recipient is defined as the entity * with whom the session is established. * @param aOlmAccountId account instance * @param aTheirIdentityKey the identity key of the recipient * @param aTheirOneTimeKey the one time key of the recipient or an exception is thrown **/ JNIEXPORT void OLM_SESSION_FUNC_DEF(initOutboundSessionJni)(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jbyteArray aTheirIdentityKeyBuffer, jbyteArray aTheirOneTimeKeyBuffer) { OlmSession* sessionPtr = getSessionInstanceId(env, thiz); const char* errorMessage = NULL; OlmAccount* accountPtr = NULL; if (!sessionPtr) { LOGE("## initOutboundSessionJni(): failure - invalid Session ptr=NULL"); errorMessage = "invalid Session ptr=NULL"; } else if (!(accountPtr = (OlmAccount*)aOlmAccountId)) { LOGE("## initOutboundSessionJni(): failure - invalid Account ptr=NULL"); errorMessage = "invalid Account ptr=NULL"; } else if (!aTheirIdentityKeyBuffer || !aTheirOneTimeKeyBuffer) { LOGE("## initOutboundSessionJni(): failure - invalid keys"); errorMessage = "invalid keys"; } else { size_t randomSize = olm_create_outbound_session_random_length(sessionPtr); uint8_t *randomBuffPtr = NULL; LOGD("## initOutboundSessionJni(): randomSize=%lu",static_cast(randomSize)); if ( (0 != randomSize) && !setRandomInBuffer(env, &randomBuffPtr, randomSize)) { LOGE("## initOutboundSessionJni(): failure - random buffer init"); errorMessage = "random buffer init"; } else { jbyte* theirIdentityKeyPtr = NULL; jbyte* theirOneTimeKeyPtr = NULL; // convert identity & one time keys to C strings if (!(theirIdentityKeyPtr = env->GetByteArrayElements(aTheirIdentityKeyBuffer, 0))) { LOGE("## initOutboundSessionJni(): failure - identityKey JNI allocation OOM"); errorMessage = "identityKey JNI allocation OOM"; } else if (!(theirOneTimeKeyPtr = env->GetByteArrayElements(aTheirOneTimeKeyBuffer, 0))) { LOGE("## initOutboundSessionJni(): failure - one time Key JNI allocation OOM"); errorMessage = "one time Key JNI allocation OOM"; } else { size_t theirIdentityKeyLength = (size_t)env->GetArrayLength(aTheirIdentityKeyBuffer); size_t theirOneTimeKeyLength = (size_t)env->GetArrayLength(aTheirOneTimeKeyBuffer); LOGD("## initOutboundSessionJni(): identityKey=%.*s oneTimeKey=%.*s", static_cast(theirIdentityKeyLength), theirIdentityKeyPtr, static_cast(theirOneTimeKeyLength), theirOneTimeKeyPtr); size_t sessionResult = olm_create_outbound_session(sessionPtr, accountPtr, theirIdentityKeyPtr, theirIdentityKeyLength, theirOneTimeKeyPtr, theirOneTimeKeyLength, (void*)randomBuffPtr, randomSize); if (sessionResult == olm_error()) { errorMessage = (const char *)olm_session_last_error(sessionPtr); LOGE("## initOutboundSessionJni(): failure - session creation Msg=%s", errorMessage); } else { LOGD("## initOutboundSessionJni(): success - result=%lu", static_cast(sessionResult)); } } if (theirIdentityKeyPtr) { env->ReleaseByteArrayElements(aTheirIdentityKeyBuffer, theirIdentityKeyPtr, JNI_ABORT); } if (theirOneTimeKeyPtr) { env->ReleaseByteArrayElements(aTheirOneTimeKeyBuffer, theirOneTimeKeyPtr, JNI_ABORT); } if (randomBuffPtr) { memset(randomBuffPtr, 0, randomSize); free(randomBuffPtr); } } } if (errorMessage) { env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage); } } // ********************************************************************* // *********************** INBOUND SESSION ***************************** // ********************************************************************* /** * Create a new in-bound session for sending/receiving messages from an * incoming PRE_KEY message.
* An exception is thrown if the operation fails. * @param aOlmAccountId account instance * @param aOneTimeKeyMsg PRE_KEY message */ JNIEXPORT void OLM_SESSION_FUNC_DEF(initInboundSessionJni)(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jbyteArray aOneTimeKeyMsgBuffer) { const char* errorMessage = NULL; OlmSession *sessionPtr = getSessionInstanceId(env,thiz); OlmAccount *accountPtr = NULL; size_t sessionResult; if (!sessionPtr) { LOGE("## initInboundSessionJni(): failure - invalid Session ptr=NULL"); errorMessage = "invalid Session ptr=NULL"; } else if (!(accountPtr = (OlmAccount*)aOlmAccountId)) { LOGE("## initInboundSessionJni(): failure - invalid Account ptr=NULL"); errorMessage = "invalid Account ptr=NULL"; } else if (!aOneTimeKeyMsgBuffer) { LOGE("## initInboundSessionJni(): failure - invalid message"); errorMessage = "invalid message"; } else { jbyte* messagePtr = env->GetByteArrayElements(aOneTimeKeyMsgBuffer, 0); if (!messagePtr) { LOGE("## initInboundSessionJni(): failure - message JNI allocation OOM"); errorMessage = "message JNI allocation OOM"; } else { size_t messageLength = (size_t)env->GetArrayLength(aOneTimeKeyMsgBuffer); LOGD("## initInboundSessionJni(): messageLength=%lu message=%.*s", static_cast(messageLength), static_cast(messageLength), messagePtr); sessionResult = olm_create_inbound_session(sessionPtr, accountPtr, (void*)messagePtr , messageLength); if (sessionResult == olm_error()) { errorMessage = olm_session_last_error(sessionPtr); LOGE("## initInboundSessionJni(): failure - init inbound session creation Msg=%s", errorMessage); } else { LOGD("## initInboundSessionJni(): success - result=%lu", static_cast(sessionResult)); } // free local alloc env->ReleaseByteArrayElements(aOneTimeKeyMsgBuffer, messagePtr, JNI_ABORT); } } if (errorMessage) { env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage); } } /** * Create a new in-bound session for sending/receiving messages from an * incoming PRE_KEY message based on the recipient identity key.
* An exception is thrown if the operation fails. * @param aOlmAccountId account instance * @param aTheirIdentityKey the identity key of the recipient * @param aOneTimeKeyMsg encrypted message */ JNIEXPORT void OLM_SESSION_FUNC_DEF(initInboundSessionFromIdKeyJni)(JNIEnv *env, jobject thiz, jlong aOlmAccountId, jbyteArray aTheirIdentityKeyBuffer, jbyteArray aOneTimeKeyMsgBuffer) { const char* errorMessage = NULL; OlmSession *sessionPtr = getSessionInstanceId(env, thiz); OlmAccount *accountPtr = NULL; jbyte *messagePtr = NULL; jbyte *theirIdentityKeyPtr = NULL; size_t sessionResult; if (!sessionPtr) { LOGE("## initInboundSessionFromIdKeyJni(): failure - invalid Session ptr=NULL"); errorMessage = "invalid Session ptr=NULL"; } else if (!(accountPtr = (OlmAccount*)aOlmAccountId)) { LOGE("## initInboundSessionFromIdKeyJni(): failure - invalid Account ptr=NULL"); errorMessage = "invalid Account ptr=NULL"; } else if (!aTheirIdentityKeyBuffer) { LOGE("## initInboundSessionFromIdKeyJni(): failure - invalid theirIdentityKey"); errorMessage = "invalid theirIdentityKey"; } else if (!aOneTimeKeyMsgBuffer) { LOGE("## initInboundSessionJni(): failure - invalid one time key message"); errorMessage = "invalid invalid one time key message"; } else if (!(messagePtr = env->GetByteArrayElements(aOneTimeKeyMsgBuffer, 0))) { LOGE("## initInboundSessionFromIdKeyJni(): failure - message JNI allocation OOM"); errorMessage = "message JNI allocation OOM"; } else if(!(theirIdentityKeyPtr = env->GetByteArrayElements(aTheirIdentityKeyBuffer, 0))) { LOGE("## initInboundSessionFromIdKeyJni(): failure - theirIdentityKey JNI allocation OOM"); errorMessage = "theirIdentityKey JNI allocation OOM"; } else { size_t messageLength = (size_t)env->GetArrayLength(aOneTimeKeyMsgBuffer); size_t theirIdentityKeyLength = (size_t)env->GetArrayLength(aTheirIdentityKeyBuffer); LOGD("## initInboundSessionFromIdKeyJni(): message=%.*s messageLength=%lu", static_cast(messageLength), messagePtr, static_cast(messageLength)); sessionResult = olm_create_inbound_session_from(sessionPtr, accountPtr, theirIdentityKeyPtr, theirIdentityKeyLength, (void*)messagePtr , messageLength); if (sessionResult == olm_error()) { errorMessage = (const char *)olm_session_last_error(sessionPtr); LOGE("## initInboundSessionFromIdKeyJni(): failure - init inbound session creation Msg=%s", errorMessage); } else { LOGD("## initInboundSessionFromIdKeyJni(): success - result=%lu", static_cast(sessionResult)); } } // free local alloc if (messagePtr) { env->ReleaseByteArrayElements(aOneTimeKeyMsgBuffer, messagePtr, JNI_ABORT); } if (theirIdentityKeyPtr) { env->ReleaseByteArrayElements(aTheirIdentityKeyBuffer, theirIdentityKeyPtr, JNI_ABORT); } if (errorMessage) { env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage); } } /** * Checks if the PRE_KEY message is for this in-bound session.
* This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY). * @param aOneTimeKeyMsg PRE KEY message * @return true if the PRE_KEY message matches */ JNIEXPORT jboolean OLM_SESSION_FUNC_DEF(matchesInboundSessionJni)(JNIEnv *env, jobject thiz, jbyteArray aOneTimeKeyMsgBuffer) { jboolean retCode = JNI_FALSE; OlmSession *sessionPtr = getSessionInstanceId(env, thiz); jbyte *messagePtr = NULL; if (!sessionPtr) { LOGE("## matchesInboundSessionJni(): failure - invalid Session ptr=NULL"); } else if (!aOneTimeKeyMsgBuffer) { LOGE("## matchesInboundSessionJni(): failure - invalid one time key message"); } else if (!(messagePtr = env->GetByteArrayElements(aOneTimeKeyMsgBuffer, 0))) { LOGE("## matchesInboundSessionJni(): failure - one time key JNI allocation OOM"); } else { size_t messageLength = (size_t)env->GetArrayLength(aOneTimeKeyMsgBuffer); size_t matchResult = olm_matches_inbound_session(sessionPtr, (void*)messagePtr , messageLength); //if(matchResult == olm_error()) { // for now olm_matches_inbound_session() returns 1 when it succeeds, otherwise 1- or 0 if (matchResult != 1) { LOGE("## matchesInboundSessionJni(): failure - no match Msg=%s",(const char *)olm_session_last_error(sessionPtr)); } else { retCode = JNI_TRUE; LOGD("## matchesInboundSessionJni(): success - result=%lu", static_cast(matchResult)); } } // free local alloc if (messagePtr) { env->ReleaseByteArrayElements(aOneTimeKeyMsgBuffer, messagePtr, JNI_ABORT); } return retCode; } /** * Checks if the PRE_KEY message is for this in-bound session based on the sender identity key.
* This API may be used to process a "m.room.encrypted" event when type = 1 (PRE_KEY). * @param aTheirIdentityKey the identity key of the sender * @param aOneTimeKeyMsg PRE KEY message * @return true if the PRE_KEY message matches. */ JNIEXPORT jboolean JNICALL OLM_SESSION_FUNC_DEF(matchesInboundSessionFromIdKeyJni)(JNIEnv *env, jobject thiz, jbyteArray aTheirIdentityKeyBuffer, jbyteArray aOneTimeKeyMsgBuffer) { jboolean retCode = JNI_FALSE; OlmSession *sessionPtr = getSessionInstanceId(env, thiz); jbyte *messagePtr = NULL; jbyte *theirIdentityKeyPtr = NULL; if (!sessionPtr) { LOGE("## matchesInboundSessionFromIdKeyJni(): failure - invalid Session ptr=NULL"); } else if (!aTheirIdentityKeyBuffer) { LOGE("## matchesInboundSessionFromIdKeyJni(): failure - invalid theirIdentityKey"); } else if (!(theirIdentityKeyPtr = env->GetByteArrayElements(aTheirIdentityKeyBuffer, 0))) { LOGE("## matchesInboundSessionFromIdKeyJni(): failure - theirIdentityKey JNI allocation OOM"); } else if (!aOneTimeKeyMsgBuffer) { LOGE("## matchesInboundSessionFromIdKeyJni(): failure - invalid one time key message"); } else if (!(messagePtr = env->GetByteArrayElements(aOneTimeKeyMsgBuffer, 0))) { LOGE("## matchesInboundSessionFromIdKeyJni(): failure - one time key JNI allocation OOM"); } else { size_t identityKeyLength = (size_t)env->GetArrayLength(aTheirIdentityKeyBuffer); size_t messageLength = (size_t)env->GetArrayLength(aOneTimeKeyMsgBuffer); size_t matchResult = olm_matches_inbound_session_from(sessionPtr, (void const *)theirIdentityKeyPtr, identityKeyLength, (void*)messagePtr , messageLength); //if(matchResult == olm_error()) { // for now olm_matches_inbound_session() returns 1 when it succeeds, otherwise 1- or 0 if (matchResult != 1) { LOGE("## matchesInboundSessionFromIdKeyJni(): failure - no match Msg=%s",(const char *)olm_session_last_error(sessionPtr)); } else { retCode = JNI_TRUE; LOGD("## matchesInboundSessionFromIdKeyJni(): success - result=%lu", static_cast(matchResult)); } } // free local alloc if (theirIdentityKeyPtr) { env->ReleaseByteArrayElements(aTheirIdentityKeyBuffer, theirIdentityKeyPtr, JNI_ABORT); } if (messagePtr) { env->ReleaseByteArrayElements(aOneTimeKeyMsgBuffer, messagePtr, JNI_ABORT); } return retCode; } /** * Encrypt a message using the session.
* An exception is thrown if the operation fails. * @param aClearMsg clear text message * @param [out] aEncryptedMsg ciphered message * @return the encrypted message */ JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(encryptMessageJni)(JNIEnv *env, jobject thiz, jbyteArray aClearMsgBuffer, jobject aEncryptedMsg) { jbyteArray encryptedMsgRet = 0; const char* errorMessage = NULL; OlmSession *sessionPtr = getSessionInstanceId(env, thiz); jbyte *clearMsgPtr = NULL; jclass encryptedMsgJClass = 0; jfieldID typeMsgFieldId; LOGD("## encryptMessageJni(): IN "); if (!sessionPtr) { LOGE("## encryptMessageJni(): failure - invalid Session ptr=NULL"); errorMessage = "invalid Session ptr=NULL"; } else if (!aClearMsgBuffer) { LOGE("## encryptMessageJni(): failure - invalid clear message"); errorMessage = "invalid clear message"; } else if (!aEncryptedMsg) { LOGE("## encryptMessageJni(): failure - invalid encrypted message"); } else if (!(clearMsgPtr = env->GetByteArrayElements(aClearMsgBuffer, 0))) { LOGE("## encryptMessageJni(): failure - clear message JNI allocation OOM"); errorMessage = "clear message JNI allocation OOM"; } else if (!(encryptedMsgJClass = env->GetObjectClass(aEncryptedMsg))) { LOGE("## encryptMessageJni(): failure - unable to get crypted message class"); errorMessage = "unable to get crypted message class"; } else if (!(typeMsgFieldId = env->GetFieldID(encryptedMsgJClass,"mType","J"))) { LOGE("## encryptMessageJni(): failure - unable to get message type field"); errorMessage = "unable to get message type field"; } else { // get message type size_t messageType = olm_encrypt_message_type(sessionPtr); uint8_t *randomBuffPtr = NULL; // compute random buffer // Note: olm_encrypt_random_length() can return 0, which means // it just does not need new random data to encrypt a new message size_t randomLength = olm_encrypt_random_length(sessionPtr); LOGD("## encryptMessageJni(): randomLength=%lu", static_cast(randomLength)); if ((0 != randomLength) && !setRandomInBuffer(env, &randomBuffPtr, randomLength)) { LOGE("## encryptMessageJni(): failure - random buffer init"); errorMessage = "random buffer init"; } else { // alloc buffer for encrypted message size_t clearMsgLength = (size_t)env->GetArrayLength(aClearMsgBuffer); size_t encryptedMsgLength = olm_encrypt_message_length(sessionPtr, clearMsgLength); void *encryptedMsgPtr = malloc(encryptedMsgLength*sizeof(uint8_t)); if (!encryptedMsgPtr) { LOGE("## encryptMessageJni(): failure - encryptedMsgPtr buffer OOM"); errorMessage = "encryptedMsgPtr buffer OOM"; } else { if (0 == randomLength) { LOGW("## encryptMessageJni(): random buffer is not required"); } LOGD("## encryptMessageJni(): messageType=%lu randomLength=%lu clearMsgLength=%lu encryptedMsgLength=%lu",static_cast(messageType),static_cast(randomLength), static_cast(clearMsgLength), static_cast(encryptedMsgLength)); // encrypt message size_t result = olm_encrypt(sessionPtr, (void const *)clearMsgPtr, clearMsgLength, randomBuffPtr, randomLength, encryptedMsgPtr, encryptedMsgLength); if (result == olm_error()) { errorMessage = (const char *)olm_session_last_error(sessionPtr); LOGE("## encryptMessageJni(): failure - Msg=%s", errorMessage); } else { // update message type: PRE KEY or normal env->SetLongField(aEncryptedMsg, typeMsgFieldId, (jlong)messageType); encryptedMsgRet = env->NewByteArray(encryptedMsgLength); env->SetByteArrayRegion(encryptedMsgRet, 0 , encryptedMsgLength, (jbyte*)encryptedMsgPtr); LOGD("## encryptMessageJni(): success - result=%lu Type=%lu encryptedMsg=%.*s", static_cast(result), static_cast(messageType), static_cast(result), (const char*)encryptedMsgPtr); } free(encryptedMsgPtr); } memset(randomBuffPtr, 0, randomLength); free(randomBuffPtr); } } // free alloc if (clearMsgPtr) { env->ReleaseByteArrayElements(aClearMsgBuffer, clearMsgPtr, JNI_ABORT); } if (errorMessage) { env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage); } return encryptedMsgRet; } /** * Decrypt a message using the session.
* An exception is thrown if the operation fails. * @param aEncryptedMsg message to decrypt * @return decrypted message if operation succeed */ JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(decryptMessageJni)(JNIEnv *env, jobject thiz, jobject aEncryptedMsg) { const char* errorMessage = NULL; jbyteArray decryptedMsgRet = 0; jclass encryptedMsgJClass = 0; jstring encryptedMsgJstring = 0; // <= obtained from encryptedMsgFieldId // field IDs jfieldID encryptedMsgFieldId; jfieldID typeMsgFieldId; // ptrs OlmSession *sessionPtr = getSessionInstanceId(env, thiz); const char *encryptedMsgPtr = NULL; // <= obtained from encryptedMsgJstring uint8_t *plainTextMsgPtr = NULL; char *tempEncryptedPtr = NULL; LOGD("## decryptMessageJni(): IN - OlmSession"); if (!sessionPtr) { LOGE("## decryptMessageJni(): failure - invalid Session ptr=NULL"); errorMessage = "invalid Session ptr=NULL"; } else if (!aEncryptedMsg) { LOGE("## decryptMessageJni(): failure - invalid encrypted message"); errorMessage = "invalid encrypted message"; } else if (!(encryptedMsgJClass = env->GetObjectClass(aEncryptedMsg))) { LOGE("## decryptMessageJni(): failure - unable to get encrypted message class"); errorMessage = "unable to get encrypted message class"; } else if (!(encryptedMsgFieldId = env->GetFieldID(encryptedMsgJClass,"mCipherText","Ljava/lang/String;"))) { LOGE("## decryptMessageJni(): failure - unable to get message field"); errorMessage = "unable to get message field"; } else if (!(typeMsgFieldId = env->GetFieldID(encryptedMsgJClass,"mType","J"))) { LOGE("## decryptMessageJni(): failure - unable to get message type field"); errorMessage = "unable to get message type field"; } else if (!(encryptedMsgJstring = (jstring)env->GetObjectField(aEncryptedMsg, encryptedMsgFieldId))) { LOGE("## decryptMessageJni(): failure - JNI encrypted object "); errorMessage = "JNI encrypted object"; } else if (!(encryptedMsgPtr = env->GetStringUTFChars(encryptedMsgJstring, 0))) { LOGE("## decryptMessageJni(): failure - encrypted message JNI allocation OOM"); errorMessage = "encrypted message JNI allocation OOM"; } else { // get message type size_t encryptedMsgType = (size_t)env->GetLongField(aEncryptedMsg, typeMsgFieldId); // get encrypted message length size_t encryptedMsgLength = (size_t)env->GetStringUTFLength(encryptedMsgJstring); // create a dedicated temp buffer to be used in next Olm API calls tempEncryptedPtr = static_cast(malloc(encryptedMsgLength*sizeof(uint8_t))); memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength); LOGD("## decryptMessageJni(): MsgType=%lu encryptedMsgLength=%lu encryptedMsg=%.*s",static_cast(encryptedMsgType),static_cast(encryptedMsgLength), static_cast(encryptedMsgLength), encryptedMsgPtr); // get max plaintext length size_t maxPlainTextLength = olm_decrypt_max_plaintext_length(sessionPtr, static_cast(encryptedMsgType), static_cast(tempEncryptedPtr), encryptedMsgLength); // Note: tempEncryptedPtr is destroyed by olm_decrypt_max_plaintext_length() if (maxPlainTextLength == olm_error()) { errorMessage = (const char *)olm_session_last_error(sessionPtr); LOGE("## decryptMessageJni(): failure - olm_decrypt_max_plaintext_length Msg=%s", errorMessage); } else { LOGD("## decryptMessageJni(): maxPlaintextLength=%lu",static_cast(maxPlainTextLength)); // allocate output decrypted message plainTextMsgPtr = static_cast(malloc(maxPlainTextLength*sizeof(uint8_t))); // decrypt, but before reload encrypted buffer (previous one was destroyed) memcpy(tempEncryptedPtr, encryptedMsgPtr, encryptedMsgLength); size_t plaintextLength = olm_decrypt(sessionPtr, encryptedMsgType, (void*)tempEncryptedPtr, encryptedMsgLength, plainTextMsgPtr, maxPlainTextLength); if (plaintextLength == olm_error()) { errorMessage = (const char *)olm_session_last_error(sessionPtr); LOGE("## decryptMessageJni(): failure - olm_decrypt Msg=%s", errorMessage); } else { decryptedMsgRet = env->NewByteArray(plaintextLength); env->SetByteArrayRegion(decryptedMsgRet, 0 , plaintextLength, (jbyte*)plainTextMsgPtr); LOGD(" ## decryptMessageJni(): UTF-8 Conversion - decrypted returnedLg=%lu OK",static_cast(plaintextLength)); } } } // free alloc if (encryptedMsgPtr) { env->ReleaseStringUTFChars(encryptedMsgJstring, encryptedMsgPtr); } if (tempEncryptedPtr) { free(tempEncryptedPtr); } if (plainTextMsgPtr) { free(plainTextMsgPtr); } if (errorMessage) { env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage); } return decryptedMsgRet; } /** * Get the session identifier for this session. * An exception is thrown if the operation fails. * @return the session identifier */ JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(getSessionIdentifierJni)(JNIEnv *env, jobject thiz) { const char* errorMessage = NULL; jbyteArray returnValue = 0; LOGD("## getSessionIdentifierJni(): IN "); OlmSession *sessionPtr = getSessionInstanceId(env, thiz); if (!sessionPtr) { LOGE("## getSessionIdentifierJni(): failure - invalid Session ptr=NULL"); errorMessage = "invalid Session ptr=NULL"; } else { // get the size to alloc to contain the id size_t lengthSessionId = olm_session_id_length(sessionPtr); LOGD("## getSessionIdentifierJni(): lengthSessionId=%lu",static_cast(lengthSessionId)); void *sessionIdPtr = malloc(lengthSessionId*sizeof(uint8_t)); if (!sessionIdPtr) { LOGE("## getSessionIdentifierJni(): failure - identifier allocation OOM"); errorMessage = "identifier allocation OOM"; } else { size_t result = olm_session_id(sessionPtr, sessionIdPtr, lengthSessionId); if (result == olm_error()) { errorMessage = (const char *)olm_session_last_error(sessionPtr); LOGE("## getSessionIdentifierJni(): failure - get session identifier failure Msg=%s", errorMessage); } else { LOGD("## getSessionIdentifierJni(): success - result=%lu sessionId=%.*s",static_cast(result), static_cast(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; } /** * Serialize and encrypt session instance.
* 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. **/ JNIEXPORT jbyteArray OLM_SESSION_FUNC_DEF(serializeJni)(JNIEnv *env, jobject thiz, jbyteArray aKeyBuffer) { const char* errorMessage = NULL; jbyteArray returnValue = 0; jbyte* keyPtr = NULL; OlmSession* sessionPtr = getSessionInstanceId(env, thiz); LOGD("## 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, 0))) { LOGE(" ## serializeJni(): failure - keyPtr JNI allocation OOM"); errorMessage = "ikeyPtr JNI allocation OOM"; } else { size_t pickledLength = olm_pickle_session_length(sessionPtr); size_t keyLength = (size_t)env->GetArrayLength(aKeyBuffer); LOGD(" ## serializeJni(): pickledLength=%lu keyLength=%lu",static_cast(pickledLength), static_cast(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_session(sessionPtr, (void const *)keyPtr, keyLength, (void*)pickledPtr, pickledLength); if (result == olm_error()) { errorMessage = olm_session_last_error(sessionPtr); LOGE(" ## serializeJni(): failure - olm_pickle_session() Msg=%s", errorMessage); } else { LOGD(" ## serializeJni(): success - result=%lu pickled=%.*s", static_cast(result), static_cast(pickledLength), static_cast(pickledPtr)); returnValue = env->NewByteArray(pickledLength); env->SetByteArrayRegion(returnValue, 0 , pickledLength, (jbyte*)pickledPtr); } free(pickledPtr); } } // free alloc if (keyPtr) { 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.
* 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_SESSION_FUNC_DEF(deserializeJni)(JNIEnv *env, jobject thiz, jbyteArray aSerializedDataBuffer, jbyteArray aKeyBuffer) { const char* errorMessage = NULL; OlmSession* sessionPtr = initializeSessionMemory(); jbyte* keyPtr = NULL; jbyte* pickledPtr = NULL; LOGD("## deserializeJni(): IN"); if (!sessionPtr) { 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, 0))) { 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(pickledLength), static_cast(keyLength)); LOGD(" ## deserializeJni(): pickled=%.*s",static_cast(pickledLength), (char const *)pickledPtr); size_t result = olm_unpickle_session(sessionPtr, (void const *)keyPtr, keyLength, (void*)pickledPtr, pickledLength); if (result == olm_error()) { errorMessage = olm_session_last_error(sessionPtr); LOGE(" ## deserializeJni(): failure - olm_unpickle_account() Msg=%s", errorMessage); } else { LOGD(" ## initJni(): success - result=%lu ", static_cast(result)); } } // free alloc if (keyPtr) { env->ReleaseByteArrayElements(aKeyBuffer, keyPtr, JNI_ABORT); } if (pickledPtr) { env->ReleaseByteArrayElements(aSerializedDataBuffer, pickledPtr, JNI_ABORT); } if (errorMessage) { if (sessionPtr) { olm_clear_session(sessionPtr); free(sessionPtr); } env->ThrowNew(env->FindClass("java/lang/Exception"), errorMessage); } return (jlong)(intptr_t)sessionPtr; }olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/jni/olm_session.h000066400000000000000000000050201311755073500257030ustar00rootroot00000000000000/* * 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 _OMLSESSION_H #define _OMLSESSION_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); // 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 olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/jni/olm_utility.cpp000066400000000000000000000167541311755073500262760ustar00rootroot00000000000000/* * 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(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; 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, 0))) { 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(signatureLength),static_cast(keyLength),static_cast(messageLength)); LOGD(" ## verifyEd25519SignatureJni(): key=%.*s", static_cast(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(result)); } } // free alloc if (signaturePtr) { env->ReleaseByteArrayElements(aSignatureBuffer, signaturePtr, JNI_ABORT); } if (keyPtr) { env->ReleaseByteArrayElements(aKeyBuffer, keyPtr, JNI_ABORT); } if (messagePtr) { env->ReleaseByteArrayElements(aMessageBuffer, messagePtr, JNI_ABORT); } return errorMessageRetValue; } /** * Compute the digest (SHA 256) for the message passed in parameter.
* 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; 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, 0))) { 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(result), static_cast(result), (char*)hashValuePtr); sha256Ret = env->NewByteArray(result); env->SetByteArrayRegion(sha256Ret, 0 , result, (jbyte*)hashValuePtr); } free(hashValuePtr); } } if (messagePtr) { env->ReleaseByteArrayElements(aMessageToHashBuffer, messagePtr, JNI_ABORT); } return sha256Ret; }olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/jni/olm_utility.h000066400000000000000000000024361311755073500257330ustar00rootroot00000000000000/* * 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 _OMLUTILITY_H #define _OMLUTILITY_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 olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/res/000077500000000000000000000000001311755073500232145ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/res/values/000077500000000000000000000000001311755073500245135ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/android/olm-sdk/src/main/res/values/strings.xml000066400000000000000000000001051311755073500267220ustar00rootroot00000000000000 OlmSdk olm-2.2.2+git20170526.0fd768e+dfsg/android/settings.gradle000066400000000000000000000000231311755073500223550ustar00rootroot00000000000000include ':olm-sdk' olm-2.2.2+git20170526.0fd768e+dfsg/common.mk000066400000000000000000000000421311755073500175370ustar00rootroot00000000000000 MAJOR := 2 MINOR := 2 PATCH := 2 olm-2.2.2+git20170526.0fd768e+dfsg/docs/000077500000000000000000000000001311755073500166525ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/docs/double_ratchet.svg000066400000000000000000001043721311755073500223660ustar00rootroot00000000000000 image/svg+xml Initial Shared Secret Secret HKDF Root Key Chain Key Info "OLM_ROOT" Salt "" Key Data HMAC Chain Key "\x02" "\x01" Key Data HMAC Message Key Secret Salt "" Info "OLM_KEYS" HKDF Cipher Key Mac Key IV Secret HKDF Root Key Chain Key Info "OLM_RATCHET" Salt Alice's Chain Bob's Chain Bob's Key Alice's Key ECDH Shared Secret olm-2.2.2+git20170526.0fd768e+dfsg/docs/megolm.rst000066400000000000000000000351171311755073500206730ustar00rootroot00000000000000.. 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 `_. .. _`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 olm-2.2.2+git20170526.0fd768e+dfsg/docs/olm.rst000066400000000000000000000345701311755073500202040ustar00rootroot00000000000000Olm: A Cryptographic Ratchet ============================ An implementation of the double cryptographic ratchet described by https://whispersystems.org/docs/specifications/doubleratchet/. Notation -------- This document uses :math:`\parallel` to represent string concatenation. When :math:`\parallel` appears on the right hand side of an :math:`=` it means that the inputs are concatenated. When :math:`\parallel` appears on the left hand side of an :math:`=` it means that the output is split. When this document uses :math:`ECDH\left(K_A,\,K_B\right)` it means that each party computes a Diffie-Hellman agreement using their private key and the remote party's public key. So party :math:`A` computes :math:`ECDH\left(K_B_public,\,K_A_private\right)` and party :math:`B` computes :math:`ECDH\left(K_A_public,\,K_B_private\right)`. Where this document uses :math:`HKDF\left(salt,\,IKM,\,info,\,L\right)` it refers to the `HMAC-based key derivation function`_ with a salt value of :math:`salt`, input key material of :math:`IKM`, context string :math:`info`, and output keying material length of :math:`L` bytes. The Olm Algorithm ----------------- Initial setup ~~~~~~~~~~~~~ The setup takes four Curve25519_ inputs: Identity keys for Alice and Bob, :math:`I_A` and :math:`I_B`, and one-time keys for Alice and Bob, :math:`E_A` and :math:`E_B`. A shared secret, :math:`S`, is generated using `Triple Diffie-Hellman`_. The initial 256 bit root key, :math:`R_0`, and 256 bit chain key, :math:`C_{0,0}`, are derived from the shared secret using an HMAC-based Key Derivation Function using SHA-256_ as the hash function (HKDF-SHA-256_) with default salt and ``"OLM_ROOT"`` as the info. .. math:: \begin{align} S&=ECDH\left(I_A,\,E_B\right)\;\parallel\;ECDH\left(E_A,\,I_B\right)\; \parallel\;ECDH\left(E_A,\,E_B\right)\\ R_0\;\parallel\;C_{0,0}&= HKDF\left(0,\,S,\,\text{"OLM\_ROOT"},\,64\right) \end{align} Advancing the root key ~~~~~~~~~~~~~~~~~~~~~~ Advancing a root key takes the previous root key, :math:`R_{i-1}`, and two Curve25519 inputs: the previous ratchet key, :math:`T_{i-1}`, and the current ratchet key :math:`T_i`. The even ratchet keys are generated by Alice. The odd ratchet keys are generated by Bob. A shared secret is generated using Diffie-Hellman on the ratchet keys. The next root key, :math:`R_i`, and chain key, :math:`C_{i,0}`, are derived from the shared secret using HKDF-SHA-256_ using :math:`R_{i-1}` as the salt and ``"OLM_RATCHET"`` as the info. .. math:: \begin{align} R_i\;\parallel\;C_{i,0}&=HKDF\left( R_{i-1},\, ECDH\left(T_{i-1},\,T_i\right),\, \text{"OLM\_RATCHET"},\, 64 \right) \end{align} Advancing the chain key ~~~~~~~~~~~~~~~~~~~~~~~ Advancing a chain key takes the previous chain key, :math:`C_{i,j-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://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 olm-2.2.2+git20170526.0fd768e+dfsg/docs/signing.rst000066400000000000000000000132221311755073500210420ustar00rootroot00000000000000.. 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 need for our users Alice and Bob to verify that they are in fact communicating with each other, rather than a man-in-the-middle. Typically this requires an out-of-band process in which Alice and Bob verify that they have the correct public keys for each other. For example, this might be done via physical presence or via a voice call. In the basic `Olm `_ protocol, it is sufficient to compare the public Curve25519 identity keys. As a naive example, Alice would meet Bob and ensure that the identity key she downloaded from the key server matched that shown by his device. This prevents the eavesdropper Eve from decrypting any messages sent from Alice to Bob, or from masquerading as Bob to send messages to Alice: she has neither Alice's nor Bob's private identity key, so cannot successfully complete the triple-DH calculation to compute the shared secret, :math:`S`, which in turn prevents her decrypting intercepted messages, or from creating new messages with valid MACs. Obviously, for protection to be complete, Bob must similarly verify Alice's key. However, the use of the Curve25519 key as the "fingerprint" in this way makes it difficult to carry out signing operations. For instance, it may be useful to cross-sign identity keys for different devices, or, as discussed below, to sign one-time keys. Curve25519 keys are intended for use in DH calculations, and their use to calculate signatures is non-trivial. The solution adopted in this library is to generate a signing key for each user. This is an `Ed25519`_ keypair, which is used to calculate a signature on an object including both the public Ed25519 signing key and the public Curve25519 identity key. It is then the **public Ed25519 signing key** which is used as the device fingerprint which Alice and Bob verify with each other. By verifying the signatures on the key object, Alice and Bob then get the same level of assurance about the ownership of the Curve25519 identity keys as if they had compared those directly. Signing one-time keys --------------------- The Olm protocol requires users to publish a set of one-time keys to a key server. To establish an Olm session, the originator downloads a key for the recipient from this server. The decision of whether to sign these one-time keys is left to the application. There are both advantages and disadvantages to doing so. Consider the scenario where one-time keys are unsigned. Alice wants to initiate an Olm session with Bob. Bob uploads his one-time keys, :math:`E_B`, but Eve 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 :math:`S`, where: .. math:: S = ECDH\left(I_A,\,E_E\right)\;\parallel\;ECDH\left(E_A,\,I_B\right)\; \parallel\;ECDH\left(E_A,\,E_E\right) Eve cannot decrypt the message because she does not have the private parts of either :math:`E_A` nor :math:`I_B`, so cannot calculate :math:`ECDH\left(E_A,\,I_B\right)`. However, suppose she later compromises Bob's identity key :math:`I_B`. This would give her the ability to decrypt any pre-key messages sent to Bob using the compromised one-time keys, and is thus a problematic loss of forward secrecy. If Bob signs his keys with his Ed25519 signing key (and Alice verifies the signature before using them), this problem is avoided. On the other hand, signing the one-time keys leads to a reduction in deniability. Recall that the shared secret is calculated as follows: .. math:: S = ECDH\left(I_A,\,E_B\right)\;\parallel\;ECDH\left(E_A,\,I_B\right)\; \parallel\;ECDH\left(E_A,\,E_B\right) If keys are unsigned, a forger can make up values of :math:`E_A` and :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 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 a conversation between the two of them, rather than constructed by a forger. If :math:`E_B` is signed, it is no longer possible to construct arbitrary transcripts. Given a transcript and Alice and Bob's identity keys, we can now show that at least one of Alice or Bob was involved in the conversation, because the ability to calculate :math:`ECDH\left(I_A,\,E_B\right)` requires knowledge of the private parts of either :math:`I_A` (proving Alice's involvement) or :math:`E_B` (proving Bob's involvement, via the signature). Note that it remains impossible to show that *both* Alice and Bob were involved. In conclusion, applications should consider whether to sign one-time keys based on the trade-off between forward secrecy and deniability. License ------- This document is licensed under the `Apache License, Version 2.0 `_. Feedback -------- Questions and feedback can be sent to richard at matrix.org. .. _`Ed25519`: http://ed25519.cr.yp.to/ olm-2.2.2+git20170526.0fd768e+dfsg/fuzzers/000077500000000000000000000000001311755073500174325ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/fuzzers/README.rst000066400000000000000000000017431311755073500211260ustar00rootroot00000000000000Fuzzers ======= This directory contains a collection of fuzzing tools. Each tests a different entry point to the code. Usage notes: 1. Install AFL: .. code:: apt-get install afl 2. Build the fuzzers: .. code:: make fuzzers 3. Some of the tests (eg ``fuzz_decrypt`` and ``fuzz_group_decrypt``) require a session file. You can use the ones generated by the python test script (``python/test.sh``). 4. Make some work directories: .. code:: mkdir -p fuzzing/in fuzzing/out 5. Generate starting input: .. code:: echo "Test" > fuzzing/in/test 6. Run the test under ``afl-fuzz``: .. code:: afl-fuzz -i fuzzing/in -o fuzzing/out -- \ ./build/fuzzers/fuzz_ [] 7. To resume with the data produced by an earlier run: .. code:: afl-fuzz -i- -o existing_output_dir [...etc...] 8. If it shows failures, pipe the failure case into ``./build/fuzzers/debug_``, fix, and repeat. olm-2.2.2+git20170526.0fd768e+dfsg/fuzzers/fuzz_decode_message.cpp000066400000000000000000000007021311755073500241420ustar00rootroot00000000000000#include "olm/message.hh" #include "fuzzing.hh" int main(int argc, const char *argv[]) { 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) ); olm::MessageReader * reader = new olm::MessageReader; decode_message(*reader, message_buffer, message_length, 8); free(message_buffer); delete reader; } olm-2.2.2+git20170526.0fd768e+dfsg/fuzzers/fuzz_decrypt.cpp000066400000000000000000000035141311755073500226710ustar00rootroot00000000000000#include "olm/olm.hh" #include "fuzzing.hh" int main(int argc, const char *argv[]) { size_t ignored; if (argc <= 3) { const char * message = "Usage: decrypt: " " \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) ); int message_type = atoi(argv[3]); 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_session_size()]; OlmSession * session = olm_session(session_memory); check_session(session, "Error unpickling session", olm_unpickle_session( session, key, key_length, session_buffer, session_length )); size_t max_length = check_session( session, "Error getting plaintext length", olm_decrypt_max_plaintext_length( session, message_type, tmp_buffer, message_length ) ); uint8_t plaintext[max_length]; size_t length = check_session( session, "Error decrypting message", olm_decrypt( session, message_type, message_buffer, message_length, plaintext, max_length ) ); ignored = write(STDOUT_FILENO, plaintext, length); ignored = write(STDOUT_FILENO, "\n", 1); return ignored; } olm-2.2.2+git20170526.0fd768e+dfsg/fuzzers/fuzz_group_decrypt.cpp000066400000000000000000000040231311755073500241010ustar00rootroot00000000000000#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 \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; } olm-2.2.2+git20170526.0fd768e+dfsg/fuzzers/fuzz_unpickle_account.cpp000066400000000000000000000006671311755073500245530ustar00rootroot00000000000000#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; } olm-2.2.2+git20170526.0fd768e+dfsg/fuzzers/fuzz_unpickle_session.cpp000066400000000000000000000006671311755073500246020ustar00rootroot00000000000000#include "olm/session.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::Session * session = new olm::Session; unpickle(pickle_buffer, pickle_buffer + pickle_length, *session); free(pickle_buffer); delete session; } olm-2.2.2+git20170526.0fd768e+dfsg/fuzzers/include/000077500000000000000000000000001311755073500210555ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/fuzzers/include/fuzzing.hh000066400000000000000000000037311311755073500230760ustar00rootroot00000000000000#include "olm/olm.hh" #include #include #include #include #include #include #include #include ssize_t read_file( int fd, uint8_t **buffer ) { size_t buffer_size = 4096; uint8_t * current_buffer = (uint8_t *) malloc(buffer_size); if (current_buffer == NULL) return -1; size_t buffer_pos = 0; while (1) { ssize_t count = read( fd, current_buffer + buffer_pos, buffer_size - buffer_pos ); if (count < 0) break; if (count == 0) { uint8_t * return_buffer = (uint8_t *) realloc(current_buffer, buffer_pos); if (return_buffer == NULL) break; *buffer = return_buffer; return buffer_pos; } buffer_pos += count; if (buffer_pos == buffer_size) { buffer_size *= 2; uint8_t * new_buffer = (uint8_t *) realloc(current_buffer, buffer_size); if (new_buffer == NULL) break; current_buffer = new_buffer; } } free(current_buffer); return -1; } template T check_errno( const char * message, T value ) { if (value == T(-1)) { perror(message); exit(1); } return value; } template size_t check_error( F f, T * object, const char * message, size_t value ) { if (value == olm_error()) { const char * olm_message = f(object); ssize_t ignored; ignored = write(STDERR_FILENO, message, strlen(message)); ignored = write(STDERR_FILENO, ": ", 2); ignored = write(STDERR_FILENO, olm_message, strlen(olm_message)); ignored = write(STDERR_FILENO, "\n", 1); exit(2); return ignored; } return value; } size_t check_session( OlmSession * session, const char * message, size_t value ) { return check_error(olm_session_last_error, session, message, value); } olm-2.2.2+git20170526.0fd768e+dfsg/include/000077500000000000000000000000001311755073500173455ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/000077500000000000000000000000001311755073500201345ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/account.hh000066400000000000000000000112141311755073500221100ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OLM_ACCOUNT_HH_ #define OLM_ACCOUNT_HH_ #include "olm/list.hh" #include "olm/crypto.h" #include "olm/error.h" #include namespace olm { struct IdentityKeys { _olm_ed25519_key_pair ed25519_key; _olm_curve25519_key_pair curve25519_key; }; struct OneTimeKey { std::uint32_t id; bool published; _olm_curve25519_key_pair key; }; static std::size_t const MAX_ONE_TIME_KEYS = 100; struct Account { Account(); IdentityKeys identity_keys; List one_time_keys; std::uint32_t next_one_time_key_id; OlmErrorCode last_error; /** Number of random bytes needed to create a new account */ std::size_t new_account_random_length(); /** 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 */ std::size_t new_account( uint8_t const * random, std::size_t random_length ); /** Number of bytes needed to output the identity keys for this account */ std::size_t get_identity_json_length(); /** Output the identity keys for this account as JSON in the following * format: * * {"curve25519":"<43 base64 characters>" * ,"ed25519":"<43 base64 characters>" * } * * * 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_identity_json( std::uint8_t * identity_json, std::size_t identity_json_length ); /** * The length of an ed25519 signature in bytes. */ std::size_t signature_length(); /** * Signs a message with the ed25519 key for this account. */ std::size_t sign( std::uint8_t const * message, std::size_t message_length, std::uint8_t * signature, std::size_t signature_length ); /** Number of bytes needed to output the one time keys for this account */ std::size_t get_one_time_keys_json_length(); /** Output the one time keys that haven't been published yet as JSON: * * {"curve25519": * ["<6 byte key id>":"<43 base64 characters>" * ,"<6 byte key id>":"<43 base64 characters>" * ... * ] * } * * 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_one_time_keys_json( std::uint8_t * one_time_json, std::size_t one_time_json_length ); /** Mark the current list of one_time_keys as being published. They * will no longer be returned by get_one_time_keys_json_length(). */ std::size_t mark_keys_as_published(); /** The largest number of one time keys this account can store. */ std::size_t max_number_of_one_time_keys(); /** The number of random bytes needed to generate a given number of new one * time keys. */ std::size_t generate_one_time_keys_random_length( std::size_t number_of_keys ); /** 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 * old keys are discarded. 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_one_time_keys( std::size_t number_of_keys, std::uint8_t const * random, std::size_t random_length ); /** Lookup a one time key with the given public key */ OneTimeKey const * lookup_key( _olm_curve25519_public_key const & public_key ); /** Remove a one time key with the given public key */ std::size_t remove_key( _olm_curve25519_public_key const & public_key ); }; std::size_t pickle_length( Account const & value ); std::uint8_t * pickle( std::uint8_t * pos, Account const & value ); std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, Account & value ); } // namespace olm #endif /* OLM_ACCOUNT_HH_ */ olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/base64.h000066400000000000000000000037651311755073500214040ustar00rootroot00000000000000/* Copyright 2015, 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. */ /* C bindings for base64 functions */ #ifndef OLM_BASE64_H_ #define OLM_BASE64_H_ #include #include #ifdef __cplusplus extern "C" { #endif /** * The number of bytes of unpadded base64 needed to encode a length of input. */ size_t _olm_encode_base64_length( size_t input_length ); /** * Encode the raw input as unpadded base64. * Writes encode_base64_length(input_length) bytes to 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. * * Returns number of bytes encoded */ size_t _olm_encode_base64( uint8_t const * input, size_t input_length, uint8_t * output ); /** * The number of bytes of raw data a length of unpadded base64 will encode to. * Returns size_t(-1) if the length is not a valid length for base64. */ size_t _olm_decode_base64_length( size_t input_length ); /** * Decodes the unpadded base64 input to raw bytes. * Writes decode_base64_length(input_length) bytes to the output buffer. * The output can overlap with the first three quarters of the input buffer. * That is, the input pointers and output pointer may be the same. * * Returns number of bytes decoded */ size_t _olm_decode_base64( uint8_t const * input, size_t input_length, uint8_t * output ); #ifdef __cplusplus } // extern "C" #endif #endif /* OLM_BASE64_H_ */ olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/base64.hh000066400000000000000000000036041311755073500215440ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OLM_BASE64_HH_ #define OLM_BASE64_HH_ #include #include namespace olm { /** * The number of bytes of unpadded base64 needed to encode a length of input. */ std::size_t encode_base64_length( std::size_t input_length ); /** * Encode the raw input as unpadded base64. * Writes encode_base64_length(input_length) bytes to 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. */ std::uint8_t * encode_base64( std::uint8_t const * input, std::size_t input_length, std::uint8_t * output ); /** * The number of bytes of raw data a length of unpadded base64 will encode to. * Returns std::size_t(-1) if the length is not a valid length for base64. */ std::size_t decode_base64_length( std::size_t input_length ); /** * Decodes the unpadded base64 input to raw bytes. * Writes decode_base64_length(input_length) bytes to the output buffer. * The output can overlap with the first three quarters of the input buffer. * That is, the input pointers and output pointer may be the same. */ std::uint8_t const * decode_base64( std::uint8_t const * input, std::size_t input_length, std::uint8_t * output ); } // namespace olm #endif /* OLM_BASE64_HH_ */ olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/cipher.h000066400000000000000000000102741311755073500215630ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OLM_CIPHER_H_ #define OLM_CIPHER_H_ #include #include #ifdef __cplusplus extern "C" { #endif struct _olm_cipher; struct _olm_cipher_ops { /** * Returns the length of the message authentication code that will be * appended to the output. */ size_t (*mac_length)(const struct _olm_cipher *cipher); /** * Returns the length of cipher-text for a given length of plain-text. */ size_t (*encrypt_ciphertext_length)( const struct _olm_cipher *cipher, size_t plaintext_length ); /* * Encrypts the plain-text into the output buffer and authenticates the * contents of the output buffer covering both cipher-text and any other * associated data in the output buffer. * * |---------------------------------------output_length-->| * output |--ciphertext_length-->| |---mac_length-->| * ciphertext * * The plain-text pointers and cipher-text pointers may be the same. * * Returns size_t(-1) if the length of the cipher-text or the output * buffer is too small. Otherwise returns the length of the output buffer. */ size_t (*encrypt)( const struct _olm_cipher *cipher, uint8_t const * key, size_t key_length, uint8_t const * plaintext, size_t plaintext_length, uint8_t * ciphertext, size_t ciphertext_length, uint8_t * output, size_t output_length ); /** * Returns the maximum length of plain-text that a given length of * cipher-text can contain. */ size_t (*decrypt_max_plaintext_length)( const struct _olm_cipher *cipher, size_t ciphertext_length ); /** * Authenticates the input and decrypts the cipher-text into the plain-text * buffer. * * |----------------------------------------input_length-->| * input |--ciphertext_length-->| |---mac_length-->| * ciphertext * * The plain-text pointers and cipher-text pointers may be the same. * * Returns size_t(-1) if the length of the plain-text buffer is too * small or if the authentication check fails. Otherwise returns the length * of the plain text. */ size_t (*decrypt)( const struct _olm_cipher *cipher, uint8_t const * key, size_t key_length, uint8_t const * input, size_t input_length, uint8_t const * ciphertext, size_t ciphertext_length, uint8_t * plaintext, size_t max_plaintext_length ); }; struct _olm_cipher { const struct _olm_cipher_ops *ops; /* cipher-specific fields follow */ }; struct _olm_cipher_aes_sha_256 { struct _olm_cipher base_cipher; /** context string for the HKDF used for deriving the AES256 key, HMAC key, * and AES IV, from the key material passed to encrypt/decrypt. */ uint8_t const * kdf_info; /** length of context string kdf_info */ size_t kdf_info_length; }; 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. * * To use it, declare: * * struct _olm_cipher_aes_sha_256 MY_CIPHER = * OLM_CIPHER_INIT_AES_SHA_256("MY_KDF"); * struct _olm_cipher *cipher = OLM_CIPHER_BASE(&MY_CIPHER); */ #define OLM_CIPHER_INIT_AES_SHA_256(KDF_INFO) { \ .base_cipher = { &_olm_cipher_aes_sha_256_ops },\ .kdf_info = (uint8_t *)(KDF_INFO), \ .kdf_info_length = sizeof(KDF_INFO) - 1 \ } #define OLM_CIPHER_BASE(CIPHER) \ (&((CIPHER)->base_cipher)) #ifdef __cplusplus } /* extern "C" */ #endif #endif /* OLM_CIPHER_H_ */ olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/crypto.h000066400000000000000000000131711311755073500216300ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* C-compatible crpyto utility functions. At some point all of crypto.hh will * move here. */ #ifndef OLM_CRYPTO_H_ #define OLM_CRYPTO_H_ #include #include #ifdef __cplusplus extern "C" { #endif /** length of a sha256 hash */ #define SHA256_OUTPUT_LENGTH 32 /** length of a public or private Curve25519 key */ #define CURVE25519_KEY_LENGTH 32 /** length of the shared secret created by a Curve25519 ECDH operation */ #define CURVE25519_SHARED_SECRET_LENGTH 32 /** amount of random data required to create a Curve25519 keypair */ #define CURVE25519_RANDOM_LENGTH CURVE25519_KEY_LENGTH /** length of a public Ed25519 key */ #define ED25519_PUBLIC_KEY_LENGTH 32 /** length of a private Ed25519 key */ #define ED25519_PRIVATE_KEY_LENGTH 64 /** amount of random data required to create a Ed25519 keypair */ #define ED25519_RANDOM_LENGTH 32 /** length of an Ed25519 signature */ #define ED25519_SIGNATURE_LENGTH 64 /** length of an aes256 key */ #define AES256_KEY_LENGTH 32 /** length of an aes256 initialisation vector */ #define AES256_IV_LENGTH 16 struct _olm_aes256_key { uint8_t key[AES256_KEY_LENGTH]; }; struct _olm_aes256_iv { uint8_t iv[AES256_IV_LENGTH]; }; struct _olm_curve25519_public_key { uint8_t public_key[CURVE25519_KEY_LENGTH]; }; struct _olm_curve25519_private_key { uint8_t private_key[CURVE25519_KEY_LENGTH]; }; struct _olm_curve25519_key_pair { struct _olm_curve25519_public_key public_key; struct _olm_curve25519_private_key private_key; }; struct _olm_ed25519_public_key { uint8_t public_key[ED25519_PUBLIC_KEY_LENGTH]; }; struct _olm_ed25519_private_key { uint8_t private_key[ED25519_PRIVATE_KEY_LENGTH]; }; struct _olm_ed25519_key_pair { struct _olm_ed25519_public_key public_key; struct _olm_ed25519_private_key private_key; }; /** The length of output the aes_encrypt_cbc function will write */ size_t _olm_crypto_aes_encrypt_cbc_length( size_t input_length ); /** Encrypts the input using AES256 in CBC mode with PKCS#7 padding. * The output buffer must be big enough to hold the output including padding */ void _olm_crypto_aes_encrypt_cbc( const struct _olm_aes256_key *key, const struct _olm_aes256_iv *iv, const uint8_t *input, size_t input_length, uint8_t *output ); /** Decrypts the input using AES256 in CBC mode. The output buffer must be at * least the same size as the input buffer. Returns the length of the plaintext * without padding on success or std::size_t(-1) if the padding is invalid. */ size_t _olm_crypto_aes_decrypt_cbc( const struct _olm_aes256_key *key, const struct _olm_aes256_iv *iv, uint8_t const * input, size_t input_length, uint8_t * output ); /** Computes SHA-256 of the input. The output buffer must be a least * SHA256_OUTPUT_LENGTH (32) bytes long. */ void _olm_crypto_sha256( uint8_t const * input, size_t input_length, uint8_t * output ); /** HMAC: Keyed-Hashing for Message Authentication * http://tools.ietf.org/html/rfc2104 * Computes HMAC-SHA-256 of the input for the key. The output buffer must * be at least SHA256_OUTPUT_LENGTH (32) bytes long. */ void _olm_crypto_hmac_sha256( uint8_t const * key, size_t key_length, uint8_t const * input, size_t input_length, uint8_t * output ); /** HMAC-based Key Derivation Function (HKDF) * https://tools.ietf.org/html/rfc5869 * Derives key material from the input bytes. */ void _olm_crypto_hkdf_sha256( uint8_t const * input, size_t input_length, uint8_t const * info, size_t info_length, uint8_t const * salt, size_t salt_length, uint8_t * output, size_t output_length ); /** Generate a curve25519 key pair * random_32_bytes should be CURVE25519_RANDOM_LENGTH (32) bytes long. */ void _olm_crypto_curve25519_generate_key( uint8_t const * random_32_bytes, struct _olm_curve25519_key_pair *output ); /** Create a shared secret using our private key and their public key. * The output buffer must be at least CURVE25519_SHARED_SECRET_LENGTH (32) bytes long. */ void _olm_crypto_curve25519_shared_secret( const struct _olm_curve25519_key_pair *our_key, const struct _olm_curve25519_public_key *their_key, uint8_t * output ); /** Generate an ed25519 key pair * random_32_bytes should be ED25519_RANDOM_LENGTH (32) bytes long. */ void _olm_crypto_ed25519_generate_key( uint8_t const * random_bytes, struct _olm_ed25519_key_pair *output ); /** Signs the message using our private key. * * The output buffer must be at least ED25519_SIGNATURE_LENGTH (64) bytes * long. */ void _olm_crypto_ed25519_sign( const struct _olm_ed25519_key_pair *our_key, const uint8_t * message, size_t message_length, uint8_t * output ); /** Verify an ed25519 signature * The signature input buffer must be ED25519_SIGNATURE_LENGTH (64) bytes long. * Returns non-zero if the signature is valid. */ int _olm_crypto_ed25519_verify( const struct _olm_ed25519_public_key *their_key, const uint8_t * message, size_t message_length, const uint8_t * signature ); #ifdef __cplusplus } // extern "C" #endif #endif /* OLM_CRYPTO_H_ */ olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/error.h000066400000000000000000000047751311755073500214530ustar00rootroot00000000000000/* Copyright 2015-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. */ #ifndef OLM_ERROR_H_ #define OLM_ERROR_H_ #ifdef __cplusplus extern "C" { #endif enum OlmErrorCode { OLM_SUCCESS = 0, /*!< There wasn't an error */ OLM_NOT_ENOUGH_RANDOM = 1, /*!< Not enough entropy was supplied */ OLM_OUTPUT_BUFFER_TOO_SMALL = 2, /*!< Supplied output buffer is too small */ OLM_BAD_MESSAGE_VERSION = 3, /*!< The message version is unsupported */ OLM_BAD_MESSAGE_FORMAT = 4, /*!< The message couldn't be decoded */ OLM_BAD_MESSAGE_MAC = 5, /*!< The message couldn't be decrypted */ OLM_BAD_MESSAGE_KEY_ID = 6, /*!< The message references an unknown key id */ OLM_INVALID_BASE64 = 7, /*!< The input base64 was invalid */ OLM_BAD_ACCOUNT_KEY = 8, /*!< The supplied account key is invalid */ OLM_UNKNOWN_PICKLE_VERSION = 9, /*!< The pickled object is too new */ OLM_CORRUPTED_PICKLE = 10, /*!< The pickled object couldn't be decoded */ OLM_BAD_SESSION_KEY = 11, /*!< Attempt to initialise an inbound group session from an invalid session key */ OLM_UNKNOWN_MESSAGE_INDEX = 12, /*!< Attempt to decode a message whose * index is earlier than our earliest * known session key. */ /** * Attempt to unpickle an account which uses pickle version 1 (which did * not save enough space for the Ed25519 key; the key should be considered * compromised. We don't let the user reload the account. */ OLM_BAD_LEGACY_ACCOUNT_PICKLE = 13, /** * Received message had a bad signature */ OLM_BAD_SIGNATURE = 14, /* remember to update the list of string constants in error.c when updating * this list. */ }; /** get a string representation of the given error code. */ const char * _olm_error_to_string(enum OlmErrorCode error); #ifdef __cplusplus } // extern "C" #endif #endif /* OLM_ERROR_H_ */ olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/inbound_group_session.h000066400000000000000000000164051311755073500247300ustar00rootroot00000000000000/* 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. */ #ifndef OLM_INBOUND_GROUP_SESSION_H_ #define OLM_INBOUND_GROUP_SESSION_H_ #include #include #ifdef __cplusplus extern "C" { #endif typedef struct OlmInboundGroupSession OlmInboundGroupSession; /** get the size of an inbound group session, in bytes. */ size_t olm_inbound_group_session_size(); /** * Initialise an inbound group session object using the supplied memory * The supplied memory should be at least olm_inbound_group_session_size() * bytes. */ OlmInboundGroupSession * olm_inbound_group_session( void *memory ); /** * A null terminated string describing the most recent error to happen to a * group session */ const char *olm_inbound_group_session_last_error( const OlmInboundGroupSession *session ); /** Clears the memory used to back this group session */ size_t olm_clear_inbound_group_session( OlmInboundGroupSession *session ); /** Returns the number of bytes needed to store an inbound group session */ size_t olm_pickle_inbound_group_session_length( const OlmInboundGroupSession *session ); /** * Stores a group session as a base64 string. Encrypts the session using the * supplied key. Returns the length of the session on success. * * Returns olm_error() on failure. If the pickle output buffer * is smaller than olm_pickle_inbound_group_session_length() then * olm_inbound_group_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */ size_t olm_pickle_inbound_group_session( OlmInboundGroupSession *session, void const * key, size_t key_length, void * pickled, size_t pickled_length ); /** * Loads a group session from a pickled base64 string. Decrypts the session * using the supplied key. * * Returns olm_error() on failure. If the key doesn't match the one used to * encrypt the account then olm_inbound_group_session_last_error() will be * "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then * olm_inbound_group_session_last_error() will be "INVALID_BASE64". The input * pickled buffer is destroyed */ size_t olm_unpickle_inbound_group_session( OlmInboundGroupSession *session, void const * key, size_t key_length, void * pickled, size_t pickled_length ); /** * Start a new inbound group session, from a key exported from * olm_outbound_group_session_key * * Returns olm_error() on failure. On failure last_error will be set with an * error code. The last_error will be: * * * OLM_INVALID_BASE64 if the session_key is not valid base64 * * OLM_BAD_SESSION_KEY if the session_key is invalid */ size_t olm_init_inbound_group_session( OlmInboundGroupSession *session, /* base64-encoded keys */ uint8_t const * session_key, size_t session_key_length ); /** * Import an inbound group session, from a previous export. * * Returns olm_error() on failure. On failure last_error will be set with an * error code. The last_error will be: * * * OLM_INVALID_BASE64 if the session_key is not valid base64 * * OLM_BAD_SESSION_KEY if the session_key is invalid */ size_t olm_import_inbound_group_session( OlmInboundGroupSession *session, /* base64-encoded keys; note that it will be overwritten with the base64-decoded data. */ uint8_t const * session_key, size_t session_key_length ); /** * Get an upper bound on the number of bytes of plain-text the decrypt method * will write for a given input message length. The actual size could be * different due to padding. * * The input message buffer is destroyed. * * Returns olm_error() on failure. */ size_t olm_group_decrypt_max_plaintext_length( OlmInboundGroupSession *session, uint8_t * message, size_t message_length ); /** * Decrypt a message. * * The input message buffer is destroyed. * * Returns the length of the decrypted plain-text, or olm_error() on failure. * * On failure last_error will be set with an error code. The last_error will * be: * * OLM_OUTPUT_BUFFER_TOO_SMALL if the plain-text buffer is too small * * OLM_INVALID_BASE64 if the message is not valid base-64 * * OLM_BAD_MESSAGE_VERSION if the message was encrypted with an unsupported * version of the protocol * * OLM_BAD_MESSAGE_FORMAT if the message headers could not be decoded * * OLM_BAD_MESSAGE_MAC if the message could not be verified * * OLM_UNKNOWN_MESSAGE_INDEX if we do not have a session key corresponding to the * message's index (ie, it was sent before the session key was shared with * us) */ size_t olm_group_decrypt( OlmInboundGroupSession *session, /* input; note that it will be overwritten with the base64-decoded message. */ uint8_t * message, size_t message_length, /* output */ uint8_t * plaintext, size_t max_plaintext_length, uint32_t * message_index ); /** * Get the number of bytes returned by olm_inbound_group_session_id() */ size_t olm_inbound_group_session_id_length( const OlmInboundGroupSession *session ); /** * Get a base64-encoded identifier for this session. * * Returns the length of the session id on success or olm_error() on * failure. On failure last_error will be set with an error code. The * last_error will be OUTPUT_BUFFER_TOO_SMALL if the id buffer was too * small. */ size_t olm_inbound_group_session_id( OlmInboundGroupSession *session, uint8_t * id, size_t id_length ); /** * Get the first message index we know how to decrypt. */ uint32_t olm_inbound_group_session_first_known_index( const OlmInboundGroupSession *session ); /** * Check if the session has been verified as a valid session. * * (A session is verified either because the original session share was signed, * or because we have subsequently successfully decrypted a message.) * * This is mainly intended for the unit tests, currently. */ int olm_inbound_group_session_is_verified( const OlmInboundGroupSession *session ); /** * Get the number of bytes returned by olm_export_inbound_group_session() */ size_t olm_export_inbound_group_session_length( const OlmInboundGroupSession *session ); /** * Export the base64-encoded ratchet key for this session, at the given index, * in a format which can be used by olm_import_inbound_group_session * * Returns the length of the ratchet key on success or olm_error() on * failure. On failure last_error will be set with an error code. The * last_error will be: * * OUTPUT_BUFFER_TOO_SMALL if the buffer was too small * * OLM_UNKNOWN_MESSAGE_INDEX if we do not have a session key corresponding to the * given index (ie, it was sent before the session key was shared with * us) */ size_t olm_export_inbound_group_session( OlmInboundGroupSession *session, uint8_t * key, size_t key_length, uint32_t message_index ); #ifdef __cplusplus } // extern "C" #endif #endif /* OLM_INBOUND_GROUP_SESSION_H_ */ olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/list.hh000066400000000000000000000057451311755073500214430ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OLM_LIST_HH_ #define OLM_LIST_HH_ #include namespace olm { template class List { public: List() : _end(_data) {} typedef T * iterator; typedef T const * const_iterator; T * begin() { return _data; } T * end() { return _end; } T const * begin() const { return _data; } T const * end() const { return _end; } /** * Is the list empty? */ bool empty() const { return _end == _data; } /** * The number of items in the list. */ std::size_t size() const { return _end - _data; } T & operator[](std::size_t index) { return _data[index]; } T const & operator[](std::size_t index) const { return _data[index]; } /** * Erase the item from the list at the given position. */ void erase(T * pos) { --_end; while (pos != _end) { *pos = *(pos + 1); ++pos; } } /** * Make space for an item in the list at a given position. * If inserting the item makes the list longer than max_size then * the end of the list is discarded. * Returns the where the item is inserted. */ T * insert(T * pos) { if (_end != _data + max_size) { ++_end; } else if (pos == _end) { --pos; } T * tmp = _end - 1; while (tmp != pos) { *tmp = *(tmp - 1); --tmp; } return pos; } /** * Make space for an item in the list at the start of the list */ T * insert() { return insert(begin()); } /** * Insert an item into the list at a given position. * If inserting the item makes the list longer than max_size then * the end of the list is discarded. * Returns the where the item is inserted. */ T * insert(T * pos, T const & value) { pos = insert(pos); *pos = value; return pos; } List & operator=(List const & other) { if (this == &other) { return *this; } T * this_pos = _data; T * const other_pos = other._data; while (other_pos != other._end) { *this_pos = *other; ++this_pos; ++other_pos; } _end = this_pos; return *this; } private: T * _end; T _data[max_size]; }; } // namespace olm #endif /* OLM_LIST_HH_ */ olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/megolm.h000066400000000000000000000051451311755073500215720ustar00rootroot00000000000000/* 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. */ #ifndef OLM_MEGOLM_H_ #define OLM_MEGOLM_H_ /** * implementation of the Megolm multi-part ratchet used in group chats. */ #include #include #ifdef __cplusplus extern "C" { #endif /** * number of bytes in each part of the ratchet; this should be the same as * the length of the hash function used in the HMAC (32 bytes for us, as we * use HMAC-SHA-256) */ #define MEGOLM_RATCHET_PART_LENGTH 32 /* SHA256_OUTPUT_LENGTH */ /** * number of parts in the ratchet; the advance() implementations rely on * this being 4. */ #define MEGOLM_RATCHET_PARTS 4 #define MEGOLM_RATCHET_LENGTH (MEGOLM_RATCHET_PARTS * MEGOLM_RATCHET_PART_LENGTH) typedef struct Megolm { uint8_t data[MEGOLM_RATCHET_PARTS][MEGOLM_RATCHET_PART_LENGTH]; uint32_t counter; } Megolm; /** * The cipher used in megolm-backed conversations * * (AES256 + SHA256, with keys based on an HKDF with info of MEGOLM_KEYS) */ extern const struct _olm_cipher *megolm_cipher; /** * initialize the megolm ratchet. random_data should be at least * MEGOLM_RATCHET_LENGTH bytes of randomness. */ void megolm_init(Megolm *megolm, uint8_t const *random_data, uint32_t counter); /** Returns the number of bytes needed to store a megolm */ size_t megolm_pickle_length(const Megolm *megolm); /** * Pickle the megolm. Returns a pointer to the next free space in the buffer. */ uint8_t * megolm_pickle(const Megolm *megolm, uint8_t *pos); /** * Unpickle the megolm. Returns a pointer to the next item in the buffer. */ const uint8_t * megolm_unpickle(Megolm *megolm, const uint8_t *pos, const uint8_t *end); /** advance the ratchet by one step */ void megolm_advance(Megolm *megolm); /** * get the key data in the ratchet. The returned data is * MEGOLM_RATCHET_LENGTH bytes long. */ #define megolm_get_data(megolm) ((const uint8_t *)((megolm)->data)) /** advance the ratchet to a given count */ void megolm_advance_to(Megolm *megolm, uint32_t advance_to); #ifdef __cplusplus } // extern "C" #endif #endif /* OLM_MEGOLM_H_ */ olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/memory.h000066400000000000000000000017611311755073500216220ustar00rootroot00000000000000/* Copyright 2015, 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. */ /* C bindings for memory functions */ #ifndef OLM_MEMORY_H_ #define OLM_MEMORY_H_ #include #ifdef __cplusplus extern "C" { #endif /** * Clear the memory held in the buffer. This is more resilient to being * optimised away than memset or bzero. */ void _olm_unset( void volatile * buffer, size_t buffer_length ); #ifdef __cplusplus } // extern "C" #endif #endif /* OLM_MEMORY_H_ */ olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/memory.hh000066400000000000000000000047371311755073500220000ustar00rootroot00000000000000/* Copyright 2015, 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. */ #include #include #include #include #include #include #include namespace olm { /** Clear the memory held in the buffer */ void unset( void volatile * buffer, std::size_t buffer_length ); /** Clear the memory backing an object */ template void unset(T & value) { unset(reinterpret_cast(&value), sizeof(T)); } /** Check if two buffers are equal in constant time. */ bool is_equal( std::uint8_t const * buffer_a, std::uint8_t const * buffer_b, std::size_t length ); /** Check if two fixed size arrays are equals */ template bool array_equal( T const & array_a, T const & array_b ) { static_assert( std::is_array::value && std::is_convertible::value && sizeof(T) > 0, "Arguments to array_equal must be std::uint8_t arrays[]." ); return is_equal(array_a, array_b, sizeof(T)); } /** Copy into a fixed size array */ template std::uint8_t const * load_array( T & destination, std::uint8_t const * source ) { static_assert( std::is_array::value && std::is_convertible::value && sizeof(T) > 0, "The first argument to load_array must be a std::uint8_t array[]." ); std::memcpy(destination, source, sizeof(T)); return source + sizeof(T); } /** Copy from a fixed size array */ template std::uint8_t * store_array( std::uint8_t * destination, T const & source ) { static_assert( std::is_array::value && std::is_convertible::value && sizeof(T) > 0, "The second argument to store_array must be a std::uint8_t array[]." ); std::memcpy(destination, source, sizeof(T)); return destination + sizeof(T); } } // namespace olm olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/message.h000066400000000000000000000046461311755073500217430ustar00rootroot00000000000000/* 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. */ /** * functions for encoding and decoding messages in the Olm protocol. * * Some of these functions have only C++ bindings, and are declared in * message.hh; in time, they should probably be converted to plain C and * declared here. */ #ifndef OLM_MESSAGE_H_ #define OLM_MESSAGE_H_ #include #include #ifdef __cplusplus extern "C" { #endif /** * The length of the buffer needed to hold a group message. */ size_t _olm_encode_group_message_length( uint32_t chain_index, size_t ciphertext_length, size_t mac_length, size_t signature_length ); /** * Writes the message headers into the output buffer. * * version: version number of the olm protocol * message_index: message index * ciphertext_length: length of the ciphertext * output: where to write the output. Should be at least * olm_encode_group_message_length() bytes long. * ciphertext_ptr: returns the address that the ciphertext * should be written to, followed by the MAC and the * signature. * * Returns the size of the message, up to the MAC. */ size_t _olm_encode_group_message( uint8_t version, uint32_t message_index, size_t ciphertext_length, uint8_t *output, uint8_t **ciphertext_ptr ); struct _OlmDecodeGroupMessageResults { uint8_t version; uint32_t message_index; int has_message_index; const uint8_t *ciphertext; size_t ciphertext_length; }; /** * Reads the message headers from the input buffer. */ void _olm_decode_group_message( const uint8_t *input, size_t input_length, size_t mac_length, size_t signature_length, /* output structure: updated with results */ struct _OlmDecodeGroupMessageResults *results ); #ifdef __cplusplus } // extern "C" #endif #endif /* OLM_MESSAGE_H_ */ olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/message.hh000066400000000000000000000067771311755073500221220ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * functions for encoding and decoding messages in the Olm protocol. * * Some of these functions have plain-C bindings, and are declared in * message.h; in time, all of the functions declared here should probably be * converted to plain C and moved to message.h. */ #include "message.h" #include #include namespace olm { /** * The length of the buffer needed to hold a message. */ std::size_t encode_message_length( std::uint32_t counter, std::size_t ratchet_key_length, std::size_t ciphertext_length, std::size_t mac_length ); struct MessageWriter { std::uint8_t * ratchet_key; std::uint8_t * ciphertext; }; struct MessageReader { std::uint8_t version; bool has_counter; std::uint32_t counter; std::uint8_t const * input; std::size_t input_length; std::uint8_t const * ratchet_key; std::size_t ratchet_key_length; std::uint8_t const * ciphertext; std::size_t ciphertext_length; }; /** * Writes the message headers into the output buffer. * Populates the writer struct with pointers into the output buffer. */ void encode_message( MessageWriter & writer, std::uint8_t version, std::uint32_t counter, std::size_t ratchet_key_length, std::size_t ciphertext_length, std::uint8_t * output ); /** * Reads the message headers from the input buffer. * Populates the reader struct with pointers into the input buffer. */ void decode_message( MessageReader & reader, std::uint8_t const * input, std::size_t input_length, std::size_t mac_length ); struct PreKeyMessageWriter { std::uint8_t * identity_key; std::uint8_t * base_key; std::uint8_t * one_time_key; std::uint8_t * message; }; struct PreKeyMessageReader { std::uint8_t version; std::uint8_t const * identity_key; std::size_t identity_key_length; std::uint8_t const * base_key; std::size_t base_key_length; std::uint8_t const * one_time_key; std::size_t one_time_key_length; std::uint8_t const * message; std::size_t message_length; }; /** * The length of the buffer needed to hold a message. */ std::size_t encode_one_time_key_message_length( std::size_t identity_key_length, std::size_t base_key_length, std::size_t one_time_key_length, std::size_t message_length ); /** * Writes the message headers into the output buffer. * Populates the writer struct with pointers into the output buffer. */ void encode_one_time_key_message( PreKeyMessageWriter & writer, std::uint8_t version, std::size_t identity_key_length, std::size_t base_key_length, std::size_t one_time_key_length, std::size_t message_length, std::uint8_t * output ); /** * Reads the message headers from the input buffer. * Populates the reader struct with pointers into the input buffer. */ void decode_one_time_key_message( PreKeyMessageReader & reader, std::uint8_t const * input, std::size_t input_length ); } // namespace olm olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/olm.h000066400000000000000000000403251311755073500211000ustar00rootroot00000000000000/* Copyright 2015, 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. */ #ifndef OLM_H_ #define OLM_H_ #include #include #include "olm/inbound_group_session.h" #include "olm/outbound_group_session.h" #ifdef __cplusplus extern "C" { #endif static const size_t OLM_MESSAGE_TYPE_PRE_KEY = 0; static const size_t OLM_MESSAGE_TYPE_MESSAGE = 1; typedef struct OlmAccount OlmAccount; typedef struct OlmSession OlmSession; typedef struct OlmUtility OlmUtility; /** Get the version number of the library. * Arguments will be updated if non-null. */ void olm_get_library_version(uint8_t *major, uint8_t *minor, uint8_t *patch); /** The size of an account object in bytes */ size_t olm_account_size(); /** The size of a session object in bytes */ size_t olm_session_size(); /** The size of a utility object in bytes */ size_t olm_utility_size(); /** Initialise an account object using the supplied memory * The supplied memory must be at least olm_account_size() bytes */ OlmAccount * olm_account( void * memory ); /** Initialise a session object using the supplied memory * The supplied memory must be at least olm_session_size() bytes */ OlmSession * olm_session( void * memory ); /** Initialise a utility object using the supplied memory * The supplied memory must be at least olm_utility_size() bytes */ OlmUtility * olm_utility( void * memory ); /** The value that olm will return from a function if there was an error */ size_t olm_error(); /** A null terminated string describing the most recent error to happen to an * account */ const char * olm_account_last_error( OlmAccount * account ); /** A null terminated string describing the most recent error to happen to a * session */ const char * olm_session_last_error( OlmSession * session ); /** A null terminated string describing the most recent error to happen to a * utility */ const char * olm_utility_last_error( OlmUtility * utility ); /** Clears the memory used to back this account */ size_t olm_clear_account( OlmAccount * account ); /** Clears the memory used to back this session */ size_t olm_clear_session( OlmSession * session ); /** Clears the memory used to back this utility */ size_t olm_clear_utility( OlmUtility * utility ); /** Returns the number of bytes needed to store an account */ size_t olm_pickle_account_length( OlmAccount * account ); /** Returns the number of bytes needed to store a session */ size_t olm_pickle_session_length( OlmSession * session ); /** Stores an account as a base64 string. Encrypts the account using the * supplied key. Returns the length of the pickled account on success. * Returns olm_error() on failure. If the pickle output buffer * is smaller than olm_pickle_account_length() then * olm_account_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */ size_t olm_pickle_account( OlmAccount * account, void const * key, size_t key_length, void * pickled, size_t pickled_length ); /** Stores a session as a base64 string. Encrypts the session using the * supplied key. Returns the length of the pickled session on success. * Returns olm_error() on failure. If the pickle output buffer * is smaller than olm_pickle_session_length() then * olm_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */ size_t olm_pickle_session( OlmSession * session, void const * key, size_t key_length, void * pickled, size_t pickled_length ); /** Loads an account from a pickled base64 string. Decrypts the account using * the supplied key. Returns olm_error() on failure. If the key doesn't * match the one used to encrypt the account then olm_account_last_error() * will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then * olm_account_last_error() will be "INVALID_BASE64". The input pickled * buffer is destroyed */ size_t olm_unpickle_account( OlmAccount * account, void const * key, size_t key_length, void * pickled, size_t pickled_length ); /** Loads a session from a pickled base64 string. Decrypts the session using * the supplied key. Returns olm_error() on failure. If the key doesn't * match the one used to encrypt the account then olm_session_last_error() * will be "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then * olm_session_last_error() will be "INVALID_BASE64". The input pickled * buffer is destroyed */ size_t olm_unpickle_session( OlmSession * session, void const * key, size_t key_length, void * pickled, size_t pickled_length ); /** The number of random bytes needed to create an account.*/ size_t olm_create_account_random_length( OlmAccount * account ); /** Creates a new account. Returns olm_error() on failure. If weren't * enough random bytes then olm_account_last_error() will be * "NOT_ENOUGH_RANDOM" */ size_t olm_create_account( OlmAccount * account, void * random, size_t random_length ); /** The size of the output buffer needed to hold the identity keys */ size_t olm_account_identity_keys_length( OlmAccount * account ); /** Writes the public parts of the identity keys for the account into the * identity_keys output buffer. Returns olm_error() on failure. If the * identity_keys buffer was too small then olm_account_last_error() will be * "OUTPUT_BUFFER_TOO_SMALL". */ size_t olm_account_identity_keys( OlmAccount * account, void * identity_keys, size_t identity_key_length ); /** The length of an ed25519 signature encoded as base64. */ size_t olm_account_signature_length( OlmAccount * account ); /** Signs a message with the ed25519 key for this account. Returns olm_error() * on failure. If the signature buffer was too small then * olm_account_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */ size_t olm_account_sign( OlmAccount * account, void const * message, size_t message_length, void * signature, size_t signature_length ); /** The size of the output buffer needed to hold the one time keys */ size_t olm_account_one_time_keys_length( OlmAccount * account ); /** Writes the public parts of the unpublished one time keys for the account * into the one_time_keys output buffer. *

* The returned data is a JSON-formatted object with the single property * curve25519, which is itself an object mapping key id to * base64-encoded Curve25519 key. For example: *

 * {
 *     curve25519: {
 *         "AAAAAA": "wo76WcYtb0Vk/pBOdmduiGJ0wIEjW4IBMbbQn7aSnTo",
 *         "AAAAAB": "LRvjo46L1X2vx69sS9QNFD29HWulxrmW11Up5AfAjgU"
 *     }
 * }
 * 
* Returns olm_error() on failure. *

* If the one_time_keys buffer was too small then olm_account_last_error() * will be "OUTPUT_BUFFER_TOO_SMALL". */ size_t olm_account_one_time_keys( OlmAccount * account, void * one_time_keys, size_t one_time_keys_length ); /** Marks the current set of one time keys as being published. */ size_t olm_account_mark_keys_as_published( OlmAccount * account ); /** The largest number of one time keys this account can store. */ size_t olm_account_max_number_of_one_time_keys( OlmAccount * account ); /** The number of random bytes needed to generate a given number of new one * time keys. */ size_t olm_account_generate_one_time_keys_random_length( OlmAccount * account, size_t 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 old keys are * discarded. Returns olm_error() on error. If the number of random bytes is * too small then olm_account_last_error() will be "NOT_ENOUGH_RANDOM". */ size_t olm_account_generate_one_time_keys( OlmAccount * account, size_t number_of_keys, void * random, size_t random_length ); /** The number of random bytes needed to create an outbound session */ size_t olm_create_outbound_session_random_length( OlmSession * session ); /** Creates a new out-bound session for sending messages to a given identity_key * and one_time_key. Returns olm_error() on failure. If the keys couldn't be * decoded as base64 then olm_session_last_error() will be "INVALID_BASE64" * If there weren't enough random bytes then olm_session_last_error() will * be "NOT_ENOUGH_RANDOM". */ size_t olm_create_outbound_session( OlmSession * session, OlmAccount * account, void const * their_identity_key, size_t their_identity_key_length, void const * their_one_time_key, size_t their_one_time_key_length, void * random, size_t random_length ); /** Create a new in-bound session for sending/receiving messages from an * incoming PRE_KEY message. Returns olm_error() on failure. If the base64 * couldn't be decoded then olm_session_last_error will be "INVALID_BASE64". * If the message was for an unsupported protocol version then * olm_session_last_error() will be "BAD_MESSAGE_VERSION". If the message * couldn't be decoded then then olm_session_last_error() will be * "BAD_MESSAGE_FORMAT". If the message refers to an unknown one time * key then olm_session_last_error() will be "BAD_MESSAGE_KEY_ID". */ size_t olm_create_inbound_session( OlmSession * session, OlmAccount * account, void * one_time_key_message, size_t message_length ); /** Create a new in-bound session for sending/receiving messages from an * incoming PRE_KEY message. Returns olm_error() on failure. If the base64 * couldn't be decoded then olm_session_last_error will be "INVALID_BASE64". * If the message was for an unsupported protocol version then * olm_session_last_error() will be "BAD_MESSAGE_VERSION". If the message * couldn't be decoded then then olm_session_last_error() will be * "BAD_MESSAGE_FORMAT". If the message refers to an unknown one time * key then olm_session_last_error() will be "BAD_MESSAGE_KEY_ID". */ size_t olm_create_inbound_session_from( OlmSession * session, OlmAccount * account, void const * their_identity_key, size_t their_identity_key_length, void * one_time_key_message, size_t message_length ); /** The length of the buffer needed to return the id for this session. */ size_t olm_session_id_length( OlmSession * session ); /** An identifier for this session. Will be the same for both ends of the * conversation. If the id buffer is too small then olm_session_last_error() * will be "OUTPUT_BUFFER_TOO_SMALL". */ size_t olm_session_id( OlmSession * session, void * id, size_t id_length ); int olm_session_has_received_message( OlmSession *session ); /** Checks if the PRE_KEY message is for this in-bound session. This can happen * if multiple messages are sent to this account before this account sends a * message in reply. Returns 1 if the session matches. Returns 0 if the session * does not match. Returns olm_error() on failure. If the base64 * couldn't be decoded then olm_session_last_error will be "INVALID_BASE64". * If the message was for an unsupported protocol version then * olm_session_last_error() will be "BAD_MESSAGE_VERSION". If the message * couldn't be decoded then then olm_session_last_error() will be * "BAD_MESSAGE_FORMAT". */ size_t olm_matches_inbound_session( OlmSession * session, void * one_time_key_message, size_t message_length ); /** Checks if the PRE_KEY message is for this in-bound session. This can happen * if multiple messages are sent to this account before this account sends a * message in reply. Returns 1 if the session matches. Returns 0 if the session * does not match. Returns olm_error() on failure. If the base64 * couldn't be decoded then olm_session_last_error will be "INVALID_BASE64". * If the message was for an unsupported protocol version then * olm_session_last_error() will be "BAD_MESSAGE_VERSION". If the message * couldn't be decoded then then olm_session_last_error() will be * "BAD_MESSAGE_FORMAT". */ size_t olm_matches_inbound_session_from( OlmSession * session, void const * their_identity_key, size_t their_identity_key_length, void * one_time_key_message, size_t message_length ); /** Removes the one time keys that the session used from the account. Returns * olm_error() on failure. If the account doesn't have any matching one time * keys then olm_account_last_error() will be "BAD_MESSAGE_KEY_ID". */ size_t olm_remove_one_time_keys( OlmAccount * account, OlmSession * session ); /** The type of the next message that olm_encrypt() will return. Returns * OLM_MESSAGE_TYPE_PRE_KEY if the message will be a PRE_KEY message. * Returns OLM_MESSAGE_TYPE_MESSAGE if the message will be a normal message. * Returns olm_error on failure. */ size_t olm_encrypt_message_type( OlmSession * session ); /** The number of random bytes needed to encrypt the next message. */ size_t olm_encrypt_random_length( OlmSession * session ); /** The size of the next message in bytes for the given number of plain-text * bytes. */ size_t olm_encrypt_message_length( OlmSession * session, size_t plaintext_length ); /** Encrypts a message using the session. Returns the length of the message in * bytes on success. Writes the message as base64 into the message buffer. * Returns olm_error() on failure. If the message buffer is too small then * olm_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". If there * weren't enough random bytes then olm_session_last_error() will be * "NOT_ENOUGH_RANDOM". */ size_t olm_encrypt( OlmSession * session, void const * plaintext, size_t plaintext_length, void * random, size_t random_length, void * message, size_t message_length ); /** The maximum number of bytes of plain-text a given message could decode to. * The actual size could be different due to padding. The input message buffer * is destroyed. Returns olm_error() on failure. If the message base64 * couldn't be decoded then olm_session_last_error() will be * "INVALID_BASE64". If the message is for an unsupported version of the * protocol then olm_session_last_error() will be "BAD_MESSAGE_VERSION". * If the message couldn't be decoded then olm_session_last_error() will be * "BAD_MESSAGE_FORMAT". */ size_t olm_decrypt_max_plaintext_length( OlmSession * session, size_t message_type, void * message, size_t message_length ); /** Decrypts a message using the session. The input message buffer is destroyed. * Returns the length of the plain-text on success. Returns olm_error() on * failure. If the plain-text buffer is smaller than * olm_decrypt_max_plaintext_length() then olm_session_last_error() * will be "OUTPUT_BUFFER_TOO_SMALL". If the base64 couldn't be decoded then * olm_session_last_error() will be "INVALID_BASE64". If the message is for * an unsupported version of the protocol then olm_session_last_error() will * be "BAD_MESSAGE_VERSION". If the message couldn't be decoded then * olm_session_last_error() will be BAD_MESSAGE_FORMAT". * If the MAC on the message was invalid then olm_session_last_error() will * be "BAD_MESSAGE_MAC". */ size_t olm_decrypt( OlmSession * session, size_t message_type, void * message, size_t message_length, void * plaintext, size_t max_plaintext_length ); /** The length of the buffer needed to hold the SHA-256 hash. */ size_t olm_sha256_length( OlmUtility * utility ); /** Calculates the SHA-256 hash of the input and encodes it as base64. If the * output buffer is smaller than olm_sha256_length() then * olm_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL". */ size_t olm_sha256( OlmUtility * utility, void const * input, size_t input_length, void * output, size_t output_length ); /** Verify an ed25519 signature. If the key was too small then * olm_session_last_error will be "INVALID_BASE64". If the signature was invalid * then olm_session_last_error() will be "BAD_MESSAGE_MAC". */ size_t olm_ed25519_verify( OlmUtility * utility, void const * key, size_t key_length, void const * message, size_t message_length, void * signature, size_t signature_length ); #ifdef __cplusplus } #endif #endif /* OLM_HH_ */ olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/olm.hh000066400000000000000000000002221311755073500212400ustar00rootroot00000000000000/* this file exists only for compatibility with existing applications. * You should use "#include " instead. */ #include "olm/olm.h" olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/outbound_group_session.h000066400000000000000000000127021311755073500251250ustar00rootroot00000000000000/* 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. */ #ifndef OLM_OUTBOUND_GROUP_SESSION_H_ #define OLM_OUTBOUND_GROUP_SESSION_H_ #include #include #ifdef __cplusplus extern "C" { #endif typedef struct OlmOutboundGroupSession OlmOutboundGroupSession; /** get the size of an outbound group session, in bytes. */ size_t olm_outbound_group_session_size(); /** * Initialise an outbound group session object using the supplied memory * The supplied memory should be at least olm_outbound_group_session_size() * bytes. */ OlmOutboundGroupSession * olm_outbound_group_session( void *memory ); /** * A null terminated string describing the most recent error to happen to a * group session */ const char *olm_outbound_group_session_last_error( const OlmOutboundGroupSession *session ); /** Clears the memory used to back this group session */ size_t olm_clear_outbound_group_session( OlmOutboundGroupSession *session ); /** Returns the number of bytes needed to store an outbound group session */ size_t olm_pickle_outbound_group_session_length( const OlmOutboundGroupSession *session ); /** * Stores a group session as a base64 string. Encrypts the session using the * supplied key. Returns the length of the session on success. * * Returns olm_error() on failure. If the pickle output buffer * is smaller than olm_pickle_outbound_group_session_length() then * olm_outbound_group_session_last_error() will be "OUTPUT_BUFFER_TOO_SMALL" */ size_t olm_pickle_outbound_group_session( OlmOutboundGroupSession *session, void const * key, size_t key_length, void * pickled, size_t pickled_length ); /** * Loads a group session from a pickled base64 string. Decrypts the session * using the supplied key. * * Returns olm_error() on failure. If the key doesn't match the one used to * encrypt the account then olm_outbound_group_session_last_error() will be * "BAD_ACCOUNT_KEY". If the base64 couldn't be decoded then * olm_outbound_group_session_last_error() will be "INVALID_BASE64". The input * pickled buffer is destroyed */ size_t olm_unpickle_outbound_group_session( OlmOutboundGroupSession *session, void const * key, size_t key_length, void * pickled, size_t pickled_length ); /** The number of random bytes needed to create an outbound group session */ size_t olm_init_outbound_group_session_random_length( const OlmOutboundGroupSession *session ); /** * Start a new outbound group session. Returns olm_error() on failure. On * failure last_error will be set with an error code. The last_error will be * NOT_ENOUGH_RANDOM if the number of random bytes was too small. */ size_t olm_init_outbound_group_session( OlmOutboundGroupSession *session, uint8_t *random, size_t random_length ); /** * The number of bytes that will be created by encrypting a message */ size_t olm_group_encrypt_message_length( OlmOutboundGroupSession *session, size_t plaintext_length ); /** * Encrypt some plain-text. Returns the length of the encrypted message or * olm_error() on failure. On failure last_error will be set with an * error code. The last_error will be OUTPUT_BUFFER_TOO_SMALL if the output * buffer is too small. */ size_t olm_group_encrypt( OlmOutboundGroupSession *session, uint8_t const * plaintext, size_t plaintext_length, uint8_t * message, size_t message_length ); /** * Get the number of bytes returned by olm_outbound_group_session_id() */ size_t olm_outbound_group_session_id_length( const OlmOutboundGroupSession *session ); /** * Get a base64-encoded identifier for this session. * * Returns the length of the session id on success or olm_error() on * failure. On failure last_error will be set with an error code. The * last_error will be OUTPUT_BUFFER_TOO_SMALL if the id buffer was too * small. */ size_t olm_outbound_group_session_id( OlmOutboundGroupSession *session, uint8_t * id, size_t id_length ); /** * Get the current message index for this session. * * Each message is sent with an increasing index; this returns the index for * the next message. */ uint32_t olm_outbound_group_session_message_index( OlmOutboundGroupSession *session ); /** * Get the number of bytes returned by olm_outbound_group_session_key() */ size_t olm_outbound_group_session_key_length( const OlmOutboundGroupSession *session ); /** * Get the base64-encoded current ratchet key for this session. * * Each message is sent with a different ratchet key. This function returns the * ratchet key that will be used for the next message. * * Returns the length of the ratchet key on success or olm_error() on * failure. On failure last_error will be set with an error code. The * last_error will be OUTPUT_BUFFER_TOO_SMALL if the buffer was too small. */ size_t olm_outbound_group_session_key( OlmOutboundGroupSession *session, uint8_t * key, size_t key_length ); #ifdef __cplusplus } // extern "C" #endif #endif /* OLM_OUTBOUND_GROUP_SESSION_H_ */ olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/pickle.h000066400000000000000000000053421311755073500215600ustar00rootroot00000000000000/* Copyright 2015-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. */ #ifndef OLM_PICKLE_H_ #define OLM_PICKLE_H_ #include #ifdef __cplusplus extern "C" { #endif struct _olm_ed25519_public_key; struct _olm_ed25519_key_pair; #define _olm_pickle_uint32_length(value) 4 uint8_t * _olm_pickle_uint32(uint8_t * pos, uint32_t value); uint8_t const * _olm_unpickle_uint32( uint8_t const * pos, uint8_t const * end, uint32_t *value ); #define _olm_pickle_bool_length(value) 1 uint8_t * _olm_pickle_bool(uint8_t * pos, int value); uint8_t const * _olm_unpickle_bool( uint8_t const * pos, uint8_t const * end, int *value ); #define _olm_pickle_bytes_length(bytes, bytes_length) (bytes_length) uint8_t * _olm_pickle_bytes(uint8_t * pos, uint8_t const * bytes, size_t bytes_length); uint8_t const * _olm_unpickle_bytes(uint8_t const * pos, uint8_t const * end, uint8_t * bytes, size_t bytes_length); /** Get the number of bytes needed to pickle an ed25519 public key */ size_t _olm_pickle_ed25519_public_key_length( const struct _olm_ed25519_public_key * value ); /** Pickle the ed25519 public key. Returns a pointer to the next free space in * the buffer. */ uint8_t * _olm_pickle_ed25519_public_key( uint8_t *pos, const struct _olm_ed25519_public_key * value ); /** Unpickle the ed25519 public key. Returns a pointer to the next item in the * buffer. */ const uint8_t * _olm_unpickle_ed25519_public_key( const uint8_t *pos, const uint8_t *end, struct _olm_ed25519_public_key * value ); /** Get the number of bytes needed to pickle an ed25519 key pair */ size_t _olm_pickle_ed25519_key_pair_length( const struct _olm_ed25519_key_pair * value ); /** Pickle the ed25519 key pair. Returns a pointer to the next free space in * the buffer. */ uint8_t * _olm_pickle_ed25519_key_pair( uint8_t *pos, const struct _olm_ed25519_key_pair * value ); /** Unpickle the ed25519 key pair. Returns a pointer to the next item in the * buffer. */ const uint8_t * _olm_unpickle_ed25519_key_pair( const uint8_t *pos, const uint8_t *end, struct _olm_ed25519_key_pair * value ); #ifdef __cplusplus } // extern "C" #endif #endif /* OLM_PICKLE_H */ olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/pickle.hh000066400000000000000000000061071311755073500217300ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OLM_PICKLE_HH_ #define OLM_PICKLE_HH_ #include "olm/list.hh" #include "olm/crypto.h" #include #include namespace olm { inline std::size_t pickle_length( const std::uint32_t & value ) { return 4; } std::uint8_t * pickle( std::uint8_t * pos, std::uint32_t value ); std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, std::uint32_t & value ); inline std::size_t pickle_length( const bool & value ) { return 1; } std::uint8_t * pickle( std::uint8_t * pos, bool value ); std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, bool & value ); template std::size_t pickle_length( olm::List const & list ) { std::size_t length = pickle_length(std::uint32_t(list.size())); for (auto const & value : list) { length += pickle_length(value); } return length; } template std::uint8_t * pickle( std::uint8_t * pos, olm::List const & list ) { pos = pickle(pos, std::uint32_t(list.size())); for (auto const & value : list) { pos = pickle(pos, value); } return pos; } template std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, olm::List & list ) { std::uint32_t size; pos = unpickle(pos, end, size); while (size-- && pos != end) { T * value = list.insert(list.end()); pos = unpickle(pos, end, *value); } return pos; } std::uint8_t * pickle_bytes( std::uint8_t * pos, std::uint8_t const * bytes, std::size_t bytes_length ); std::uint8_t const * unpickle_bytes( std::uint8_t const * pos, std::uint8_t const * end, std::uint8_t * bytes, std::size_t bytes_length ); std::size_t pickle_length( const _olm_curve25519_public_key & value ); std::uint8_t * pickle( std::uint8_t * pos, const _olm_curve25519_public_key & value ); std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, _olm_curve25519_public_key & value ); std::size_t pickle_length( const _olm_curve25519_key_pair & value ); std::uint8_t * pickle( std::uint8_t * pos, const _olm_curve25519_key_pair & value ); std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, _olm_curve25519_key_pair & value ); } // namespace olm #endif /* OLM_PICKLE_HH */ olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/pickle_encoding.h000066400000000000000000000041331311755073500234230ustar00rootroot00000000000000/* 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. */ /* functions for encrypting and decrypting pickled representations of objects */ #ifndef OLM_PICKLE_ENCODING_H_ #define OLM_PICKLE_ENCODING_H_ #include #include #include "olm/error.h" #ifdef __cplusplus extern "C" { #endif /** * Get the number of bytes needed to encode a pickle of the length given */ size_t _olm_enc_output_length(size_t raw_length); /** * Get the point in the output buffer that the raw pickle should be written to. * * In order that we can use the same buffer for the raw pickle, and the encoded * pickle, the raw pickle needs to be written at the end of the buffer. (The * base-64 encoding would otherwise overwrite the end of the input before it * was encoded.) */ uint8_t *_olm_enc_output_pos(uint8_t * output, size_t raw_length); /** * Encrypt and encode the given pickle in-situ. * * The raw pickle should have been written to enc_output_pos(pickle, * raw_length). * * Returns the number of bytes in the encoded pickle. */ size_t _olm_enc_output( uint8_t const * key, size_t key_length, uint8_t *pickle, size_t raw_length ); /** * Decode and decrypt the given pickle in-situ. * * Returns the number of bytes in the decoded pickle, or olm_error() on error, * in which case *last_error will be updated, if last_error is non-NULL. */ size_t _olm_enc_input( uint8_t const * key, size_t key_length, uint8_t * input, size_t b64_length, enum OlmErrorCode * last_error ); #ifdef __cplusplus } // extern "C" #endif #endif /* OLM_PICKLE_ENCODING_H_ */ olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/ratchet.hh000066400000000000000000000136001311755073500221070ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "olm/crypto.h" #include "olm/list.hh" #include "olm/error.h" struct _olm_cipher; namespace olm { /** length of a shared key: the root key R(i), chain key C(i,j), and message key * M(i,j)). They are all only used to stuff into HMACs, so could be any length * for that. The chain key and message key are both derived from SHA256 * operations, so their length is determined by that. */ const std::size_t OLM_SHARED_KEY_LENGTH = SHA256_OUTPUT_LENGTH; typedef std::uint8_t SharedKey[OLM_SHARED_KEY_LENGTH]; struct ChainKey { std::uint32_t index; SharedKey key; }; struct MessageKey { std::uint32_t index; SharedKey key; }; struct SenderChain { _olm_curve25519_key_pair ratchet_key; ChainKey chain_key; }; struct ReceiverChain { _olm_curve25519_public_key ratchet_key; ChainKey chain_key; }; struct SkippedMessageKey { _olm_curve25519_public_key ratchet_key; MessageKey message_key; }; static std::size_t const MAX_RECEIVER_CHAINS = 5; static std::size_t const MAX_SKIPPED_MESSAGE_KEYS = 40; struct KdfInfo { std::uint8_t const * root_info; std::size_t root_info_length; std::uint8_t const * ratchet_info; std::size_t ratchet_info_length; }; struct Ratchet { Ratchet( KdfInfo const & kdf_info, _olm_cipher const *ratchet_cipher ); /** A some strings identifying the application to feed into the KDF. */ KdfInfo const & kdf_info; /** The AEAD cipher to use for encrypting messages. */ _olm_cipher const *ratchet_cipher; /** The last error that happened encrypting or decrypting a message. */ OlmErrorCode last_error; /** The root key is used to generate chain keys from the ephemeral keys. * A new root_key derived each time a new chain is started. */ SharedKey root_key; /** The sender chain is used to send messages. Each time a new ephemeral * key is received from the remote server we generate a new sender chain * with a new empheral key when we next send a message. */ List sender_chain; /** The receiver chain is used to decrypt received messages. We store the * last few chains so we can decrypt any out of order messages we haven't * received yet. */ List receiver_chains; /** List of message keys we've skipped over when advancing the receiver * chain. */ List skipped_message_keys; /** Initialise the session using a shared secret and the public part of the * remote's first ratchet key */ void initialise_as_bob( std::uint8_t const * shared_secret, std::size_t shared_secret_length, _olm_curve25519_public_key const & their_ratchet_key ); /** Initialise the session using a shared secret and the public/private key * pair for the first ratchet key */ void initialise_as_alice( std::uint8_t const * shared_secret, std::size_t shared_secret_length, _olm_curve25519_key_pair const & our_ratchet_key ); /** The number of bytes of output the encrypt method will write for * a given message length. */ std::size_t encrypt_output_length( std::size_t plaintext_length ); /** The number of bytes of random data the encrypt method will need to * encrypt a message. This will be 32 bytes if the session needs to * generate a new ephemeral key, or will be 0 bytes otherwise.*/ std::size_t encrypt_random_length(); /** Encrypt some plain-text. Returns the length of the encrypted message * or std::size_t(-1) on failure. On failure last_error will be set with * an error code. The last_error will be NOT_ENOUGH_RANDOM if the number * of random bytes is too small. The last_error will be * OUTPUT_BUFFER_TOO_SMALL if the output buffer is too small. */ std::size_t encrypt( std::uint8_t const * plaintext, std::size_t plaintext_length, std::uint8_t const * random, std::size_t random_length, std::uint8_t * output, std::size_t max_output_length ); /** An upper bound on the number of bytes of plain-text the decrypt method * will write for a given input message length. */ std::size_t decrypt_max_plaintext_length( std::uint8_t const * input, std::size_t input_length ); /** Decrypt a message. Returns the length of the decrypted plain-text or * std::size_t(-1) on failure. On failure last_error will be set with an * error code. The last_error will be OUTPUT_BUFFER_TOO_SMALL if the * plain-text buffer is too small. The last_error will be * BAD_MESSAGE_VERSION if the message was encrypted with an unsupported * version of the protocol. The last_error will be BAD_MESSAGE_FORMAT if * the message headers could not be decoded. The last_error will be * BAD_MESSAGE_MAC if the message could not be verified */ std::size_t decrypt( std::uint8_t const * input, std::size_t input_length, std::uint8_t * plaintext, std::size_t max_plaintext_length ); }; std::size_t pickle_length( Ratchet const & value ); std::uint8_t * pickle( std::uint8_t * pos, Ratchet const & value ); std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, Ratchet & value, bool includes_chain_index ); } // namespace olm olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/session.hh000066400000000000000000000133121311755073500221400ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OLM_SESSION_HH_ #define OLM_SESSION_HH_ #include "olm/ratchet.hh" namespace olm { struct Account; enum struct MessageType { PRE_KEY = 0, MESSAGE = 1, }; struct Session { Session(); Ratchet ratchet; OlmErrorCode last_error; bool received_message; _olm_curve25519_public_key alice_identity_key; _olm_curve25519_public_key alice_base_key; _olm_curve25519_public_key bob_one_time_key; /** The number of random bytes that are needed to create a new outbound * session. This will be 64 bytes since two ephemeral keys are needed. */ std::size_t new_outbound_session_random_length(); /** Start a new outbound session. Returns std::size_t(-1) on failure. On * failure last_error will be set with an error code. The last_error will be * NOT_ENOUGH_RANDOM if the number of random bytes was too small. */ std::size_t new_outbound_session( Account const & local_account, _olm_curve25519_public_key const & identity_key, _olm_curve25519_public_key const & one_time_key, std::uint8_t const * random, std::size_t random_length ); /** Start a new inbound session from a pre-key message. * Returns std::size_t(-1) on failure. On failure last_error will be set * with an error code. The last_error will be BAD_MESSAGE_FORMAT if * the message headers could not be decoded. */ std::size_t new_inbound_session( Account & local_account, _olm_curve25519_public_key const * their_identity_key, std::uint8_t const * pre_key_message, std::size_t message_length ); /** The number of bytes written by session_id() */ std::size_t session_id_length(); /** An identifier for this session. Generated by hashing the public keys * used to create the session. Returns the length of the session id on * success or std::size_t(-1) on failure. On failure last_error will be set * with an error code. The last_error will be OUTPUT_BUFFER_TOO_SMALL if * the id buffer was too small. */ std::size_t session_id( std::uint8_t * id, std::size_t id_length ); /** True if this session can be used to decode an inbound pre-key message. * This can be used to test whether a pre-key message should be decoded * with an existing session or if a new session will need to be created. * Returns true if the session is the same. Returns false if either the * session does not match or the pre-key message could not be decoded. */ bool matches_inbound_session( _olm_curve25519_public_key const * their_identity_key, std::uint8_t const * pre_key_message, std::size_t message_length ); /** Whether the next message will be a pre-key message or a normal message. * An outbound session will send pre-key messages until it receives a * message with a ratchet key. */ MessageType encrypt_message_type(); std::size_t encrypt_message_length( std::size_t plaintext_length ); /** The number of bytes of random data the encrypt method will need to * encrypt a message. This will be 32 bytes if the session needs to * generate a new ephemeral key, or will be 0 bytes otherwise. */ std::size_t encrypt_random_length(); /** Encrypt some plain-text. Returns the length of the encrypted message * or std::size_t(-1) on failure. On failure last_error will be set with * an error code. The last_error will be NOT_ENOUGH_RANDOM if the number * of random bytes is too small. The last_error will be * OUTPUT_BUFFER_TOO_SMALL if the output buffer is too small. */ std::size_t encrypt( std::uint8_t const * plaintext, std::size_t plaintext_length, std::uint8_t const * random, std::size_t random_length, std::uint8_t * message, std::size_t message_length ); /** An upper bound on the number of bytes of plain-text the decrypt method * will write for a given input message length. */ std::size_t decrypt_max_plaintext_length( MessageType message_type, std::uint8_t const * message, std::size_t message_length ); /** Decrypt a message. Returns the length of the decrypted plain-text or * std::size_t(-1) on failure. On failure last_error will be set with an * error code. The last_error will be OUTPUT_BUFFER_TOO_SMALL if the * plain-text buffer is too small. The last_error will be * BAD_MESSAGE_VERSION if the message was encrypted with an unsupported * version of the protocol. The last_error will be BAD_MESSAGE_FORMAT if * the message headers could not be decoded. The last_error will be * BAD_MESSAGE_MAC if the message could not be verified */ std::size_t decrypt( MessageType message_type, std::uint8_t const * message, std::size_t message_length, std::uint8_t * plaintext, std::size_t max_plaintext_length ); }; std::size_t pickle_length( Session const & value ); std::uint8_t * pickle( std::uint8_t * pos, Session const & value ); std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, Session & value ); } // namespace olm #endif /* OLM_SESSION_HH_ */ olm-2.2.2+git20170526.0fd768e+dfsg/include/olm/utility.hh000066400000000000000000000036071311755073500221660ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef UTILITY_HH_ #define UTILITY_HH_ #include "olm/error.h" #include #include struct _olm_ed25519_public_key; namespace olm { struct Utility { Utility(); OlmErrorCode last_error; /** The length of a SHA-256 hash in bytes. */ std::size_t sha256_length(); /** Compute a SHA-256 hash. Returns the length of the SHA-256 hash in bytes * on success. Returns std::size_t(-1) on failure. On failure last_error * will be set with an error code. If the output buffer was too small then * last error will be OUTPUT_BUFFER_TOO_SMALL. */ std::size_t sha256( std::uint8_t const * input, std::size_t input_length, std::uint8_t * output, std::size_t output_length ); /** Verify a ed25519 signature. Returns std::size_t(0) on success. Returns * std::size_t(-1) on failure or if the signature was invalid. On failure * last_error will be set with an error code. If the signature was too short * or was not a valid signature then last_error will be BAD_MESSAGE_MAC. */ std::size_t ed25519_verify( _olm_ed25519_public_key const & key, std::uint8_t const * message, std::size_t message_length, std::uint8_t const * signature, std::size_t signature_length ); }; } // namespace olm #endif /* UTILITY_HH_ */ olm-2.2.2+git20170526.0fd768e+dfsg/javascript/000077500000000000000000000000001311755073500200705ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/javascript/.gitignore000066400000000000000000000001071311755073500220560ustar00rootroot00000000000000/exported_functions.json /node_modules /npm-debug.log /olm.js /reports olm-2.2.2+git20170526.0fd768e+dfsg/javascript/README.md000066400000000000000000000023761311755073500213570ustar00rootroot00000000000000Olm === Example: var alice = new Olm.Account(); var bob = new Olm.Account(); alice.create(); bob.create(); bob.generate_one_time_keys(1); var bobs_id_keys = JSON.parse(bob.identity_keys()); var bobs_id_key = bobs_id_keys.curve25519; var bobs_ot_keys = JSON.parse(bob.one_time_keys()); for (key in bobs_ot_keys.curve25519) { var bobs_ot_key = bobs_ot_keys.curve25519[key]; } alice_session = new Olm.Session(); alice_session.create_outbound(alice, bobs_id_key, bobs_ot_key); alice_message = a_session.encrypt("Hello"); bob_session.create_inbound(bob, bob_message); var plaintext = bob_session.decrypt(message_1.type, bob_message); bob.remove_one_time_keys(bob_session); Group chat: var outbound_session = new Olm.OutboundGroupSession(); outbound_session.create(); // exchange these over a secure channel var session_id = group_session.session_id(); var session_key = group_session.session_key(); var message_index = group_session.message_index(); var inbound_session = new Olm.InboundGroupSession(); inbound_session.create(message_index, session_key); var ciphertext = outbound_session.encrypt("Hello"); var plaintext = inbound_session.decrypt(ciphertext); olm-2.2.2+git20170526.0fd768e+dfsg/javascript/demo/000077500000000000000000000000001311755073500210145ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/javascript/demo/demo.css000066400000000000000000000002221311755073500224460ustar00rootroot00000000000000div.user { width: 500px; float: left; overflow: scroll; margin: 0px 20px 0px 0px; border: 1px solid black; padding: 5px; }olm-2.2.2+git20170526.0fd768e+dfsg/javascript/demo/group_demo.html000066400000000000000000000023751311755073500240510ustar00rootroot00000000000000

User1

Outgoing

One-to-one output

Group output

Incoming

One-to-one Received

Group received

Tasks

User 2

Outgoing

One-to-one output

Group output

Incoming

One-to-one Received

Group received

Tasks

olm-2.2.2+git20170526.0fd768e+dfsg/javascript/demo/group_demo.js000066400000000000000000000337361311755073500235260ustar00rootroot00000000000000/* Javascript parts of the group demo. To use, load group_demo.html in your * browser. */ function buttonElement(buttonLabel, clickHandler) { var button = document.createElement("button"); button.appendChild(document.createTextNode(buttonLabel)); button.addEventListener("click", clickHandler, false); return button; } function buttonsAndText(textContent, buttonLabelToHandlerMap) { var el = document.createElement("div"); for (var label in buttonLabelToHandlerMap) { if (!buttonLabelToHandlerMap.hasOwnProperty(label)) { continue; } var handler = buttonLabelToHandlerMap[label]; var button = buttonElement(label, handler); el.appendChild(button); } var message_element = document.createElement("tt"); el.appendChild(message_element); var content = document.createTextNode(textContent); message_element.appendChild(content); return el; } function buttonAndTextElement(buttonLabel, textContent, clickHandler) { var buttonMap = {}; buttonMap[buttonLabel] = clickHandler; return buttonsAndText(textContent, buttonMap); } function DemoUser(name) { this.name = name; this.olmAccount = new Olm.Account(); this.olmAccount.create(); this.idKey = this.getIdKeys()["curve25519"]; /* the people in our chat, indexed by their Curve25519 identity key. */ this.peers = {}; /* the Ed25519 signing key for each peer, indexed by their Curve25519 id key */ this.peerSigningKeys = {}; /* for each peer, a one-to-one session - indexed by id key and created on * demand */ this.peerSessions = {}; /* for each peer, info on their sender session - indexed by id key and * session id */ this.peerGroupSessions = {}; /* our outbound group session */ this.groupSession = undefined; /* a list of pending tasks */ this.tasks = []; this.taskWorker = undefined; /* the operations our peers are allowed to do on us */ var publicOps = [ "getIdKeys", "getOneTimeKey", "receiveOneToOne", "receiveGroup", ]; this.remoteOps = {}; for (var i=0; i "+result); el2.appendChild(content); var body = JSON.parse(result); // create a new inbound session if we don't yet have one if (!self.peerGroupSessions[sender] || !self.peerGroupSessions[sender][body.session_id]) { self.createInboundSession( sender, body.session_id, body.session_key ); } }); }); this.cipherInputDiv.appendChild(el); }; /** * add a task to decrypt a one-to-one message. Calls the callback with the * decrypted plaintext */ DemoUser.prototype.decryptOneToOne = function(jsonpacket, callback) { var self = this; self.addTask("decrypt one-to-one message", function(done) { var packet = JSON.parse(jsonpacket); var peerId = packet.sender_key; var session = self.peerSessions[peerId]; var plaintext; if (session) { plaintext = session.decrypt(packet.ciphertext.type, packet.ciphertext.body); done(plaintext); return; } if (packet.ciphertext.type != 0) { throw new Error("Unknown one-to-one session"); } session = new Olm.Session(); session.create_inbound(self.olmAccount, packet.ciphertext.body); self.peerSessions[peerId] = session; plaintext = session.decrypt(packet.ciphertext.type, packet.ciphertext.body); done(plaintext); }, callback) }; /* ************************************************************************ * * group messaging */ /** * retrieve, or initiate, an outbound group session */ DemoUser.prototype.getGroupSession = function() { if (this.groupSession) { return this.groupSession; } this.groupSession = new Olm.OutboundGroupSession(); this.groupSession.create(); var keymsg = { "session_id": this.groupSession.session_id(), "session_key": this.groupSession.session_key(), "message_index": this.groupSession.message_index(), }; var jsonmsg = JSON.stringify(keymsg); for (var peer in this.peers) { if (!this.peers.hasOwnProperty(peer)) { continue; } this.sendToPeer(peer, jsonmsg); } return this.groupSession; }; /** * add a task to create an inbound group session */ DemoUser.prototype.createInboundSession = function( peer_id, session_id, session_key, callback ) { var self = this; this.addTask("init inbound session", function(done) { session = new Olm.InboundGroupSession(); session.create(session_key); if (!self.peerGroupSessions[peer_id]) { self.peerGroupSessions[peer_id] = {}; } if (session_id != session.session_id()) { throw new Error("Mismatched session_ids"); } self.peerGroupSessions[peer_id][session_id] = session; done(session); }, callback); }; /** * handler for receiving a group message */ DemoUser.prototype.receiveGroup = function(jsonpacket) { var self = this; var el = buttonAndTextElement("decrypt", jsonpacket, function(ev) { self.decryptGroup(jsonpacket, function(result) { var el2 = document.createElement("tt"); el.appendChild(el2); var content = document.createTextNode(" -> "+result); el2.appendChild(content); }); }); this.groupInputDiv.appendChild(el); }; /** * add a task to decrypt a received group message. Calls the callback with the * decrypted plaintext */ DemoUser.prototype.decryptGroup = function(jsonpacket, callback) { var self = this; this.addTask("decrypt group message", function(done) { var packet = JSON.parse(jsonpacket); var sender = packet.sender_key; var session_id = packet.session_id; var sender_signing_key = self.peerSigningKeys[sender]; if (!sender_signing_key) { throw new Error("No known signing key for sender "+sender); } var olmUtility = new Olm.Utility(); olmUtility.ed25519_verify( sender_signing_key, packet.body, packet.signature ); var peer_sessions = self.peerGroupSessions[sender]; if (!peer_sessions) { throw new Error("No sessions for sender "+sender); } var session = peer_sessions[session_id]; if (!session) { throw new Error("Unknown session id " + session_id); } var result = session.decrypt(packet.body); done(result.plaintext); }, callback); }; /** * add a task to encrypt, and prepare for sending, a group message. * * Will create a group session if necessary */ DemoUser.prototype.encrypt = function(message) { var self = this; var session = this.getGroupSession(); function sendJsonToPeers(json) { for (var peer in self.peers) { if (!self.peers.hasOwnProperty(peer)) { continue; } self.peers[peer].receiveGroup(json); } } self.addTask("encrypt group message", function(done) { var encrypted = session.encrypt(message); var signature = self.olmAccount.sign(encrypted); var packet = { sender_key: self.idKey, session_id: session.session_id(), body: encrypted, signature: signature, }; var json = JSON.stringify(packet); var el = buttonsAndText(json, { send: function(ev) { sendJsonToPeers(json); }, "send corrupted": function(ev) { var p = JSON.parse(json); p.body += " "; sendJsonToPeers(JSON.stringify(p)); }, }); self.groupOutputDiv.appendChild(el); done(); }); }; /* ************************************************************************** */ function initUserDiv(demoUser, div) { demoUser.progressElement = div.getElementsByClassName("user_progress")[0]; demoUser.cipherOutputDiv = div.getElementsByClassName("user_cipher_output")[0]; demoUser.cipherInputDiv = div.getElementsByClassName("user_cipher_input")[0]; demoUser.groupOutputDiv = div.getElementsByClassName("group_output")[0]; demoUser.groupInputDiv = div.getElementsByClassName("group_input")[0]; var plain_input = div.getElementsByClassName("user_plain_input")[0]; var encrypt = div.getElementsByClassName("user_encrypt")[0]; encrypt.addEventListener("click", function() { demoUser.encrypt(plain_input.value); }, false); } function startDemo() { var user1 = new DemoUser(); initUserDiv(user1, document.getElementById("user1")); user1.generateKeys(); var user2 = new DemoUser(); initUserDiv(user2, document.getElementById("user2")); user2.generateKeys(); user1.addPeer(user2.remoteOps); user2.addPeer(user1.remoteOps); } document.addEventListener("DOMContentLoaded", startDemo, false); olm-2.2.2+git20170526.0fd768e+dfsg/javascript/demo/one_to_one_demo.html000066400000000000000000000120731311755073500250350ustar00rootroot00000000000000

Alice

Encryption

Decryption

Bob

Encryption

Decryption

olm-2.2.2+git20170526.0fd768e+dfsg/javascript/olm_inbound_group_session.js000066400000000000000000000125441311755073500257200ustar00rootroot00000000000000/* The 'length' argument to Pointer_stringify doesn't work if the input includes * characters >= 128; we therefore need to add a NULL character to all of our * strings. This acts as a symbolic constant to help show what we're doing. */ var NULL_BYTE_PADDING_LENGTH = 1; function InboundGroupSession() { var size = Module['_olm_inbound_group_session_size'](); this.buf = malloc(size); this.ptr = Module['_olm_inbound_group_session'](this.buf); } function inbound_group_session_method(wrapped) { return function() { var result = wrapped.apply(this, arguments); if (result === OLM_ERROR) { var message = Pointer_stringify( Module['_olm_inbound_group_session_last_error'](arguments[0]) ); throw new Error("OLM." + message); } return result; } } InboundGroupSession.prototype['free'] = function() { Module['_olm_clear_inbound_group_session'](this.ptr); free(this.ptr); } InboundGroupSession.prototype['pickle'] = restore_stack(function(key) { var key_array = array_from_string(key); var pickle_length = inbound_group_session_method( Module['_olm_pickle_inbound_group_session_length'] )(this.ptr); var key_buffer = stack(key_array); var pickle_buffer = stack(pickle_length + NULL_BYTE_PADDING_LENGTH); inbound_group_session_method(Module['_olm_pickle_inbound_group_session'])( this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length ); return Pointer_stringify(pickle_buffer); }); InboundGroupSession.prototype['unpickle'] = restore_stack(function(key, pickle) { var key_array = array_from_string(key); var key_buffer = stack(key_array); var pickle_array = array_from_string(pickle); var pickle_buffer = stack(pickle_array); inbound_group_session_method(Module['_olm_unpickle_inbound_group_session'])( this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_array.length ); }); InboundGroupSession.prototype['create'] = restore_stack(function(session_key) { var key_array = array_from_string(session_key); var key_buffer = stack(key_array); inbound_group_session_method(Module['_olm_init_inbound_group_session'])( this.ptr, key_buffer, key_array.length ); }); InboundGroupSession.prototype['import_session'] = restore_stack(function(session_key) { var key_array = array_from_string(session_key); var key_buffer = stack(key_array); inbound_group_session_method(Module['_olm_import_inbound_group_session'])( this.ptr, key_buffer, key_array.length ); }); InboundGroupSession.prototype['decrypt'] = restore_stack(function( message ) { var message_buffer, plaintext_buffer, plaintext_length; try { message_buffer = malloc(message.length); Module['writeAsciiToMemory'](message, message_buffer, true); var max_plaintext_length = inbound_group_session_method( Module['_olm_group_decrypt_max_plaintext_length'] )(this.ptr, message_buffer, message.length); // caculating the length destroys the input buffer, so we need to re-copy it. Module['writeAsciiToMemory'](message, message_buffer, true); plaintext_buffer = malloc(max_plaintext_length + NULL_BYTE_PADDING_LENGTH); var message_index = stack(4); plaintext_length = inbound_group_session_method( Module["_olm_group_decrypt"] )( this.ptr, message_buffer, message.length, plaintext_buffer, max_plaintext_length, message_index ); // UTF8ToString requires a null-terminated argument, so add the // null terminator. Module['setValue']( plaintext_buffer+plaintext_length, 0, "i8" ); return { "plaintext": UTF8ToString(plaintext_buffer), "message_index": Module['getValue'](message_index, "i32") } } finally { if (message_buffer !== undefined) { free(message_buffer); } if (plaintext_buffer !== undefined) { // don't leave a copy of the plaintext in the heap. bzero(plaintext_buffer, plaintext_length + NULL_BYTE_PADDING_LENGTH); free(plaintext_buffer); } } }); InboundGroupSession.prototype['session_id'] = restore_stack(function() { var length = inbound_group_session_method( Module['_olm_inbound_group_session_id_length'] )(this.ptr); var session_id = stack(length + NULL_BYTE_PADDING_LENGTH); inbound_group_session_method(Module['_olm_inbound_group_session_id'])( this.ptr, session_id, length ); return Pointer_stringify(session_id); }); InboundGroupSession.prototype['first_known_index'] = restore_stack(function() { return inbound_group_session_method( Module['_olm_inbound_group_session_first_known_index'] )(this.ptr); }); InboundGroupSession.prototype['export_session'] = restore_stack(function(message_index) { var key_length = inbound_group_session_method( Module['_olm_export_inbound_group_session_length'] )(this.ptr); var key = stack(key_length + NULL_BYTE_PADDING_LENGTH); outbound_group_session_method(Module['_olm_export_inbound_group_session'])( this.ptr, key, key_length, message_index ); return Pointer_stringify(key); }); olm_exports['InboundGroupSession'] = InboundGroupSession; olm-2.2.2+git20170526.0fd768e+dfsg/javascript/olm_outbound_group_session.js000066400000000000000000000115571311755073500261240ustar00rootroot00000000000000/* The 'length' argument to Pointer_stringify doesn't work if the input includes * characters >= 128; we therefore need to add a NULL character to all of our * strings. This acts as a symbolic constant to help show what we're doing. */ var NULL_BYTE_PADDING_LENGTH = 1; function OutboundGroupSession() { var size = Module['_olm_outbound_group_session_size'](); this.buf = malloc(size); this.ptr = Module['_olm_outbound_group_session'](this.buf); } function outbound_group_session_method(wrapped) { return function() { var result = wrapped.apply(this, arguments); if (result === OLM_ERROR) { var message = Pointer_stringify( Module['_olm_outbound_group_session_last_error'](arguments[0]) ); throw new Error("OLM." + message); } return result; } } OutboundGroupSession.prototype['free'] = function() { Module['_olm_clear_outbound_group_session'](this.ptr); free(this.ptr); } OutboundGroupSession.prototype['pickle'] = restore_stack(function(key) { var key_array = array_from_string(key); var pickle_length = outbound_group_session_method( Module['_olm_pickle_outbound_group_session_length'] )(this.ptr); var key_buffer = stack(key_array); var pickle_buffer = stack(pickle_length + NULL_BYTE_PADDING_LENGTH); outbound_group_session_method(Module['_olm_pickle_outbound_group_session'])( this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length ); return Pointer_stringify(pickle_buffer); }); OutboundGroupSession.prototype['unpickle'] = restore_stack(function(key, pickle) { var key_array = array_from_string(key); var key_buffer = stack(key_array); var pickle_array = array_from_string(pickle); var pickle_buffer = stack(pickle_array); outbound_group_session_method(Module['_olm_unpickle_outbound_group_session'])( this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_array.length ); }); OutboundGroupSession.prototype['create'] = restore_stack(function() { var random_length = outbound_group_session_method( Module['_olm_init_outbound_group_session_random_length'] )(this.ptr); var random = random_stack(random_length); outbound_group_session_method(Module['_olm_init_outbound_group_session'])( this.ptr, random, random_length ); }); OutboundGroupSession.prototype['encrypt'] = function(plaintext) { var plaintext_buffer, message_buffer, plaintext_length; try { plaintext_length = Module['lengthBytesUTF8'](plaintext); var message_length = outbound_group_session_method( Module['_olm_group_encrypt_message_length'] )(this.ptr, plaintext_length); // need to allow space for the terminator (which stringToUTF8 always // writes), hence + 1. plaintext_buffer = malloc(plaintext_length + 1); Module['stringToUTF8'](plaintext, plaintext_buffer, plaintext_length + 1); message_buffer = malloc(message_length + NULL_BYTE_PADDING_LENGTH); outbound_group_session_method(Module['_olm_group_encrypt'])( this.ptr, plaintext_buffer, plaintext_length, message_buffer, message_length ); // UTF8ToString requires a null-terminated argument, so add the // null terminator. Module['setValue']( message_buffer+message_length, 0, "i8" ); return Module['UTF8ToString'](message_buffer); } finally { if (plaintext_buffer !== undefined) { // don't leave a copy of the plaintext in the heap. bzero(plaintext_buffer, plaintext_length + 1); free(plaintext_buffer); } if (message_buffer !== undefined) { free(message_buffer); } } }; OutboundGroupSession.prototype['session_id'] = restore_stack(function() { var length = outbound_group_session_method( Module['_olm_outbound_group_session_id_length'] )(this.ptr); var session_id = stack(length + NULL_BYTE_PADDING_LENGTH); outbound_group_session_method(Module['_olm_outbound_group_session_id'])( this.ptr, session_id, length ); return Pointer_stringify(session_id); }); OutboundGroupSession.prototype['session_key'] = restore_stack(function() { var key_length = outbound_group_session_method( Module['_olm_outbound_group_session_key_length'] )(this.ptr); var key = stack(key_length + NULL_BYTE_PADDING_LENGTH); outbound_group_session_method(Module['_olm_outbound_group_session_key'])( this.ptr, key, key_length ); return Pointer_stringify(key); }); OutboundGroupSession.prototype['message_index'] = function() { var idx = outbound_group_session_method( Module['_olm_outbound_group_session_message_index'] )(this.ptr); return idx; }; olm_exports['OutboundGroupSession'] = OutboundGroupSession; olm-2.2.2+git20170526.0fd768e+dfsg/javascript/olm_post.js000066400000000000000000000374771311755073500223040ustar00rootroot00000000000000var runtime = Module['Runtime']; var malloc = Module['_malloc']; var free = Module['_free']; var Pointer_stringify = Module['Pointer_stringify']; var OLM_ERROR = Module['_olm_error'](); /* The 'length' argument to Pointer_stringify doesn't work if the input * includes characters >= 128, which makes Pointer_stringify unreliable. We * could use it on strings which are known to be ascii, but that seems * dangerous. Instead we add a NULL character to all of our strings and just * use UTF8ToString. */ var NULL_BYTE_PADDING_LENGTH = 1; /* allocate a number of bytes of storage on the stack. * * If size_or_array is a Number, allocates that number of zero-initialised bytes. */ function stack(size_or_array) { return Module['allocate'](size_or_array, 'i8', Module['ALLOC_STACK']); } function array_from_string(string) { return Module['intArrayFromString'](string, true); } function random_stack(size) { var ptr = stack(size); var array = new Uint8Array(Module['HEAPU8'].buffer, ptr, size); get_random_values(array); return ptr; } function restore_stack(wrapped) { return function() { var sp = runtime.stackSave(); try { return wrapped.apply(this, arguments); } finally { runtime.stackRestore(sp); } } } /* set a memory area to zero */ function bzero(ptr, n) { while(n-- > 0) { Module['HEAP8'][ptr++] = 0; } } function Account() { var size = Module['_olm_account_size'](); this.buf = malloc(size); this.ptr = Module['_olm_account'](this.buf); } function account_method(wrapped) { return function() { var result = wrapped.apply(this, arguments); if (result === OLM_ERROR) { var message = Pointer_stringify( Module['_olm_account_last_error'](arguments[0]) ); throw new Error("OLM." + message); } return result; } } Account.prototype['free'] = function() { Module['_olm_clear_account'](this.ptr); free(this.ptr); } Account.prototype['create'] = restore_stack(function() { var random_length = account_method( Module['_olm_create_account_random_length'] )(this.ptr); var random = random_stack(random_length); account_method(Module['_olm_create_account'])( this.ptr, random, random_length ); }); Account.prototype['identity_keys'] = restore_stack(function() { var keys_length = account_method( Module['_olm_account_identity_keys_length'] )(this.ptr); var keys = stack(keys_length + NULL_BYTE_PADDING_LENGTH); account_method(Module['_olm_account_identity_keys'])( this.ptr, keys, keys_length ); return Pointer_stringify(keys); }); Account.prototype['sign'] = restore_stack(function(message) { var signature_length = account_method( Module['_olm_account_signature_length'] )(this.ptr); var message_array = array_from_string(message); var message_buffer = stack(message_array); var signature_buffer = stack(signature_length + NULL_BYTE_PADDING_LENGTH); account_method(Module['_olm_account_sign'])( this.ptr, message_buffer, message_array.length, signature_buffer, signature_length ); return Pointer_stringify(signature_buffer); }); Account.prototype['one_time_keys'] = restore_stack(function() { var keys_length = account_method( Module['_olm_account_one_time_keys_length'] )(this.ptr); var keys = stack(keys_length + NULL_BYTE_PADDING_LENGTH); account_method(Module['_olm_account_one_time_keys'])( this.ptr, keys, keys_length ); return Pointer_stringify(keys); }); Account.prototype['mark_keys_as_published'] = restore_stack(function() { account_method(Module['_olm_account_mark_keys_as_published'])(this.ptr); }); Account.prototype['max_number_of_one_time_keys'] = restore_stack(function() { return account_method(Module['_olm_account_max_number_of_one_time_keys'])( this.ptr ); }); Account.prototype['generate_one_time_keys'] = restore_stack(function( number_of_keys ) { var random_length = account_method( Module['_olm_account_generate_one_time_keys_random_length'] )(this.ptr, number_of_keys); var random = random_stack(random_length); account_method(Module['_olm_account_generate_one_time_keys'])( this.ptr, number_of_keys, random, random_length ); }); Account.prototype['remove_one_time_keys'] = restore_stack(function(session) { account_method(Module['_olm_remove_one_time_keys'])( this.ptr, session.ptr ); }); Account.prototype['pickle'] = restore_stack(function(key) { var key_array = array_from_string(key); var pickle_length = account_method( Module['_olm_pickle_account_length'] )(this.ptr); var key_buffer = stack(key_array); var pickle_buffer = stack(pickle_length + NULL_BYTE_PADDING_LENGTH); account_method(Module['_olm_pickle_account'])( this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length ); return Pointer_stringify(pickle_buffer); }); Account.prototype['unpickle'] = restore_stack(function(key, pickle) { var key_array = array_from_string(key); var key_buffer = stack(key_array); var pickle_array = array_from_string(pickle); var pickle_buffer = stack(pickle_array); account_method(Module['_olm_unpickle_account'])( this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_array.length ); }); function Session() { var size = Module['_olm_session_size'](); this.buf = malloc(size); this.ptr = Module['_olm_session'](this.buf); } function session_method(wrapped) { return function() { var result = wrapped.apply(this, arguments); if (result === OLM_ERROR) { var message = Pointer_stringify( Module['_olm_session_last_error'](arguments[0]) ); throw new Error("OLM." + message); } return result; } } Session.prototype['free'] = function() { Module['_olm_clear_session'](this.ptr); free(this.ptr); } Session.prototype['pickle'] = restore_stack(function(key) { var key_array = array_from_string(key); var pickle_length = session_method( Module['_olm_pickle_session_length'] )(this.ptr); var key_buffer = stack(key_array); var pickle_buffer = stack(pickle_length + NULL_BYTE_PADDING_LENGTH); session_method(Module['_olm_pickle_session'])( this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length ); return Pointer_stringify(pickle_buffer); }); Session.prototype['unpickle'] = restore_stack(function(key, pickle) { var key_array = array_from_string(key); var key_buffer = stack(key_array); var pickle_array = array_from_string(pickle); var pickle_buffer = stack(pickle_array); session_method(Module['_olm_unpickle_session'])( this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_array.length ); }); Session.prototype['create_outbound'] = restore_stack(function( account, their_identity_key, their_one_time_key ) { var random_length = session_method( Module['_olm_create_outbound_session_random_length'] )(this.ptr); var random = random_stack(random_length); var identity_key_array = array_from_string(their_identity_key); var one_time_key_array = array_from_string(their_one_time_key); var identity_key_buffer = stack(identity_key_array); var one_time_key_buffer = stack(one_time_key_array); session_method(Module['_olm_create_outbound_session'])( this.ptr, account.ptr, identity_key_buffer, identity_key_array.length, one_time_key_buffer, one_time_key_array.length, random, random_length ); }); Session.prototype['create_inbound'] = restore_stack(function( account, one_time_key_message ) { var message_array = array_from_string(one_time_key_message); var message_buffer = stack(message_array); session_method(Module['_olm_create_inbound_session'])( this.ptr, account.ptr, message_buffer, message_array.length ); }); Session.prototype['create_inbound_from'] = restore_stack(function( account, identity_key, one_time_key_message ) { var identity_key_array = array_from_string(identity_key); var identity_key_buffer = stack(identity_key_array); var message_array = array_from_string(one_time_key_message); var message_buffer = stack(message_array); session_method(Module['_olm_create_inbound_session_from'])( this.ptr, account.ptr, identity_key_buffer, identity_key_array.length, message_buffer, message_array.length ); }); Session.prototype['session_id'] = restore_stack(function() { var id_length = session_method(Module['_olm_session_id_length'])(this.ptr); var id_buffer = stack(id_length + NULL_BYTE_PADDING_LENGTH); session_method(Module['_olm_session_id'])( this.ptr, id_buffer, id_length ); return Pointer_stringify(id_buffer); }); Session.prototype['has_received_message'] = function() { return session_method(Module['_olm_session_has_received_message'])( this.ptr ) ? true : false; }; Session.prototype['matches_inbound'] = restore_stack(function( one_time_key_message ) { var message_array = array_from_string(one_time_key_message); var message_buffer = stack(message_array); return session_method(Module['_olm_matches_inbound_session'])( this.ptr, message_buffer, message_array.length ) ? true : false; }); Session.prototype['matches_inbound_from'] = restore_stack(function( identity_key, one_time_key_message ) { var identity_key_array = array_from_string(identity_key); var identity_key_buffer = stack(identity_key_array); var message_array = array_from_string(one_time_key_message); var message_buffer = stack(message_array); return session_method(Module['_olm_matches_inbound_session_from'])( this.ptr, identity_key_buffer, identity_key_array.length, message_buffer, message_array.length ) ? true : false; }); Session.prototype['encrypt'] = restore_stack(function( plaintext ) { var plaintext_buffer, message_buffer, plaintext_length; try { var random_length = session_method( Module['_olm_encrypt_random_length'] )(this.ptr); var message_type = session_method( Module['_olm_encrypt_message_type'] )(this.ptr); plaintext_length = Module['lengthBytesUTF8'](plaintext); var message_length = session_method( Module['_olm_encrypt_message_length'] )(this.ptr, plaintext_length); var random = random_stack(random_length); // need to allow space for the terminator (which stringToUTF8 always // writes), hence + 1. plaintext_buffer = malloc(plaintext_length + 1); Module['stringToUTF8'](plaintext, plaintext_buffer, plaintext_length + 1); message_buffer = malloc(message_length + NULL_BYTE_PADDING_LENGTH); session_method(Module['_olm_encrypt'])( this.ptr, plaintext_buffer, plaintext_length, random, random_length, message_buffer, message_length ); // UTF8ToString requires a null-terminated argument, so add the // null terminator. Module['setValue']( message_buffer+message_length, 0, "i8" ); return { "type": message_type, "body": Module['UTF8ToString'](message_buffer), }; } finally { if (plaintext_buffer !== undefined) { // don't leave a copy of the plaintext in the heap. bzero(plaintext_buffer, plaintext_length + 1); free(plaintext_buffer); } if (message_buffer !== undefined) { free(message_buffer); } } }); Session.prototype['decrypt'] = restore_stack(function( message_type, message ) { var message_buffer, plaintext_buffer, max_plaintext_length; try { message_buffer = malloc(message.length); Module['writeAsciiToMemory'](message, message_buffer, true); max_plaintext_length = session_method( Module['_olm_decrypt_max_plaintext_length'] )(this.ptr, message_type, message_buffer, message.length); // caculating the length destroys the input buffer, so we need to re-copy it. Module['writeAsciiToMemory'](message, message_buffer, true); plaintext_buffer = malloc(max_plaintext_length + NULL_BYTE_PADDING_LENGTH); var plaintext_length = session_method(Module["_olm_decrypt"])( this.ptr, message_type, message_buffer, message.length, plaintext_buffer, max_plaintext_length ); // UTF8ToString requires a null-terminated argument, so add the // null terminator. Module['setValue']( plaintext_buffer+plaintext_length, 0, "i8" ); return UTF8ToString(plaintext_buffer); } finally { if (message_buffer !== undefined) { free(message_buffer); } if (plaintext_buffer !== undefined) { // don't leave a copy of the plaintext in the heap. bzero(plaintext_buffer, max_plaintext_length + NULL_BYTE_PADDING_LENGTH); free(plaintext_buffer); } } }); function Utility() { var size = Module['_olm_utility_size'](); this.buf = malloc(size); this.ptr = Module['_olm_utility'](this.buf); } function utility_method(wrapped) { return function() { var result = wrapped.apply(this, arguments); if (result === OLM_ERROR) { var message = Pointer_stringify( Module['_olm_utility_last_error'](arguments[0]) ); throw new Error("OLM." + message); } return result; } } Utility.prototype['free'] = function() { Module['_olm_clear_utility'](this.ptr); free(this.ptr); } Utility.prototype['sha256'] = restore_stack(function(input) { var output_length = utility_method(Module['_olm_sha256_length'])(this.ptr); var input_array = array_from_string(input); var input_buffer = stack(input_array); var output_buffer = stack(output_length + NULL_BYTE_PADDING_LENGTH); utility_method(Module['_olm_sha2516'])( this.ptr, input_buffer, input_array.length(), output_buffer, output_length ); return Pointer_stringify(output_buffer); }); Utility.prototype['ed25519_verify'] = restore_stack(function( key, message, signature ) { var key_array = array_from_string(key); var key_buffer = stack(key_array); var message_array = array_from_string(message); var message_buffer = stack(message_array); var signature_array = array_from_string(signature); var signature_buffer = stack(signature_array); utility_method(Module['_olm_ed25519_verify'])( this.ptr, key_buffer, key_array.length, message_buffer, message_array.length, signature_buffer, signature_array.length ); }); olm_exports["Account"] = Account; olm_exports["Session"] = Session; olm_exports["Utility"] = Utility; olm_exports["get_library_version"] = restore_stack(function() { var buf = stack(3); Module['_olm_get_library_version'](buf, buf+1, buf+2); return [ getValue(buf, 'i8'), getValue(buf+1, 'i8'), getValue(buf+2, 'i8'), ]; }); })(); // export the olm functions into the environment. // // make sure that we do this *after* populating olm_exports, so that we don't // get a half-built window.Olm if there is an exception. if (typeof module !== 'undefined' && module.exports) { // node / browserify module.exports = olm_exports; } if (typeof(window) !== 'undefined') { // We've been imported directly into a browser. Define the global 'Olm' object. // (we do this even if module.exports was defined, because it's useful to have // Olm in the global scope for browserified and webpacked apps.) window["Olm"] = olm_exports; } olm-2.2.2+git20170526.0fd768e+dfsg/javascript/olm_pre.js000066400000000000000000000020411311755073500220600ustar00rootroot00000000000000var olm_exports = {}; var get_random_values; var process; // Shadow the process object so that emscripten won't get // confused by browserify if (typeof(window) !== 'undefined') { // We've in a browser (directly, via browserify, or via webpack). get_random_values = function(buf) { window.crypto.getRandomValues(buf); }; } else if (module["exports"]) { // We're running in node. var nodeCrypto = require("crypto"); get_random_values = function(buf) { var bytes = nodeCrypto.randomBytes(buf.length); buf.set(bytes); }; process = global["process"]; } else { throw new Error("Cannot find global to attach library to"); } (function() { /* applications should define OLM_OPTIONS in the environment to override * emscripten module settings */ var Module = {}; if (typeof(OLM_OPTIONS) !== 'undefined') { for (var key in OLM_OPTIONS) { if (OLM_OPTIONS.hasOwnProperty(key)) { Module[key] = OLM_OPTIONS[key]; } } } olm-2.2.2+git20170526.0fd768e+dfsg/javascript/package.json000066400000000000000000000012651311755073500223620ustar00rootroot00000000000000{ "name": "olm", "version": "2.2.2", "description": "An implementation of the Double Ratchet cryptographic ratchet", "main": "olm.js", "files": [ "olm.js", "README.md" ], "scripts": { "build": "make -C .. js", "test": "jasmine-node test --verbose --junitreport --captureExceptions" }, "repository": { "type": "git", "url": "git+https://github.com/matrix-org/olm.git" }, "keywords": [ "matrix-org" ], "author": "matrix.org", "license": "Apache-2.0", "bugs": { "url": "https://github.com/matrix-org/olm/issues" }, "homepage": "https://github.com/matrix-org/olm#readme", "devDependencies": { "jasmine-node": "^1.14.5" } } olm-2.2.2+git20170526.0fd768e+dfsg/javascript/test/000077500000000000000000000000001311755073500210475ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/javascript/test/megolm.spec.js000066400000000000000000000042771311755073500236300ustar00rootroot00000000000000/* 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. */ "use strict"; var Olm = require('../olm'); describe("megolm", function() { var aliceSession, bobSession; beforeEach(function() { aliceSession = new Olm.OutboundGroupSession(); bobSession = new Olm.InboundGroupSession(); }); afterEach(function() { if (aliceSession !== undefined) { aliceSession.free(); aliceSession = undefined; } if (bobSession !== undefined) { bobSession.free(); bobSession = undefined; } }); it("should encrypt and decrypt", function() { aliceSession.create(); expect(aliceSession.message_index()).toEqual(0); bobSession.create(aliceSession.session_key()); var TEST_TEXT='têst1'; var encrypted = aliceSession.encrypt(TEST_TEXT); var decrypted = bobSession.decrypt(encrypted); console.log(TEST_TEXT, "->", decrypted); expect(decrypted.plaintext).toEqual(TEST_TEXT); expect(decrypted.message_index).toEqual(0); TEST_TEXT='hot beverage: ☕'; encrypted = aliceSession.encrypt(TEST_TEXT); decrypted = bobSession.decrypt(encrypted); console.log(TEST_TEXT, "->", decrypted); expect(decrypted.plaintext).toEqual(TEST_TEXT); expect(decrypted.message_index).toEqual(1); // shorter text, to spot buffer overruns TEST_TEXT='☕'; encrypted = aliceSession.encrypt(TEST_TEXT); decrypted = bobSession.decrypt(encrypted); console.log(TEST_TEXT, "->", decrypted); expect(decrypted.plaintext).toEqual(TEST_TEXT); expect(decrypted.message_index).toEqual(2); }); }); olm-2.2.2+git20170526.0fd768e+dfsg/javascript/test/olm.spec.js000066400000000000000000000054531311755073500231340ustar00rootroot00000000000000/* 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. */ "use strict"; var Olm = require('../olm'); if (!Object.keys) { Object.keys = function(o) { var k=[], p; for (p in o) if (Object.prototype.hasOwnProperty.call(o,p)) k.push(p); return k; } } describe("olm", function() { var aliceAccount, bobAccount; var aliceSession, bobSession; beforeEach(function() { aliceAccount = new Olm.Account(); bobAccount = new Olm.Account(); aliceSession = new Olm.Session(); bobSession = new Olm.Session(); }); afterEach(function() { if (aliceAccount !== undefined) { aliceAccount.free(); aliceAccount = undefined; } if (bobAccount !== undefined) { bobAccount.free(); bobAccount = undefined; } if (aliceSession !== undefined) { aliceSession.free(); aliceSession = undefined; } if (bobSession !== undefined) { bobSession.free(); bobSession = undefined; } }); it('should encrypt and decrypt', function() { aliceAccount.create(); bobAccount.create(); bobAccount.generate_one_time_keys(1); var bobOneTimeKeys = JSON.parse(bobAccount.one_time_keys()).curve25519; bobAccount.mark_keys_as_published(); var bobIdKey = JSON.parse(bobAccount.identity_keys()).curve25519; var otk_id = Object.keys(bobOneTimeKeys)[0]; aliceSession.create_outbound( aliceAccount, bobIdKey, bobOneTimeKeys[otk_id] ); var TEST_TEXT='têst1'; var encrypted = aliceSession.encrypt(TEST_TEXT); expect(encrypted.type).toEqual(0); bobSession.create_inbound(bobAccount, encrypted.body); bobAccount.remove_one_time_keys(bobSession); var decrypted = bobSession.decrypt(encrypted.type, encrypted.body); console.log(TEST_TEXT, "->", decrypted); expect(decrypted).toEqual(TEST_TEXT); TEST_TEXT='hot beverage: ☕'; encrypted = bobSession.encrypt(TEST_TEXT); expect(encrypted.type).toEqual(1); decrypted = aliceSession.decrypt(encrypted.type, encrypted.body); console.log(TEST_TEXT, "->", decrypted); expect(decrypted).toEqual(TEST_TEXT); }); }); olm-2.2.2+git20170526.0fd768e+dfsg/jenkins.sh000077500000000000000000000003111311755073500177150ustar00rootroot00000000000000#!/bin/sh set -e make clean rm -f olm-*.tgz make lib make test ./python/test_olm.sh pep8 -v python . ~/.emsdk_set_env.sh make js (cd javascript && npm install && npm run test) npm pack javascript olm-2.2.2+git20170526.0fd768e+dfsg/lib/000077500000000000000000000000001311755073500164705ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/000077500000000000000000000000001311755073500221575ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/README.md000066400000000000000000000030501311755073500234340ustar00rootroot00000000000000crypto-algorithms ================= About --- These are basic implementations of standard cryptography algorithms, written by Brad Conte (brad@bradconte.com) from scratch and without any cross-licensing. They exist to provide publically accessible, restriction-free implementations of popular cryptographic algorithms, like AES and SHA-1. These are primarily intended for educational and pragmatic purposes (such as comparing a specification to actual implementation code, or for building an internal application that computes test vectors for a product). The algorithms have been tested against standard test vectors. This code is released into the public domain free of any restrictions. The author requests acknowledgement if the code is used, but does not require it. This code is provided free of any liability and without any quality claims by the author. Note that these are *not* cryptographically secure implementations. They have no resistence to side-channel attacks and should not be used in contexts that need cryptographically secure implementations. These algorithms are not optimized for speed or space. They are primarily designed to be easy to read, although some basic optimization techniques have been employed. Building --- The source code for each algorithm will come in a pair of a source code file and a header file. There should be no inter-header file dependencies, no additional libraries, no platform-specific header files, or any other complicating matters. Compiling them should be as easy as adding the relevent source code to the project.olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/aes.c000066400000000000000000001212571311755073500231030ustar00rootroot00000000000000/********************************************************************* * Filename: aes.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: This code is the implementation of the AES algorithm and the CTR, CBC, and CCM modes of operation it can be used in. AES is, specified by the NIST in in publication FIPS PUB 197, availible at: * http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf . The CBC and CTR modes of operation are specified by NIST SP 800-38 A, available at: * http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf . The CCM mode of operation is specified by NIST SP80-38 C, available at: * http://csrc.nist.gov/publications/nistpubs/800-38C/SP800-38C_updated-July20_2007.pdf *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include #include "aes.h" #include #include /****************************** MACROS ******************************/ // The least significant byte of the word is rotated to the end. #define KE_ROTWORD(x) (((x) << 8) | ((x) >> 24)) #define TRUE 1 #define FALSE 0 /**************************** DATA TYPES ****************************/ #define AES_128_ROUNDS 10 #define AES_192_ROUNDS 12 #define AES_256_ROUNDS 14 /*********************** FUNCTION DECLARATIONS **********************/ void ccm_prepare_first_ctr_blk(BYTE counter[], const BYTE nonce[], int nonce_len, int payload_len_store_size); void ccm_prepare_first_format_blk(BYTE buf[], int assoc_len, int payload_len, int payload_len_store_size, int mac_len, const BYTE nonce[], int nonce_len); void ccm_format_assoc_data(BYTE buf[], int *end_of_buf, const BYTE assoc[], int assoc_len); void ccm_format_payload_data(BYTE buf[], int *end_of_buf, const BYTE payload[], int payload_len); /**************************** VARIABLES *****************************/ // This is the specified AES SBox. To look up a substitution value, put the first // nibble in the first index (row) and the second nibble in the second index (column). static const BYTE aes_sbox[16][16] = { {0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76}, {0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0}, {0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15}, {0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75}, {0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84}, {0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF}, {0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8}, {0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2}, {0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73}, {0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB}, {0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79}, {0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08}, {0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A}, {0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E}, {0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF}, {0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16} }; static const BYTE aes_invsbox[16][16] = { {0x52,0x09,0x6A,0xD5,0x30,0x36,0xA5,0x38,0xBF,0x40,0xA3,0x9E,0x81,0xF3,0xD7,0xFB}, {0x7C,0xE3,0x39,0x82,0x9B,0x2F,0xFF,0x87,0x34,0x8E,0x43,0x44,0xC4,0xDE,0xE9,0xCB}, {0x54,0x7B,0x94,0x32,0xA6,0xC2,0x23,0x3D,0xEE,0x4C,0x95,0x0B,0x42,0xFA,0xC3,0x4E}, {0x08,0x2E,0xA1,0x66,0x28,0xD9,0x24,0xB2,0x76,0x5B,0xA2,0x49,0x6D,0x8B,0xD1,0x25}, {0x72,0xF8,0xF6,0x64,0x86,0x68,0x98,0x16,0xD4,0xA4,0x5C,0xCC,0x5D,0x65,0xB6,0x92}, {0x6C,0x70,0x48,0x50,0xFD,0xED,0xB9,0xDA,0x5E,0x15,0x46,0x57,0xA7,0x8D,0x9D,0x84}, {0x90,0xD8,0xAB,0x00,0x8C,0xBC,0xD3,0x0A,0xF7,0xE4,0x58,0x05,0xB8,0xB3,0x45,0x06}, {0xD0,0x2C,0x1E,0x8F,0xCA,0x3F,0x0F,0x02,0xC1,0xAF,0xBD,0x03,0x01,0x13,0x8A,0x6B}, {0x3A,0x91,0x11,0x41,0x4F,0x67,0xDC,0xEA,0x97,0xF2,0xCF,0xCE,0xF0,0xB4,0xE6,0x73}, {0x96,0xAC,0x74,0x22,0xE7,0xAD,0x35,0x85,0xE2,0xF9,0x37,0xE8,0x1C,0x75,0xDF,0x6E}, {0x47,0xF1,0x1A,0x71,0x1D,0x29,0xC5,0x89,0x6F,0xB7,0x62,0x0E,0xAA,0x18,0xBE,0x1B}, {0xFC,0x56,0x3E,0x4B,0xC6,0xD2,0x79,0x20,0x9A,0xDB,0xC0,0xFE,0x78,0xCD,0x5A,0xF4}, {0x1F,0xDD,0xA8,0x33,0x88,0x07,0xC7,0x31,0xB1,0x12,0x10,0x59,0x27,0x80,0xEC,0x5F}, {0x60,0x51,0x7F,0xA9,0x19,0xB5,0x4A,0x0D,0x2D,0xE5,0x7A,0x9F,0x93,0xC9,0x9C,0xEF}, {0xA0,0xE0,0x3B,0x4D,0xAE,0x2A,0xF5,0xB0,0xC8,0xEB,0xBB,0x3C,0x83,0x53,0x99,0x61}, {0x17,0x2B,0x04,0x7E,0xBA,0x77,0xD6,0x26,0xE1,0x69,0x14,0x63,0x55,0x21,0x0C,0x7D} }; // This table stores pre-calculated values for all possible GF(2^8) calculations.This // table is only used by the (Inv)MixColumns steps. // USAGE: The second index (column) is the coefficient of multiplication. Only 7 different // coefficients are used: 0x01, 0x02, 0x03, 0x09, 0x0b, 0x0d, 0x0e, but multiplication by // 1 is negligible leaving only 6 coefficients. Each column of the table is devoted to one // of these coefficients, in the ascending order of value, from values 0x00 to 0xFF. static const BYTE gf_mul[256][6] = { {0x00,0x00,0x00,0x00,0x00,0x00},{0x02,0x03,0x09,0x0b,0x0d,0x0e}, {0x04,0x06,0x12,0x16,0x1a,0x1c},{0x06,0x05,0x1b,0x1d,0x17,0x12}, {0x08,0x0c,0x24,0x2c,0x34,0x38},{0x0a,0x0f,0x2d,0x27,0x39,0x36}, {0x0c,0x0a,0x36,0x3a,0x2e,0x24},{0x0e,0x09,0x3f,0x31,0x23,0x2a}, {0x10,0x18,0x48,0x58,0x68,0x70},{0x12,0x1b,0x41,0x53,0x65,0x7e}, {0x14,0x1e,0x5a,0x4e,0x72,0x6c},{0x16,0x1d,0x53,0x45,0x7f,0x62}, {0x18,0x14,0x6c,0x74,0x5c,0x48},{0x1a,0x17,0x65,0x7f,0x51,0x46}, {0x1c,0x12,0x7e,0x62,0x46,0x54},{0x1e,0x11,0x77,0x69,0x4b,0x5a}, {0x20,0x30,0x90,0xb0,0xd0,0xe0},{0x22,0x33,0x99,0xbb,0xdd,0xee}, {0x24,0x36,0x82,0xa6,0xca,0xfc},{0x26,0x35,0x8b,0xad,0xc7,0xf2}, {0x28,0x3c,0xb4,0x9c,0xe4,0xd8},{0x2a,0x3f,0xbd,0x97,0xe9,0xd6}, {0x2c,0x3a,0xa6,0x8a,0xfe,0xc4},{0x2e,0x39,0xaf,0x81,0xf3,0xca}, {0x30,0x28,0xd8,0xe8,0xb8,0x90},{0x32,0x2b,0xd1,0xe3,0xb5,0x9e}, {0x34,0x2e,0xca,0xfe,0xa2,0x8c},{0x36,0x2d,0xc3,0xf5,0xaf,0x82}, {0x38,0x24,0xfc,0xc4,0x8c,0xa8},{0x3a,0x27,0xf5,0xcf,0x81,0xa6}, {0x3c,0x22,0xee,0xd2,0x96,0xb4},{0x3e,0x21,0xe7,0xd9,0x9b,0xba}, {0x40,0x60,0x3b,0x7b,0xbb,0xdb},{0x42,0x63,0x32,0x70,0xb6,0xd5}, {0x44,0x66,0x29,0x6d,0xa1,0xc7},{0x46,0x65,0x20,0x66,0xac,0xc9}, {0x48,0x6c,0x1f,0x57,0x8f,0xe3},{0x4a,0x6f,0x16,0x5c,0x82,0xed}, {0x4c,0x6a,0x0d,0x41,0x95,0xff},{0x4e,0x69,0x04,0x4a,0x98,0xf1}, {0x50,0x78,0x73,0x23,0xd3,0xab},{0x52,0x7b,0x7a,0x28,0xde,0xa5}, {0x54,0x7e,0x61,0x35,0xc9,0xb7},{0x56,0x7d,0x68,0x3e,0xc4,0xb9}, {0x58,0x74,0x57,0x0f,0xe7,0x93},{0x5a,0x77,0x5e,0x04,0xea,0x9d}, {0x5c,0x72,0x45,0x19,0xfd,0x8f},{0x5e,0x71,0x4c,0x12,0xf0,0x81}, {0x60,0x50,0xab,0xcb,0x6b,0x3b},{0x62,0x53,0xa2,0xc0,0x66,0x35}, {0x64,0x56,0xb9,0xdd,0x71,0x27},{0x66,0x55,0xb0,0xd6,0x7c,0x29}, {0x68,0x5c,0x8f,0xe7,0x5f,0x03},{0x6a,0x5f,0x86,0xec,0x52,0x0d}, {0x6c,0x5a,0x9d,0xf1,0x45,0x1f},{0x6e,0x59,0x94,0xfa,0x48,0x11}, {0x70,0x48,0xe3,0x93,0x03,0x4b},{0x72,0x4b,0xea,0x98,0x0e,0x45}, {0x74,0x4e,0xf1,0x85,0x19,0x57},{0x76,0x4d,0xf8,0x8e,0x14,0x59}, {0x78,0x44,0xc7,0xbf,0x37,0x73},{0x7a,0x47,0xce,0xb4,0x3a,0x7d}, {0x7c,0x42,0xd5,0xa9,0x2d,0x6f},{0x7e,0x41,0xdc,0xa2,0x20,0x61}, {0x80,0xc0,0x76,0xf6,0x6d,0xad},{0x82,0xc3,0x7f,0xfd,0x60,0xa3}, {0x84,0xc6,0x64,0xe0,0x77,0xb1},{0x86,0xc5,0x6d,0xeb,0x7a,0xbf}, {0x88,0xcc,0x52,0xda,0x59,0x95},{0x8a,0xcf,0x5b,0xd1,0x54,0x9b}, {0x8c,0xca,0x40,0xcc,0x43,0x89},{0x8e,0xc9,0x49,0xc7,0x4e,0x87}, {0x90,0xd8,0x3e,0xae,0x05,0xdd},{0x92,0xdb,0x37,0xa5,0x08,0xd3}, {0x94,0xde,0x2c,0xb8,0x1f,0xc1},{0x96,0xdd,0x25,0xb3,0x12,0xcf}, {0x98,0xd4,0x1a,0x82,0x31,0xe5},{0x9a,0xd7,0x13,0x89,0x3c,0xeb}, {0x9c,0xd2,0x08,0x94,0x2b,0xf9},{0x9e,0xd1,0x01,0x9f,0x26,0xf7}, {0xa0,0xf0,0xe6,0x46,0xbd,0x4d},{0xa2,0xf3,0xef,0x4d,0xb0,0x43}, {0xa4,0xf6,0xf4,0x50,0xa7,0x51},{0xa6,0xf5,0xfd,0x5b,0xaa,0x5f}, {0xa8,0xfc,0xc2,0x6a,0x89,0x75},{0xaa,0xff,0xcb,0x61,0x84,0x7b}, {0xac,0xfa,0xd0,0x7c,0x93,0x69},{0xae,0xf9,0xd9,0x77,0x9e,0x67}, {0xb0,0xe8,0xae,0x1e,0xd5,0x3d},{0xb2,0xeb,0xa7,0x15,0xd8,0x33}, {0xb4,0xee,0xbc,0x08,0xcf,0x21},{0xb6,0xed,0xb5,0x03,0xc2,0x2f}, {0xb8,0xe4,0x8a,0x32,0xe1,0x05},{0xba,0xe7,0x83,0x39,0xec,0x0b}, {0xbc,0xe2,0x98,0x24,0xfb,0x19},{0xbe,0xe1,0x91,0x2f,0xf6,0x17}, {0xc0,0xa0,0x4d,0x8d,0xd6,0x76},{0xc2,0xa3,0x44,0x86,0xdb,0x78}, {0xc4,0xa6,0x5f,0x9b,0xcc,0x6a},{0xc6,0xa5,0x56,0x90,0xc1,0x64}, {0xc8,0xac,0x69,0xa1,0xe2,0x4e},{0xca,0xaf,0x60,0xaa,0xef,0x40}, {0xcc,0xaa,0x7b,0xb7,0xf8,0x52},{0xce,0xa9,0x72,0xbc,0xf5,0x5c}, {0xd0,0xb8,0x05,0xd5,0xbe,0x06},{0xd2,0xbb,0x0c,0xde,0xb3,0x08}, {0xd4,0xbe,0x17,0xc3,0xa4,0x1a},{0xd6,0xbd,0x1e,0xc8,0xa9,0x14}, {0xd8,0xb4,0x21,0xf9,0x8a,0x3e},{0xda,0xb7,0x28,0xf2,0x87,0x30}, {0xdc,0xb2,0x33,0xef,0x90,0x22},{0xde,0xb1,0x3a,0xe4,0x9d,0x2c}, {0xe0,0x90,0xdd,0x3d,0x06,0x96},{0xe2,0x93,0xd4,0x36,0x0b,0x98}, {0xe4,0x96,0xcf,0x2b,0x1c,0x8a},{0xe6,0x95,0xc6,0x20,0x11,0x84}, {0xe8,0x9c,0xf9,0x11,0x32,0xae},{0xea,0x9f,0xf0,0x1a,0x3f,0xa0}, {0xec,0x9a,0xeb,0x07,0x28,0xb2},{0xee,0x99,0xe2,0x0c,0x25,0xbc}, {0xf0,0x88,0x95,0x65,0x6e,0xe6},{0xf2,0x8b,0x9c,0x6e,0x63,0xe8}, {0xf4,0x8e,0x87,0x73,0x74,0xfa},{0xf6,0x8d,0x8e,0x78,0x79,0xf4}, {0xf8,0x84,0xb1,0x49,0x5a,0xde},{0xfa,0x87,0xb8,0x42,0x57,0xd0}, {0xfc,0x82,0xa3,0x5f,0x40,0xc2},{0xfe,0x81,0xaa,0x54,0x4d,0xcc}, {0x1b,0x9b,0xec,0xf7,0xda,0x41},{0x19,0x98,0xe5,0xfc,0xd7,0x4f}, {0x1f,0x9d,0xfe,0xe1,0xc0,0x5d},{0x1d,0x9e,0xf7,0xea,0xcd,0x53}, {0x13,0x97,0xc8,0xdb,0xee,0x79},{0x11,0x94,0xc1,0xd0,0xe3,0x77}, {0x17,0x91,0xda,0xcd,0xf4,0x65},{0x15,0x92,0xd3,0xc6,0xf9,0x6b}, {0x0b,0x83,0xa4,0xaf,0xb2,0x31},{0x09,0x80,0xad,0xa4,0xbf,0x3f}, {0x0f,0x85,0xb6,0xb9,0xa8,0x2d},{0x0d,0x86,0xbf,0xb2,0xa5,0x23}, {0x03,0x8f,0x80,0x83,0x86,0x09},{0x01,0x8c,0x89,0x88,0x8b,0x07}, {0x07,0x89,0x92,0x95,0x9c,0x15},{0x05,0x8a,0x9b,0x9e,0x91,0x1b}, {0x3b,0xab,0x7c,0x47,0x0a,0xa1},{0x39,0xa8,0x75,0x4c,0x07,0xaf}, {0x3f,0xad,0x6e,0x51,0x10,0xbd},{0x3d,0xae,0x67,0x5a,0x1d,0xb3}, {0x33,0xa7,0x58,0x6b,0x3e,0x99},{0x31,0xa4,0x51,0x60,0x33,0x97}, {0x37,0xa1,0x4a,0x7d,0x24,0x85},{0x35,0xa2,0x43,0x76,0x29,0x8b}, {0x2b,0xb3,0x34,0x1f,0x62,0xd1},{0x29,0xb0,0x3d,0x14,0x6f,0xdf}, {0x2f,0xb5,0x26,0x09,0x78,0xcd},{0x2d,0xb6,0x2f,0x02,0x75,0xc3}, {0x23,0xbf,0x10,0x33,0x56,0xe9},{0x21,0xbc,0x19,0x38,0x5b,0xe7}, {0x27,0xb9,0x02,0x25,0x4c,0xf5},{0x25,0xba,0x0b,0x2e,0x41,0xfb}, {0x5b,0xfb,0xd7,0x8c,0x61,0x9a},{0x59,0xf8,0xde,0x87,0x6c,0x94}, {0x5f,0xfd,0xc5,0x9a,0x7b,0x86},{0x5d,0xfe,0xcc,0x91,0x76,0x88}, {0x53,0xf7,0xf3,0xa0,0x55,0xa2},{0x51,0xf4,0xfa,0xab,0x58,0xac}, {0x57,0xf1,0xe1,0xb6,0x4f,0xbe},{0x55,0xf2,0xe8,0xbd,0x42,0xb0}, {0x4b,0xe3,0x9f,0xd4,0x09,0xea},{0x49,0xe0,0x96,0xdf,0x04,0xe4}, {0x4f,0xe5,0x8d,0xc2,0x13,0xf6},{0x4d,0xe6,0x84,0xc9,0x1e,0xf8}, {0x43,0xef,0xbb,0xf8,0x3d,0xd2},{0x41,0xec,0xb2,0xf3,0x30,0xdc}, {0x47,0xe9,0xa9,0xee,0x27,0xce},{0x45,0xea,0xa0,0xe5,0x2a,0xc0}, {0x7b,0xcb,0x47,0x3c,0xb1,0x7a},{0x79,0xc8,0x4e,0x37,0xbc,0x74}, {0x7f,0xcd,0x55,0x2a,0xab,0x66},{0x7d,0xce,0x5c,0x21,0xa6,0x68}, {0x73,0xc7,0x63,0x10,0x85,0x42},{0x71,0xc4,0x6a,0x1b,0x88,0x4c}, {0x77,0xc1,0x71,0x06,0x9f,0x5e},{0x75,0xc2,0x78,0x0d,0x92,0x50}, {0x6b,0xd3,0x0f,0x64,0xd9,0x0a},{0x69,0xd0,0x06,0x6f,0xd4,0x04}, {0x6f,0xd5,0x1d,0x72,0xc3,0x16},{0x6d,0xd6,0x14,0x79,0xce,0x18}, {0x63,0xdf,0x2b,0x48,0xed,0x32},{0x61,0xdc,0x22,0x43,0xe0,0x3c}, {0x67,0xd9,0x39,0x5e,0xf7,0x2e},{0x65,0xda,0x30,0x55,0xfa,0x20}, {0x9b,0x5b,0x9a,0x01,0xb7,0xec},{0x99,0x58,0x93,0x0a,0xba,0xe2}, {0x9f,0x5d,0x88,0x17,0xad,0xf0},{0x9d,0x5e,0x81,0x1c,0xa0,0xfe}, {0x93,0x57,0xbe,0x2d,0x83,0xd4},{0x91,0x54,0xb7,0x26,0x8e,0xda}, {0x97,0x51,0xac,0x3b,0x99,0xc8},{0x95,0x52,0xa5,0x30,0x94,0xc6}, {0x8b,0x43,0xd2,0x59,0xdf,0x9c},{0x89,0x40,0xdb,0x52,0xd2,0x92}, {0x8f,0x45,0xc0,0x4f,0xc5,0x80},{0x8d,0x46,0xc9,0x44,0xc8,0x8e}, {0x83,0x4f,0xf6,0x75,0xeb,0xa4},{0x81,0x4c,0xff,0x7e,0xe6,0xaa}, {0x87,0x49,0xe4,0x63,0xf1,0xb8},{0x85,0x4a,0xed,0x68,0xfc,0xb6}, {0xbb,0x6b,0x0a,0xb1,0x67,0x0c},{0xb9,0x68,0x03,0xba,0x6a,0x02}, {0xbf,0x6d,0x18,0xa7,0x7d,0x10},{0xbd,0x6e,0x11,0xac,0x70,0x1e}, {0xb3,0x67,0x2e,0x9d,0x53,0x34},{0xb1,0x64,0x27,0x96,0x5e,0x3a}, {0xb7,0x61,0x3c,0x8b,0x49,0x28},{0xb5,0x62,0x35,0x80,0x44,0x26}, {0xab,0x73,0x42,0xe9,0x0f,0x7c},{0xa9,0x70,0x4b,0xe2,0x02,0x72}, {0xaf,0x75,0x50,0xff,0x15,0x60},{0xad,0x76,0x59,0xf4,0x18,0x6e}, {0xa3,0x7f,0x66,0xc5,0x3b,0x44},{0xa1,0x7c,0x6f,0xce,0x36,0x4a}, {0xa7,0x79,0x74,0xd3,0x21,0x58},{0xa5,0x7a,0x7d,0xd8,0x2c,0x56}, {0xdb,0x3b,0xa1,0x7a,0x0c,0x37},{0xd9,0x38,0xa8,0x71,0x01,0x39}, {0xdf,0x3d,0xb3,0x6c,0x16,0x2b},{0xdd,0x3e,0xba,0x67,0x1b,0x25}, {0xd3,0x37,0x85,0x56,0x38,0x0f},{0xd1,0x34,0x8c,0x5d,0x35,0x01}, {0xd7,0x31,0x97,0x40,0x22,0x13},{0xd5,0x32,0x9e,0x4b,0x2f,0x1d}, {0xcb,0x23,0xe9,0x22,0x64,0x47},{0xc9,0x20,0xe0,0x29,0x69,0x49}, {0xcf,0x25,0xfb,0x34,0x7e,0x5b},{0xcd,0x26,0xf2,0x3f,0x73,0x55}, {0xc3,0x2f,0xcd,0x0e,0x50,0x7f},{0xc1,0x2c,0xc4,0x05,0x5d,0x71}, {0xc7,0x29,0xdf,0x18,0x4a,0x63},{0xc5,0x2a,0xd6,0x13,0x47,0x6d}, {0xfb,0x0b,0x31,0xca,0xdc,0xd7},{0xf9,0x08,0x38,0xc1,0xd1,0xd9}, {0xff,0x0d,0x23,0xdc,0xc6,0xcb},{0xfd,0x0e,0x2a,0xd7,0xcb,0xc5}, {0xf3,0x07,0x15,0xe6,0xe8,0xef},{0xf1,0x04,0x1c,0xed,0xe5,0xe1}, {0xf7,0x01,0x07,0xf0,0xf2,0xf3},{0xf5,0x02,0x0e,0xfb,0xff,0xfd}, {0xeb,0x13,0x79,0x92,0xb4,0xa7},{0xe9,0x10,0x70,0x99,0xb9,0xa9}, {0xef,0x15,0x6b,0x84,0xae,0xbb},{0xed,0x16,0x62,0x8f,0xa3,0xb5}, {0xe3,0x1f,0x5d,0xbe,0x80,0x9f},{0xe1,0x1c,0x54,0xb5,0x8d,0x91}, {0xe7,0x19,0x4f,0xa8,0x9a,0x83},{0xe5,0x1a,0x46,0xa3,0x97,0x8d} }; /*********************** FUNCTION DEFINITIONS ***********************/ // XORs the in and out buffers, storing the result in out. Length is in bytes. void xor_buf(const BYTE in[], BYTE out[], size_t len) { size_t idx; for (idx = 0; idx < len; idx++) out[idx] ^= in[idx]; } /******************* * AES - CBC *******************/ int aes_encrypt_cbc(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) { BYTE buf_in[AES_BLOCK_SIZE], buf_out[AES_BLOCK_SIZE]; int blocks, idx; if (in_len % AES_BLOCK_SIZE != 0) return(FALSE); blocks = in_len / AES_BLOCK_SIZE; memcpy(buf_out, iv, AES_BLOCK_SIZE); for (idx = 0; idx < blocks; idx++) { memcpy(buf_in, &in[idx * AES_BLOCK_SIZE], AES_BLOCK_SIZE); xor_buf(buf_out, buf_in, AES_BLOCK_SIZE); aes_encrypt(buf_in, buf_out, key, keysize); memcpy(&out[idx * AES_BLOCK_SIZE], buf_out, AES_BLOCK_SIZE); } return(TRUE); } int aes_encrypt_cbc_mac(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) { BYTE buf_in[AES_BLOCK_SIZE], buf_out[AES_BLOCK_SIZE]; int blocks, idx; if (in_len % AES_BLOCK_SIZE != 0) return(FALSE); blocks = in_len / AES_BLOCK_SIZE; memcpy(buf_out, iv, AES_BLOCK_SIZE); for (idx = 0; idx < blocks; idx++) { memcpy(buf_in, &in[idx * AES_BLOCK_SIZE], AES_BLOCK_SIZE); xor_buf(buf_out, buf_in, AES_BLOCK_SIZE); aes_encrypt(buf_in, buf_out, key, keysize); // Do not output all encrypted blocks. } memcpy(out, buf_out, AES_BLOCK_SIZE); // Only output the last block. return(TRUE); } // No need for an aes_decrypt_cbc() for just CCM. /******************* * AES - CTR *******************/ void increment_iv(BYTE iv[], int counter_size) { int idx; // Use counter_size bytes at the end of the IV as the big-endian integer to increment. for (idx = AES_BLOCK_SIZE - 1; idx >= AES_BLOCK_SIZE - counter_size; idx--) { iv[idx]++; if (iv[idx] != 0 || idx == AES_BLOCK_SIZE - counter_size) break; } } // Performs the encryption in-place, the input and output buffers may be the same. // Input may be an arbitrary length (in bytes). void aes_encrypt_ctr(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) { size_t idx = 0, last_block_length; BYTE iv_buf[AES_BLOCK_SIZE], out_buf[AES_BLOCK_SIZE]; if (in != out) memcpy(out, in, in_len); memcpy(iv_buf, iv, AES_BLOCK_SIZE); last_block_length = in_len - AES_BLOCK_SIZE; if (in_len > AES_BLOCK_SIZE) { for (idx = 0; idx < last_block_length; idx += AES_BLOCK_SIZE) { aes_encrypt(iv_buf, out_buf, key, keysize); xor_buf(out_buf, &out[idx], AES_BLOCK_SIZE); increment_iv(iv_buf, AES_BLOCK_SIZE); } } aes_encrypt(iv_buf, out_buf, key, keysize); xor_buf(out_buf, &out[idx], in_len - idx); // Use the Most Significant bytes. } void aes_decrypt_ctr(const BYTE in[], size_t in_len, BYTE out[], const WORD key[], int keysize, const BYTE iv[]) { // CTR encryption is its own inverse function. aes_encrypt_ctr(in, in_len, out, key, keysize, iv); } /******************* * AES - CCM *******************/ // out_len = payload_len + assoc_len int aes_encrypt_ccm(const BYTE payload[], WORD payload_len, const BYTE assoc[], unsigned short assoc_len, const BYTE nonce[], unsigned short nonce_len, BYTE out[], WORD *out_len, WORD mac_len, const BYTE key_str[], int keysize) { BYTE temp_iv[AES_BLOCK_SIZE], counter[AES_BLOCK_SIZE], mac[16], *buf; int end_of_buf, payload_len_store_size; WORD key[60]; if (mac_len != 4 && mac_len != 6 && mac_len != 8 && mac_len != 10 && mac_len != 12 && mac_len != 14 && mac_len != 16) return(FALSE); if (nonce_len < 7 || nonce_len > 13) return(FALSE); if (assoc_len > 32768 /* = 2^15 */) return(FALSE); buf = (BYTE*)malloc(payload_len + assoc_len + 48 /*Round both payload and associated data up a block size and add an extra block.*/); if (! buf) return(FALSE); // Prepare the key for usage. aes_key_setup(key_str, key, keysize); // Format the first block of the formatted data. payload_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len; ccm_prepare_first_format_blk(buf, assoc_len, payload_len, payload_len_store_size, mac_len, nonce, nonce_len); end_of_buf = AES_BLOCK_SIZE; // Format the Associated Data, aka, assoc[]. ccm_format_assoc_data(buf, &end_of_buf, assoc, assoc_len); // Format the Payload, aka payload[]. ccm_format_payload_data(buf, &end_of_buf, payload, payload_len); // Create the first counter block. ccm_prepare_first_ctr_blk(counter, nonce, nonce_len, payload_len_store_size); // Perform the CBC operation with an IV of zeros on the formatted buffer to calculate the MAC. memset(temp_iv, 0, AES_BLOCK_SIZE); aes_encrypt_cbc_mac(buf, end_of_buf, mac, key, keysize, temp_iv); // Copy the Payload and MAC to the output buffer. memcpy(out, payload, payload_len); memcpy(&out[payload_len], mac, mac_len); // Encrypt the Payload with CTR mode with a counter starting at 1. memcpy(temp_iv, counter, AES_BLOCK_SIZE); increment_iv(temp_iv, AES_BLOCK_SIZE - 1 - mac_len); // Last argument is the byte size of the counting portion of the counter block. /*BUG?*/ aes_encrypt_ctr(out, payload_len, out, key, keysize, temp_iv); // Encrypt the MAC with CTR mode with a counter starting at 0. aes_encrypt_ctr(&out[payload_len], mac_len, &out[payload_len], key, keysize, counter); free(buf); *out_len = payload_len + mac_len; return(TRUE); } // plaintext_len = ciphertext_len - mac_len // Needs a flag for whether the MAC matches. int aes_decrypt_ccm(const BYTE ciphertext[], WORD ciphertext_len, const BYTE assoc[], unsigned short assoc_len, const BYTE nonce[], unsigned short nonce_len, BYTE plaintext[], WORD *plaintext_len, WORD mac_len, int *mac_auth, const BYTE key_str[], int keysize) { BYTE temp_iv[AES_BLOCK_SIZE], counter[AES_BLOCK_SIZE], mac[16], mac_buf[16], *buf; int end_of_buf, plaintext_len_store_size; WORD key[60]; if (ciphertext_len <= mac_len) return(FALSE); buf = (BYTE*)malloc(assoc_len + ciphertext_len /*ciphertext_len = plaintext_len + mac_len*/ + 48); if (! buf) return(FALSE); // Prepare the key for usage. aes_key_setup(key_str, key, keysize); // Copy the plaintext and MAC to the output buffers. *plaintext_len = ciphertext_len - mac_len; plaintext_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len; memcpy(plaintext, ciphertext, *plaintext_len); memcpy(mac, &ciphertext[*plaintext_len], mac_len); // Prepare the first counter block for use in decryption. ccm_prepare_first_ctr_blk(counter, nonce, nonce_len, plaintext_len_store_size); // Decrypt the Payload with CTR mode with a counter starting at 1. memcpy(temp_iv, counter, AES_BLOCK_SIZE); increment_iv(temp_iv, AES_BLOCK_SIZE - 1 - mac_len); // (AES_BLOCK_SIZE - 1 - mac_len) is the byte size of the counting portion of the counter block. aes_decrypt_ctr(plaintext, *plaintext_len, plaintext, key, keysize, temp_iv); // Setting mac_auth to NULL disables the authentication check. if (mac_auth != NULL) { // Decrypt the MAC with CTR mode with a counter starting at 0. aes_decrypt_ctr(mac, mac_len, mac, key, keysize, counter); // Format the first block of the formatted data. plaintext_len_store_size = AES_BLOCK_SIZE - 1 - nonce_len; ccm_prepare_first_format_blk(buf, assoc_len, *plaintext_len, plaintext_len_store_size, mac_len, nonce, nonce_len); end_of_buf = AES_BLOCK_SIZE; // Format the Associated Data into the authentication buffer. ccm_format_assoc_data(buf, &end_of_buf, assoc, assoc_len); // Format the Payload into the authentication buffer. ccm_format_payload_data(buf, &end_of_buf, plaintext, *plaintext_len); // Perform the CBC operation with an IV of zeros on the formatted buffer to calculate the MAC. memset(temp_iv, 0, AES_BLOCK_SIZE); aes_encrypt_cbc_mac(buf, end_of_buf, mac_buf, key, keysize, temp_iv); // Compare the calculated MAC against the MAC embedded in the ciphertext to see if they are the same. if (! memcmp(mac, mac_buf, mac_len)) { *mac_auth = TRUE; } else { *mac_auth = FALSE; memset(plaintext, 0, *plaintext_len); } } free(buf); return(TRUE); } // Creates the first counter block. First byte is flags, then the nonce, then the incremented part. void ccm_prepare_first_ctr_blk(BYTE counter[], const BYTE nonce[], int nonce_len, int payload_len_store_size) { memset(counter, 0, AES_BLOCK_SIZE); counter[0] = (payload_len_store_size - 1) & 0x07; memcpy(&counter[1], nonce, nonce_len); } void ccm_prepare_first_format_blk(BYTE buf[], int assoc_len, int payload_len, int payload_len_store_size, int mac_len, const BYTE nonce[], int nonce_len) { // Set the flags for the first byte of the first block. buf[0] = ((((mac_len - 2) / 2) & 0x07) << 3) | ((payload_len_store_size - 1) & 0x07); if (assoc_len > 0) buf[0] += 0x40; // Format the rest of the first block, storing the nonce and the size of the payload. memcpy(&buf[1], nonce, nonce_len); memset(&buf[1 + nonce_len], 0, AES_BLOCK_SIZE - 1 - nonce_len); buf[15] = payload_len & 0x000000FF; buf[14] = (payload_len >> 8) & 0x000000FF; } void ccm_format_assoc_data(BYTE buf[], int *end_of_buf, const BYTE assoc[], int assoc_len) { int pad; buf[*end_of_buf + 1] = assoc_len & 0x00FF; buf[*end_of_buf] = (assoc_len >> 8) & 0x00FF; *end_of_buf += 2; memcpy(&buf[*end_of_buf], assoc, assoc_len); *end_of_buf += assoc_len; pad = AES_BLOCK_SIZE - (*end_of_buf % AES_BLOCK_SIZE); /*BUG?*/ memset(&buf[*end_of_buf], 0, pad); *end_of_buf += pad; } void ccm_format_payload_data(BYTE buf[], int *end_of_buf, const BYTE payload[], int payload_len) { int pad; memcpy(&buf[*end_of_buf], payload, payload_len); *end_of_buf += payload_len; pad = *end_of_buf % AES_BLOCK_SIZE; if (pad != 0) pad = AES_BLOCK_SIZE - pad; memset(&buf[*end_of_buf], 0, pad); *end_of_buf += pad; } /******************* * AES *******************/ ///////////////// // KEY EXPANSION ///////////////// // Substitutes a word using the AES S-Box. WORD SubWord(WORD word) { unsigned int result; result = (int)aes_sbox[(word >> 4) & 0x0000000F][word & 0x0000000F]; result += (int)aes_sbox[(word >> 12) & 0x0000000F][(word >> 8) & 0x0000000F] << 8; result += (int)aes_sbox[(word >> 20) & 0x0000000F][(word >> 16) & 0x0000000F] << 16; result += (int)aes_sbox[(word >> 28) & 0x0000000F][(word >> 24) & 0x0000000F] << 24; return(result); } // Performs the action of generating the keys that will be used in every round of // encryption. "key" is the user-supplied input key, "w" is the output key schedule, // "keysize" is the length in bits of "key", must be 128, 192, or 256. void aes_key_setup(const BYTE key[], WORD w[], int keysize) { int Nb=4,Nr,Nk,idx; WORD temp,Rcon[]={0x01000000,0x02000000,0x04000000,0x08000000,0x10000000,0x20000000, 0x40000000,0x80000000,0x1b000000,0x36000000,0x6c000000,0xd8000000, 0xab000000,0x4d000000,0x9a000000}; switch (keysize) { case 128: Nr = 10; Nk = 4; break; case 192: Nr = 12; Nk = 6; break; case 256: Nr = 14; Nk = 8; break; default: return; } for (idx=0; idx < Nk; ++idx) { w[idx] = ((key[4 * idx]) << 24) | ((key[4 * idx + 1]) << 16) | ((key[4 * idx + 2]) << 8) | ((key[4 * idx + 3])); } for (idx = Nk; idx < Nb * (Nr+1); ++idx) { temp = w[idx - 1]; if ((idx % Nk) == 0) temp = SubWord(KE_ROTWORD(temp)) ^ Rcon[(idx-1)/Nk]; else if (Nk > 6 && (idx % Nk) == 4) temp = SubWord(temp); w[idx] = w[idx-Nk] ^ temp; } } ///////////////// // ADD ROUND KEY ///////////////// // Performs the AddRoundKey step. Each round has its own pre-generated 16-byte key in the // form of 4 integers (the "w" array). Each integer is XOR'd by one column of the state. // Also performs the job of InvAddRoundKey(); since the function is a simple XOR process, // it is its own inverse. void AddRoundKey(BYTE state[][4], const WORD w[]) { BYTE subkey[4]; // memcpy(subkey,&w[idx],4); // Not accurate for big endian machines // Subkey 1 subkey[0] = w[0] >> 24; subkey[1] = w[0] >> 16; subkey[2] = w[0] >> 8; subkey[3] = w[0]; state[0][0] ^= subkey[0]; state[1][0] ^= subkey[1]; state[2][0] ^= subkey[2]; state[3][0] ^= subkey[3]; // Subkey 2 subkey[0] = w[1] >> 24; subkey[1] = w[1] >> 16; subkey[2] = w[1] >> 8; subkey[3] = w[1]; state[0][1] ^= subkey[0]; state[1][1] ^= subkey[1]; state[2][1] ^= subkey[2]; state[3][1] ^= subkey[3]; // Subkey 3 subkey[0] = w[2] >> 24; subkey[1] = w[2] >> 16; subkey[2] = w[2] >> 8; subkey[3] = w[2]; state[0][2] ^= subkey[0]; state[1][2] ^= subkey[1]; state[2][2] ^= subkey[2]; state[3][2] ^= subkey[3]; // Subkey 4 subkey[0] = w[3] >> 24; subkey[1] = w[3] >> 16; subkey[2] = w[3] >> 8; subkey[3] = w[3]; state[0][3] ^= subkey[0]; state[1][3] ^= subkey[1]; state[2][3] ^= subkey[2]; state[3][3] ^= subkey[3]; } ///////////////// // (Inv)SubBytes ///////////////// // Performs the SubBytes step. All bytes in the state are substituted with a // pre-calculated value from a lookup table. void SubBytes(BYTE state[][4]) { state[0][0] = aes_sbox[state[0][0] >> 4][state[0][0] & 0x0F]; state[0][1] = aes_sbox[state[0][1] >> 4][state[0][1] & 0x0F]; state[0][2] = aes_sbox[state[0][2] >> 4][state[0][2] & 0x0F]; state[0][3] = aes_sbox[state[0][3] >> 4][state[0][3] & 0x0F]; state[1][0] = aes_sbox[state[1][0] >> 4][state[1][0] & 0x0F]; state[1][1] = aes_sbox[state[1][1] >> 4][state[1][1] & 0x0F]; state[1][2] = aes_sbox[state[1][2] >> 4][state[1][2] & 0x0F]; state[1][3] = aes_sbox[state[1][3] >> 4][state[1][3] & 0x0F]; state[2][0] = aes_sbox[state[2][0] >> 4][state[2][0] & 0x0F]; state[2][1] = aes_sbox[state[2][1] >> 4][state[2][1] & 0x0F]; state[2][2] = aes_sbox[state[2][2] >> 4][state[2][2] & 0x0F]; state[2][3] = aes_sbox[state[2][3] >> 4][state[2][3] & 0x0F]; state[3][0] = aes_sbox[state[3][0] >> 4][state[3][0] & 0x0F]; state[3][1] = aes_sbox[state[3][1] >> 4][state[3][1] & 0x0F]; state[3][2] = aes_sbox[state[3][2] >> 4][state[3][2] & 0x0F]; state[3][3] = aes_sbox[state[3][3] >> 4][state[3][3] & 0x0F]; } void InvSubBytes(BYTE state[][4]) { state[0][0] = aes_invsbox[state[0][0] >> 4][state[0][0] & 0x0F]; state[0][1] = aes_invsbox[state[0][1] >> 4][state[0][1] & 0x0F]; state[0][2] = aes_invsbox[state[0][2] >> 4][state[0][2] & 0x0F]; state[0][3] = aes_invsbox[state[0][3] >> 4][state[0][3] & 0x0F]; state[1][0] = aes_invsbox[state[1][0] >> 4][state[1][0] & 0x0F]; state[1][1] = aes_invsbox[state[1][1] >> 4][state[1][1] & 0x0F]; state[1][2] = aes_invsbox[state[1][2] >> 4][state[1][2] & 0x0F]; state[1][3] = aes_invsbox[state[1][3] >> 4][state[1][3] & 0x0F]; state[2][0] = aes_invsbox[state[2][0] >> 4][state[2][0] & 0x0F]; state[2][1] = aes_invsbox[state[2][1] >> 4][state[2][1] & 0x0F]; state[2][2] = aes_invsbox[state[2][2] >> 4][state[2][2] & 0x0F]; state[2][3] = aes_invsbox[state[2][3] >> 4][state[2][3] & 0x0F]; state[3][0] = aes_invsbox[state[3][0] >> 4][state[3][0] & 0x0F]; state[3][1] = aes_invsbox[state[3][1] >> 4][state[3][1] & 0x0F]; state[3][2] = aes_invsbox[state[3][2] >> 4][state[3][2] & 0x0F]; state[3][3] = aes_invsbox[state[3][3] >> 4][state[3][3] & 0x0F]; } ///////////////// // (Inv)ShiftRows ///////////////// // Performs the ShiftRows step. All rows are shifted cylindrically to the left. void ShiftRows(BYTE state[][4]) { int t; // Shift left by 1 t = state[1][0]; state[1][0] = state[1][1]; state[1][1] = state[1][2]; state[1][2] = state[1][3]; state[1][3] = t; // Shift left by 2 t = state[2][0]; state[2][0] = state[2][2]; state[2][2] = t; t = state[2][1]; state[2][1] = state[2][3]; state[2][3] = t; // Shift left by 3 t = state[3][0]; state[3][0] = state[3][3]; state[3][3] = state[3][2]; state[3][2] = state[3][1]; state[3][1] = t; } // All rows are shifted cylindrically to the right. void InvShiftRows(BYTE state[][4]) { int t; // Shift right by 1 t = state[1][3]; state[1][3] = state[1][2]; state[1][2] = state[1][1]; state[1][1] = state[1][0]; state[1][0] = t; // Shift right by 2 t = state[2][3]; state[2][3] = state[2][1]; state[2][1] = t; t = state[2][2]; state[2][2] = state[2][0]; state[2][0] = t; // Shift right by 3 t = state[3][3]; state[3][3] = state[3][0]; state[3][0] = state[3][1]; state[3][1] = state[3][2]; state[3][2] = t; } ///////////////// // (Inv)MixColumns ///////////////// // Performs the MixColums step. The state is multiplied by itself using matrix // multiplication in a Galios Field 2^8. All multiplication is pre-computed in a table. // Addition is equivilent to XOR. (Must always make a copy of the column as the original // values will be destoyed.) void MixColumns(BYTE state[][4]) { BYTE col[4]; // Column 1 col[0] = state[0][0]; col[1] = state[1][0]; col[2] = state[2][0]; col[3] = state[3][0]; state[0][0] = gf_mul[col[0]][0]; state[0][0] ^= gf_mul[col[1]][1]; state[0][0] ^= col[2]; state[0][0] ^= col[3]; state[1][0] = col[0]; state[1][0] ^= gf_mul[col[1]][0]; state[1][0] ^= gf_mul[col[2]][1]; state[1][0] ^= col[3]; state[2][0] = col[0]; state[2][0] ^= col[1]; state[2][0] ^= gf_mul[col[2]][0]; state[2][0] ^= gf_mul[col[3]][1]; state[3][0] = gf_mul[col[0]][1]; state[3][0] ^= col[1]; state[3][0] ^= col[2]; state[3][0] ^= gf_mul[col[3]][0]; // Column 2 col[0] = state[0][1]; col[1] = state[1][1]; col[2] = state[2][1]; col[3] = state[3][1]; state[0][1] = gf_mul[col[0]][0]; state[0][1] ^= gf_mul[col[1]][1]; state[0][1] ^= col[2]; state[0][1] ^= col[3]; state[1][1] = col[0]; state[1][1] ^= gf_mul[col[1]][0]; state[1][1] ^= gf_mul[col[2]][1]; state[1][1] ^= col[3]; state[2][1] = col[0]; state[2][1] ^= col[1]; state[2][1] ^= gf_mul[col[2]][0]; state[2][1] ^= gf_mul[col[3]][1]; state[3][1] = gf_mul[col[0]][1]; state[3][1] ^= col[1]; state[3][1] ^= col[2]; state[3][1] ^= gf_mul[col[3]][0]; // Column 3 col[0] = state[0][2]; col[1] = state[1][2]; col[2] = state[2][2]; col[3] = state[3][2]; state[0][2] = gf_mul[col[0]][0]; state[0][2] ^= gf_mul[col[1]][1]; state[0][2] ^= col[2]; state[0][2] ^= col[3]; state[1][2] = col[0]; state[1][2] ^= gf_mul[col[1]][0]; state[1][2] ^= gf_mul[col[2]][1]; state[1][2] ^= col[3]; state[2][2] = col[0]; state[2][2] ^= col[1]; state[2][2] ^= gf_mul[col[2]][0]; state[2][2] ^= gf_mul[col[3]][1]; state[3][2] = gf_mul[col[0]][1]; state[3][2] ^= col[1]; state[3][2] ^= col[2]; state[3][2] ^= gf_mul[col[3]][0]; // Column 4 col[0] = state[0][3]; col[1] = state[1][3]; col[2] = state[2][3]; col[3] = state[3][3]; state[0][3] = gf_mul[col[0]][0]; state[0][3] ^= gf_mul[col[1]][1]; state[0][3] ^= col[2]; state[0][3] ^= col[3]; state[1][3] = col[0]; state[1][3] ^= gf_mul[col[1]][0]; state[1][3] ^= gf_mul[col[2]][1]; state[1][3] ^= col[3]; state[2][3] = col[0]; state[2][3] ^= col[1]; state[2][3] ^= gf_mul[col[2]][0]; state[2][3] ^= gf_mul[col[3]][1]; state[3][3] = gf_mul[col[0]][1]; state[3][3] ^= col[1]; state[3][3] ^= col[2]; state[3][3] ^= gf_mul[col[3]][0]; } void InvMixColumns(BYTE state[][4]) { BYTE col[4]; // Column 1 col[0] = state[0][0]; col[1] = state[1][0]; col[2] = state[2][0]; col[3] = state[3][0]; state[0][0] = gf_mul[col[0]][5]; state[0][0] ^= gf_mul[col[1]][3]; state[0][0] ^= gf_mul[col[2]][4]; state[0][0] ^= gf_mul[col[3]][2]; state[1][0] = gf_mul[col[0]][2]; state[1][0] ^= gf_mul[col[1]][5]; state[1][0] ^= gf_mul[col[2]][3]; state[1][0] ^= gf_mul[col[3]][4]; state[2][0] = gf_mul[col[0]][4]; state[2][0] ^= gf_mul[col[1]][2]; state[2][0] ^= gf_mul[col[2]][5]; state[2][0] ^= gf_mul[col[3]][3]; state[3][0] = gf_mul[col[0]][3]; state[3][0] ^= gf_mul[col[1]][4]; state[3][0] ^= gf_mul[col[2]][2]; state[3][0] ^= gf_mul[col[3]][5]; // Column 2 col[0] = state[0][1]; col[1] = state[1][1]; col[2] = state[2][1]; col[3] = state[3][1]; state[0][1] = gf_mul[col[0]][5]; state[0][1] ^= gf_mul[col[1]][3]; state[0][1] ^= gf_mul[col[2]][4]; state[0][1] ^= gf_mul[col[3]][2]; state[1][1] = gf_mul[col[0]][2]; state[1][1] ^= gf_mul[col[1]][5]; state[1][1] ^= gf_mul[col[2]][3]; state[1][1] ^= gf_mul[col[3]][4]; state[2][1] = gf_mul[col[0]][4]; state[2][1] ^= gf_mul[col[1]][2]; state[2][1] ^= gf_mul[col[2]][5]; state[2][1] ^= gf_mul[col[3]][3]; state[3][1] = gf_mul[col[0]][3]; state[3][1] ^= gf_mul[col[1]][4]; state[3][1] ^= gf_mul[col[2]][2]; state[3][1] ^= gf_mul[col[3]][5]; // Column 3 col[0] = state[0][2]; col[1] = state[1][2]; col[2] = state[2][2]; col[3] = state[3][2]; state[0][2] = gf_mul[col[0]][5]; state[0][2] ^= gf_mul[col[1]][3]; state[0][2] ^= gf_mul[col[2]][4]; state[0][2] ^= gf_mul[col[3]][2]; state[1][2] = gf_mul[col[0]][2]; state[1][2] ^= gf_mul[col[1]][5]; state[1][2] ^= gf_mul[col[2]][3]; state[1][2] ^= gf_mul[col[3]][4]; state[2][2] = gf_mul[col[0]][4]; state[2][2] ^= gf_mul[col[1]][2]; state[2][2] ^= gf_mul[col[2]][5]; state[2][2] ^= gf_mul[col[3]][3]; state[3][2] = gf_mul[col[0]][3]; state[3][2] ^= gf_mul[col[1]][4]; state[3][2] ^= gf_mul[col[2]][2]; state[3][2] ^= gf_mul[col[3]][5]; // Column 4 col[0] = state[0][3]; col[1] = state[1][3]; col[2] = state[2][3]; col[3] = state[3][3]; state[0][3] = gf_mul[col[0]][5]; state[0][3] ^= gf_mul[col[1]][3]; state[0][3] ^= gf_mul[col[2]][4]; state[0][3] ^= gf_mul[col[3]][2]; state[1][3] = gf_mul[col[0]][2]; state[1][3] ^= gf_mul[col[1]][5]; state[1][3] ^= gf_mul[col[2]][3]; state[1][3] ^= gf_mul[col[3]][4]; state[2][3] = gf_mul[col[0]][4]; state[2][3] ^= gf_mul[col[1]][2]; state[2][3] ^= gf_mul[col[2]][5]; state[2][3] ^= gf_mul[col[3]][3]; state[3][3] = gf_mul[col[0]][3]; state[3][3] ^= gf_mul[col[1]][4]; state[3][3] ^= gf_mul[col[2]][2]; state[3][3] ^= gf_mul[col[3]][5]; } ///////////////// // (En/De)Crypt ///////////////// void aes_encrypt(const BYTE in[], BYTE out[], const WORD key[], int keysize) { BYTE state[4][4]; // Copy input array (should be 16 bytes long) to a matrix (sequential bytes are ordered // by row, not col) called "state" for processing. // *** Implementation note: The official AES documentation references the state by // column, then row. Accessing an element in C requires row then column. Thus, all state // references in AES must have the column and row indexes reversed for C implementation. state[0][0] = in[0]; state[1][0] = in[1]; state[2][0] = in[2]; state[3][0] = in[3]; state[0][1] = in[4]; state[1][1] = in[5]; state[2][1] = in[6]; state[3][1] = in[7]; state[0][2] = in[8]; state[1][2] = in[9]; state[2][2] = in[10]; state[3][2] = in[11]; state[0][3] = in[12]; state[1][3] = in[13]; state[2][3] = in[14]; state[3][3] = in[15]; // Perform the necessary number of rounds. The round key is added first. // The last round does not perform the MixColumns step. AddRoundKey(state,&key[0]); SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[4]); SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[8]); SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[12]); SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[16]); SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[20]); SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[24]); SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[28]); SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[32]); SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[36]); if (keysize != 128) { SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[40]); SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[44]); if (keysize != 192) { SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[48]); SubBytes(state); ShiftRows(state); MixColumns(state); AddRoundKey(state,&key[52]); SubBytes(state); ShiftRows(state); AddRoundKey(state,&key[56]); } else { SubBytes(state); ShiftRows(state); AddRoundKey(state,&key[48]); } } else { SubBytes(state); ShiftRows(state); AddRoundKey(state,&key[40]); } // Copy the state to the output array. out[0] = state[0][0]; out[1] = state[1][0]; out[2] = state[2][0]; out[3] = state[3][0]; out[4] = state[0][1]; out[5] = state[1][1]; out[6] = state[2][1]; out[7] = state[3][1]; out[8] = state[0][2]; out[9] = state[1][2]; out[10] = state[2][2]; out[11] = state[3][2]; out[12] = state[0][3]; out[13] = state[1][3]; out[14] = state[2][3]; out[15] = state[3][3]; } void aes_decrypt(const BYTE in[], BYTE out[], const WORD key[], int keysize) { BYTE state[4][4]; // Copy the input to the state. state[0][0] = in[0]; state[1][0] = in[1]; state[2][0] = in[2]; state[3][0] = in[3]; state[0][1] = in[4]; state[1][1] = in[5]; state[2][1] = in[6]; state[3][1] = in[7]; state[0][2] = in[8]; state[1][2] = in[9]; state[2][2] = in[10]; state[3][2] = in[11]; state[0][3] = in[12]; state[1][3] = in[13]; state[2][3] = in[14]; state[3][3] = in[15]; // Perform the necessary number of rounds. The round key is added first. // The last round does not perform the MixColumns step. if (keysize > 128) { if (keysize > 192) { AddRoundKey(state,&key[56]); InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[52]);InvMixColumns(state); InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[48]);InvMixColumns(state); } else { AddRoundKey(state,&key[48]); } InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[44]);InvMixColumns(state); InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[40]);InvMixColumns(state); } else { AddRoundKey(state,&key[40]); } InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[36]);InvMixColumns(state); InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[32]);InvMixColumns(state); InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[28]);InvMixColumns(state); InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[24]);InvMixColumns(state); InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[20]);InvMixColumns(state); InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[16]);InvMixColumns(state); InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[12]);InvMixColumns(state); InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[8]);InvMixColumns(state); InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[4]);InvMixColumns(state); InvShiftRows(state);InvSubBytes(state);AddRoundKey(state,&key[0]); // Copy the state to the output array. out[0] = state[0][0]; out[1] = state[1][0]; out[2] = state[2][0]; out[3] = state[3][0]; out[4] = state[0][1]; out[5] = state[1][1]; out[6] = state[2][1]; out[7] = state[3][1]; out[8] = state[0][2]; out[9] = state[1][2]; out[10] = state[2][2]; out[11] = state[3][2]; out[12] = state[0][3]; out[13] = state[1][3]; out[14] = state[2][3]; out[15] = state[3][3]; } /******************* ** AES DEBUGGING FUNCTIONS *******************/ /* // This prints the "state" grid as a linear hex string. void print_state(BYTE state[][4]) { int idx,idx2; for (idx=0; idx < 4; idx++) for (idx2=0; idx2 < 4; idx2++) printf("%02x",state[idx2][idx]); printf("\n"); } // This prints the key (4 consecutive ints) used for a given round as a linear hex string. void print_rnd_key(WORD key[]) { int idx; for (idx=0; idx < 4; idx++) printf("%08x",key[idx]); printf("\n"); } */ olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/aes.h000066400000000000000000000166451311755073500231140ustar00rootroot00000000000000/********************************************************************* * Filename: aes.h * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Defines the API for the corresponding AES implementation. *********************************************************************/ #ifndef AES_H #define AES_H /*************************** HEADER FILES ***************************/ #include /****************************** MACROS ******************************/ #define AES_BLOCK_SIZE 16 // AES operates on 16 bytes at a time /**************************** DATA TYPES ****************************/ typedef unsigned char BYTE; // 8-bit byte typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines /*********************** FUNCTION DECLARATIONS **********************/ /////////////////// // AES /////////////////// // Key setup must be done before any AES en/de-cryption functions can be used. void aes_key_setup(const BYTE key[], // The key, must be 128, 192, or 256 bits WORD w[], // Output key schedule to be used later int keysize); // Bit length of the key, 128, 192, or 256 void aes_encrypt(const BYTE in[], // 16 bytes of plaintext BYTE out[], // 16 bytes of ciphertext const WORD key[], // From the key setup int keysize); // Bit length of the key, 128, 192, or 256 void aes_decrypt(const BYTE in[], // 16 bytes of ciphertext BYTE out[], // 16 bytes of plaintext const WORD key[], // From the key setup int keysize); // Bit length of the key, 128, 192, or 256 /////////////////// // AES - CBC /////////////////// int aes_encrypt_cbc(const BYTE in[], // Plaintext size_t in_len, // Must be a multiple of AES_BLOCK_SIZE BYTE out[], // Ciphertext, same length as plaintext const WORD key[], // From the key setup int keysize, // Bit length of the key, 128, 192, or 256 const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long // Only output the CBC-MAC of the input. int aes_encrypt_cbc_mac(const BYTE in[], // plaintext size_t in_len, // Must be a multiple of AES_BLOCK_SIZE BYTE out[], // Output MAC const WORD key[], // From the key setup int keysize, // Bit length of the key, 128, 192, or 256 const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long /////////////////// // AES - CTR /////////////////// void increment_iv(BYTE iv[], // Must be a multiple of AES_BLOCK_SIZE int counter_size); // Bytes of the IV used for counting (low end) void aes_encrypt_ctr(const BYTE in[], // Plaintext size_t in_len, // Any byte length BYTE out[], // Ciphertext, same length as plaintext const WORD key[], // From the key setup int keysize, // Bit length of the key, 128, 192, or 256 const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long void aes_decrypt_ctr(const BYTE in[], // Ciphertext size_t in_len, // Any byte length BYTE out[], // Plaintext, same length as ciphertext const WORD key[], // From the key setup int keysize, // Bit length of the key, 128, 192, or 256 const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long /////////////////// // AES - CCM /////////////////// // Returns True if the input parameters do not violate any constraint. int aes_encrypt_ccm(const BYTE plaintext[], // IN - Plaintext. WORD plaintext_len, // IN - Plaintext length. const BYTE associated_data[], // IN - Associated Data included in authentication, but not encryption. unsigned short associated_data_len, // IN - Associated Data length in bytes. const BYTE nonce[], // IN - The Nonce to be used for encryption. unsigned short nonce_len, // IN - Nonce length in bytes. BYTE ciphertext[], // OUT - Ciphertext, a concatination of the plaintext and the MAC. WORD *ciphertext_len, // OUT - The length of the ciphertext, always plaintext_len + mac_len. WORD mac_len, // IN - The desired length of the MAC, must be 4, 6, 8, 10, 12, 14, or 16. const BYTE key[], // IN - The AES key for encryption. int keysize); // IN - The length of the key in bits. Valid values are 128, 192, 256. // Returns True if the input parameters do not violate any constraint. // Use mac_auth to ensure decryption/validation was preformed correctly. // If authentication does not succeed, the plaintext is zeroed out. To overwride // this, call with mac_auth = NULL. The proper proceedure is to decrypt with // authentication enabled (mac_auth != NULL) and make a second call to that // ignores authentication explicitly if the first call failes. int aes_decrypt_ccm(const BYTE ciphertext[], // IN - Ciphertext, the concatination of encrypted plaintext and MAC. WORD ciphertext_len, // IN - Ciphertext length in bytes. const BYTE assoc[], // IN - The Associated Data, required for authentication. unsigned short assoc_len, // IN - Associated Data length in bytes. const BYTE nonce[], // IN - The Nonce to use for decryption, same one as for encryption. unsigned short nonce_len, // IN - Nonce length in bytes. BYTE plaintext[], // OUT - The plaintext that was decrypted. Will need to be large enough to hold ciphertext_len - mac_len. WORD *plaintext_len, // OUT - Length in bytes of the output plaintext, always ciphertext_len - mac_len . WORD mac_len, // IN - The length of the MAC that was calculated. int *mac_auth, // OUT - TRUE if authentication succeeded, FALSE if it did not. NULL pointer will ignore the authentication. const BYTE key[], // IN - The AES key for decryption. int keysize); // IN - The length of the key in BITS. Valid values are 128, 192, 256. /////////////////// // Test functions /////////////////// int aes_test(); int aes_ecb_test(); int aes_cbc_test(); int aes_ctr_test(); int aes_ccm_test(); #endif // AES_H olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/aes_test.c000066400000000000000000000226741311755073500241450ustar00rootroot00000000000000/********************************************************************* * Filename: aes_test.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Performs known-answer tests on the corresponding AES implementation. These tests do not encompass the full range of available test vectors and are not sufficient for FIPS-140 certification. However, if the tests pass it is very, very likely that the code is correct and was compiled properly. This code also serves as example usage of the functions. *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include #include "aes.h" /*********************** FUNCTION DEFINITIONS ***********************/ void print_hex(BYTE str[], int len) { int idx; for(idx = 0; idx < len; idx++) printf("%02x", str[idx]); } int aes_ecb_test() { WORD key_schedule[60], idx; BYTE enc_buf[128]; BYTE plaintext[2][16] = { {0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a}, {0xae,0x2d,0x8a,0x57,0x1e,0x03,0xac,0x9c,0x9e,0xb7,0x6f,0xac,0x45,0xaf,0x8e,0x51} }; BYTE ciphertext[2][16] = { {0xf3,0xee,0xd1,0xbd,0xb5,0xd2,0xa0,0x3c,0x06,0x4b,0x5a,0x7e,0x3d,0xb1,0x81,0xf8}, {0x59,0x1c,0xcb,0x10,0xd4,0x10,0xed,0x26,0xdc,0x5b,0xa7,0x4a,0x31,0x36,0x28,0x70} }; BYTE key[1][32] = { {0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe,0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81,0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7,0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4} }; int pass = 1; // Raw ECB mode. //printf("* ECB mode:\n"); aes_key_setup(key[0], key_schedule, 256); //printf( "Key : "); //print_hex(key[0], 32); for(idx = 0; idx < 2; idx++) { aes_encrypt(plaintext[idx], enc_buf, key_schedule, 256); //printf("\nPlaintext : "); //print_hex(plaintext[idx], 16); //printf("\n-encrypted to: "); //print_hex(enc_buf, 16); pass = pass && !memcmp(enc_buf, ciphertext[idx], 16); aes_decrypt(ciphertext[idx], enc_buf, key_schedule, 256); //printf("\nCiphertext : "); //print_hex(ciphertext[idx], 16); //printf("\n-decrypted to: "); //print_hex(enc_buf, 16); pass = pass && !memcmp(enc_buf, plaintext[idx], 16); //printf("\n\n"); } return(pass); } int aes_cbc_test() { WORD key_schedule[60]; BYTE enc_buf[128]; BYTE plaintext[1][32] = { {0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a,0xae,0x2d,0x8a,0x57,0x1e,0x03,0xac,0x9c,0x9e,0xb7,0x6f,0xac,0x45,0xaf,0x8e,0x51} }; BYTE ciphertext[2][32] = { {0xf5,0x8c,0x4c,0x04,0xd6,0xe5,0xf1,0xba,0x77,0x9e,0xab,0xfb,0x5f,0x7b,0xfb,0xd6,0x9c,0xfc,0x4e,0x96,0x7e,0xdb,0x80,0x8d,0x67,0x9f,0x77,0x7b,0xc6,0x70,0x2c,0x7d} }; BYTE iv[1][16] = { {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f} }; BYTE key[1][32] = { {0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe,0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81,0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7,0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4} }; int pass = 1; //printf("* CBC mode:\n"); aes_key_setup(key[0], key_schedule, 256); //printf( "Key : "); //print_hex(key[0], 32); //printf("\nIV : "); //print_hex(iv[0], 16); aes_encrypt_cbc(plaintext[0], 32, enc_buf, key_schedule, 256, iv[0]); //printf("\nPlaintext : "); //print_hex(plaintext[0], 32); //printf("\n-encrypted to: "); //print_hex(enc_buf, 32); //printf("\nCiphertext : "); //print_hex(ciphertext[0], 32); pass = pass && !memcmp(enc_buf, ciphertext[0], 32); //printf("\n\n"); return(pass); } int aes_ctr_test() { WORD key_schedule[60]; BYTE enc_buf[128]; BYTE plaintext[1][32] = { {0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a,0xae,0x2d,0x8a,0x57,0x1e,0x03,0xac,0x9c,0x9e,0xb7,0x6f,0xac,0x45,0xaf,0x8e,0x51} }; BYTE ciphertext[1][32] = { {0x60,0x1e,0xc3,0x13,0x77,0x57,0x89,0xa5,0xb7,0xa7,0xf5,0x04,0xbb,0xf3,0xd2,0x28,0xf4,0x43,0xe3,0xca,0x4d,0x62,0xb5,0x9a,0xca,0x84,0xe9,0x90,0xca,0xca,0xf5,0xc5} }; BYTE iv[1][16] = { {0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff}, }; BYTE key[1][32] = { {0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe,0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81,0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7,0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4} }; int pass = 1; //printf("* CTR mode:\n"); aes_key_setup(key[0], key_schedule, 256); //printf( "Key : "); //print_hex(key[0], 32); //printf("\nIV : "); //print_hex(iv[0], 16); aes_encrypt_ctr(plaintext[0], 32, enc_buf, key_schedule, 256, iv[0]); //printf("\nPlaintext : "); //print_hex(plaintext[0], 32); //printf("\n-encrypted to: "); //print_hex(enc_buf, 32); pass = pass && !memcmp(enc_buf, ciphertext[0], 32); aes_decrypt_ctr(ciphertext[0], 32, enc_buf, key_schedule, 256, iv[0]); //printf("\nCiphertext : "); //print_hex(ciphertext[0], 32); //printf("\n-decrypted to: "); //print_hex(enc_buf, 32); pass = pass && !memcmp(enc_buf, plaintext[0], 32); //printf("\n\n"); return(pass); } int aes_ccm_test() { int mac_auth; WORD enc_buf_len; BYTE enc_buf[128]; BYTE plaintext[3][32] = { {0x20,0x21,0x22,0x23}, {0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f}, {0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37} }; BYTE assoc[3][32] = { {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07}, {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}, {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13} }; BYTE ciphertext[3][32 + 16] = { {0x71,0x62,0x01,0x5b,0x4d,0xac,0x25,0x5d}, {0xd2,0xa1,0xf0,0xe0,0x51,0xea,0x5f,0x62,0x08,0x1a,0x77,0x92,0x07,0x3d,0x59,0x3d,0x1f,0xc6,0x4f,0xbf,0xac,0xcd}, {0xe3,0xb2,0x01,0xa9,0xf5,0xb7,0x1a,0x7a,0x9b,0x1c,0xea,0xec,0xcd,0x97,0xe7,0x0b,0x61,0x76,0xaa,0xd9,0xa4,0x42,0x8a,0xa5,0x48,0x43,0x92,0xfb,0xc1,0xb0,0x99,0x51} }; BYTE iv[3][16] = { {0x10,0x11,0x12,0x13,0x14,0x15,0x16}, {0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17}, {0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b} }; BYTE key[1][32] = { {0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f} }; int pass = 1; //printf("* CCM mode:\n"); //printf("Key : "); //print_hex(key[0], 16); //print_hex(plaintext[0], 4); //print_hex(assoc[0], 8); //print_hex(ciphertext[0], 8); //print_hex(iv[0], 7); //print_hex(key[0], 16); aes_encrypt_ccm(plaintext[0], 4, assoc[0], 8, iv[0], 7, enc_buf, &enc_buf_len, 4, key[0], 128); //printf("\nNONCE : "); //print_hex(iv[0], 7); //printf("\nAssoc. Data : "); //print_hex(assoc[0], 8); //printf("\nPayload : "); //print_hex(plaintext[0], 4); //printf("\n-encrypted to: "); //print_hex(enc_buf, enc_buf_len); pass = pass && !memcmp(enc_buf, ciphertext[0], enc_buf_len); aes_decrypt_ccm(ciphertext[0], 8, assoc[0], 8, iv[0], 7, enc_buf, &enc_buf_len, 4, &mac_auth, key[0], 128); //printf("\n-Ciphertext : "); //print_hex(ciphertext[0], 8); //printf("\n-decrypted to: "); //print_hex(enc_buf, enc_buf_len); //printf("\nAuthenticated: %d ", mac_auth); pass = pass && !memcmp(enc_buf, plaintext[0], enc_buf_len) && mac_auth; aes_encrypt_ccm(plaintext[1], 16, assoc[1], 16, iv[1], 8, enc_buf, &enc_buf_len, 6, key[0], 128); //printf("\n\nNONCE : "); //print_hex(iv[1], 8); //printf("\nAssoc. Data : "); //print_hex(assoc[1], 16); //printf("\nPayload : "); //print_hex(plaintext[1], 16); //printf("\n-encrypted to: "); //print_hex(enc_buf, enc_buf_len); pass = pass && !memcmp(enc_buf, ciphertext[1], enc_buf_len); aes_decrypt_ccm(ciphertext[1], 22, assoc[1], 16, iv[1], 8, enc_buf, &enc_buf_len, 6, &mac_auth, key[0], 128); //printf("\n-Ciphertext : "); //print_hex(ciphertext[1], 22); //printf("\n-decrypted to: "); //print_hex(enc_buf, enc_buf_len); //printf("\nAuthenticated: %d ", mac_auth); pass = pass && !memcmp(enc_buf, plaintext[1], enc_buf_len) && mac_auth; aes_encrypt_ccm(plaintext[2], 24, assoc[2], 20, iv[2], 12, enc_buf, &enc_buf_len, 8, key[0], 128); //printf("\n\nNONCE : "); //print_hex(iv[2], 12); //printf("\nAssoc. Data : "); //print_hex(assoc[2], 20); //printf("\nPayload : "); //print_hex(plaintext[2], 24); //printf("\n-encrypted to: "); //print_hex(enc_buf, enc_buf_len); pass = pass && !memcmp(enc_buf, ciphertext[2], enc_buf_len); aes_decrypt_ccm(ciphertext[2], 32, assoc[2], 20, iv[2], 12, enc_buf, &enc_buf_len, 8, &mac_auth, key[0], 128); //printf("\n-Ciphertext : "); //print_hex(ciphertext[2], 32); //printf("\n-decrypted to: "); //print_hex(enc_buf, enc_buf_len); //printf("\nAuthenticated: %d ", mac_auth); pass = pass && !memcmp(enc_buf, plaintext[2], enc_buf_len) && mac_auth; //printf("\n\n"); return(pass); } int aes_test() { int pass = 1; pass = pass && aes_ecb_test(); pass = pass && aes_cbc_test(); pass = pass && aes_ctr_test(); pass = pass && aes_ccm_test(); return(pass); } int main(int argc, char *argv[]) { printf("AES Tests: %s\n", aes_test() ? "SUCCEEDED" : "FAILED"); return(0); } olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/arcfour.c000066400000000000000000000023241311755073500237650ustar00rootroot00000000000000/********************************************************************* * Filename: arcfour.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Implementation of the ARCFOUR encryption algorithm. Algorithm specification can be found here: * http://en.wikipedia.org/wiki/RC4 *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include "arcfour.h" /*********************** FUNCTION DEFINITIONS ***********************/ void arcfour_key_setup(BYTE state[], const BYTE key[], int len) { int i, j; BYTE t; for (i = 0; i < 256; ++i) state[i] = i; for (i = 0, j = 0; i < 256; ++i) { j = (j + state[i] + key[i % len]) % 256; t = state[i]; state[i] = state[j]; state[j] = t; } } void arcfour_generate_stream(BYTE state[], BYTE out[], size_t len) { int i, j; size_t idx; BYTE t; for (idx = 0, i = 0, j = 0; idx < len; ++idx) { i = (i + 1) % 256; j = (j + state[i]) % 256; t = state[i]; state[i] = state[j]; state[j] = t; out[idx] = state[(state[i] + state[j]) % 256]; } } olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/arcfour.h000066400000000000000000000023251311755073500237730ustar00rootroot00000000000000/********************************************************************* * Filename: arcfour.h * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Defines the API for the corresponding ARCFOUR implementation. *********************************************************************/ #ifndef ARCFOUR_H #define ARCFOUR_H /*************************** HEADER FILES ***************************/ #include /**************************** DATA TYPES ****************************/ typedef unsigned char BYTE; // 8-bit byte /*********************** FUNCTION DECLARATIONS **********************/ // Input: state - the state used to generate the keystream // key - Key to use to initialize the state // len - length of key in bytes (valid lenth is 1 to 256) void arcfour_key_setup(BYTE state[], const BYTE key[], int len); // Pseudo-Random Generator Algorithm // Input: state - the state used to generate the keystream // out - Must be allocated to be of at least "len" length // len - number of bytes to generate void arcfour_generate_stream(BYTE state[], BYTE out[], size_t len); #endif // ARCFOUR_H olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/arcfour_test.c000066400000000000000000000031401311755073500250210ustar00rootroot00000000000000/********************************************************************* * Filename: arcfour_test.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Performs known-answer tests on the corresponding ARCFOUR implementation. These tests do not encompass the full range of available test vectors, however, if the tests pass it is very, very likely that the code is correct and was compiled properly. This code also serves as example usage of the functions. *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include #include "arcfour.h" /*********************** FUNCTION DEFINITIONS ***********************/ int rc4_test() { BYTE state[256]; BYTE key[3][10] = {{"Key"}, {"Wiki"}, {"Secret"}}; BYTE stream[3][10] = {{0xEB,0x9F,0x77,0x81,0xB7,0x34,0xCA,0x72,0xA7,0x19}, {0x60,0x44,0xdb,0x6d,0x41,0xb7}, {0x04,0xd4,0x6b,0x05,0x3c,0xa8,0x7b,0x59}}; int stream_len[3] = {10,6,8}; BYTE buf[1024]; int idx; int pass = 1; // Only test the output stream. Note that the state can be reused. for (idx = 0; idx < 3; idx++) { arcfour_key_setup(state, key[idx], strlen(key[idx])); arcfour_generate_stream(state, buf, stream_len[idx]); pass = pass && !memcmp(stream[idx], buf, stream_len[idx]); } return(pass); } int main() { printf("ARCFOUR tests: %s\n", rc4_test() ? "SUCCEEDED" : "FAILED"); return(0); } olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/base64.c000066400000000000000000000075401311755073500234150ustar00rootroot00000000000000/********************************************************************* * Filename: base64.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Implementation of the Base64 encoding algorithm. *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include "base64.h" /****************************** MACROS ******************************/ #define NEWLINE_INVL 76 /**************************** VARIABLES *****************************/ // Note: To change the charset to a URL encoding, replace the '+' and '/' with '*' and '-' static const BYTE charset[]={"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"}; /*********************** FUNCTION DEFINITIONS ***********************/ BYTE revchar(char ch) { if (ch >= 'A' && ch <= 'Z') ch -= 'A'; else if (ch >= 'a' && ch <='z') ch = ch - 'a' + 26; else if (ch >= '0' && ch <='9') ch = ch - '0' + 52; else if (ch == '+') ch = 62; else if (ch == '/') ch = 63; return(ch); } size_t base64_encode(const BYTE in[], BYTE out[], size_t len, int newline_flag) { size_t idx, idx2, blks, blk_ceiling, left_over, newline_count = 0; blks = (len / 3); left_over = len % 3; if (out == NULL) { idx2 = blks * 4 ; if (left_over) idx2 += 4; if (newline_flag) idx2 += len / 57; // (NEWLINE_INVL / 4) * 3 = 57. One newline per 57 input bytes. } else { // Since 3 input bytes = 4 output bytes, determine out how many even sets of // 3 bytes the input has. blk_ceiling = blks * 3; for (idx = 0, idx2 = 0; idx < blk_ceiling; idx += 3, idx2 += 4) { out[idx2] = charset[in[idx] >> 2]; out[idx2 + 1] = charset[((in[idx] & 0x03) << 4) | (in[idx + 1] >> 4)]; out[idx2 + 2] = charset[((in[idx + 1] & 0x0f) << 2) | (in[idx + 2] >> 6)]; out[idx2 + 3] = charset[in[idx + 2] & 0x3F]; // The offical standard requires a newline every 76 characters. // (Eg, first newline is character 77 of the output.) if (((idx2 - newline_count + 4) % NEWLINE_INVL == 0) && newline_flag) { out[idx2 + 4] = '\n'; idx2++; newline_count++; } } if (left_over == 1) { out[idx2] = charset[in[idx] >> 2]; out[idx2 + 1] = charset[(in[idx] & 0x03) << 4]; out[idx2 + 2] = '='; out[idx2 + 3] = '='; idx2 += 4; } else if (left_over == 2) { out[idx2] = charset[in[idx] >> 2]; out[idx2 + 1] = charset[((in[idx] & 0x03) << 4) | (in[idx + 1] >> 4)]; out[idx2 + 2] = charset[(in[idx + 1] & 0x0F) << 2]; out[idx2 + 3] = '='; idx2 += 4; } } return(idx2); } size_t base64_decode(const BYTE in[], BYTE out[], size_t len) { BYTE ch; size_t idx, idx2, blks, blk_ceiling, left_over; if (in[len - 1] == '=') len--; if (in[len - 1] == '=') len--; blks = len / 4; left_over = len % 4; if (out == NULL) { if (len >= 77 && in[NEWLINE_INVL] == '\n') // Verify that newlines where used. len -= len / (NEWLINE_INVL + 1); blks = len / 4; left_over = len % 4; idx = blks * 3; if (left_over == 2) idx ++; else if (left_over == 3) idx += 2; } else { blk_ceiling = blks * 4; for (idx = 0, idx2 = 0; idx2 < blk_ceiling; idx += 3, idx2 += 4) { if (in[idx2] == '\n') idx2++; out[idx] = (revchar(in[idx2]) << 2) | ((revchar(in[idx2 + 1]) & 0x30) >> 4); out[idx + 1] = (revchar(in[idx2 + 1]) << 4) | (revchar(in[idx2 + 2]) >> 2); out[idx + 2] = (revchar(in[idx2 + 2]) << 6) | revchar(in[idx2 + 3]); } if (left_over == 2) { out[idx] = (revchar(in[idx2]) << 2) | ((revchar(in[idx2 + 1]) & 0x30) >> 4); idx++; } else if (left_over == 3) { out[idx] = (revchar(in[idx2]) << 2) | ((revchar(in[idx2 + 1]) & 0x30) >> 4); out[idx + 1] = (revchar(in[idx2 + 1]) << 4) | (revchar(in[idx2 + 2]) >> 2); idx += 2; } } return(idx); } olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/base64.h000066400000000000000000000022271311755073500234170ustar00rootroot00000000000000/********************************************************************* * Filename: base64.h * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Defines the API for the corresponding Base64 implementation. *********************************************************************/ #ifndef BASE64_H #define BASE64_H /*************************** HEADER FILES ***************************/ #include /**************************** DATA TYPES ****************************/ typedef unsigned char BYTE; // 8-bit byte /*********************** FUNCTION DECLARATIONS **********************/ // Returns the size of the output. If called with out = NULL, will just return // the size of what the output would have been (without a terminating NULL). size_t base64_encode(const BYTE in[], BYTE out[], size_t len, int newline_flag); // Returns the size of the output. If called with out = NULL, will just return // the size of what the output would have been (without a terminating NULL). size_t base64_decode(const BYTE in[], BYTE out[], size_t len); #endif // BASE64_H olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/base64_test.c000066400000000000000000000046321311755073500244530ustar00rootroot00000000000000/********************************************************************* * Filename: blowfish_test.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Performs known-answer tests on the corresponding Base64 implementation. These tests do not encompass the full range of available test vectors, however, if the tests pass it is very, very likely that the code is correct and was compiled properly. This code also serves as example usage of the functions. *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include #include "base64.h" /*********************** FUNCTION DEFINITIONS ***********************/ int base64_test() { BYTE text[3][1024] = {{"fo"}, {"foobar"}, {"Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure."}}; BYTE code[3][1024] = {{"Zm8="}, {"Zm9vYmFy"}, {"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz\nIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg\ndGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu\ndWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo\nZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4="}}; BYTE buf[1024]; size_t buf_len; int pass = 1; int idx; for (idx = 0; idx < 3; idx++) { buf_len = base64_encode(text[idx], buf, strlen(text[idx]), 1); pass = pass && ((buf_len == strlen(code[idx])) && (buf_len == base64_encode(text[idx], NULL, strlen(text[idx]), 1))); pass = pass && !strcmp(code[idx], buf); memset(buf, 0, sizeof(buf)); buf_len = base64_decode(code[idx], buf, strlen(code[idx])); pass = pass && ((buf_len == strlen(text[idx])) && (buf_len == base64_decode(code[idx], NULL, strlen(code[idx])))); pass = pass && !strcmp(text[idx], buf); } return(pass); } int main() { printf("Base64 tests: %s\n", base64_test() ? "PASSED" : "FAILED"); return 0; } olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/blowfish.c000066400000000000000000000401031311755073500241360ustar00rootroot00000000000000/********************************************************************* * Filename: blowfish.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Implementation of the Blowfish encryption algorithm. Modes of operation (such as CBC) are not included. Algorithm specification can be found here: * http://www.schneier.com/blowfish.html *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include #include "blowfish.h" /****************************** MACROS ******************************/ #define F(x,t) t = keystruct->s[0][(x) >> 24]; \ t += keystruct->s[1][((x) >> 16) & 0xff]; \ t ^= keystruct->s[2][((x) >> 8) & 0xff]; \ t += keystruct->s[3][(x) & 0xff]; #define swap(r,l,t) t = l; l = r; r = t; #define ITERATION(l,r,t,pval) l ^= keystruct->p[pval]; F(l,t); r^= t; swap(r,l,t); /**************************** VARIABLES *****************************/ static const WORD p_perm[18] = { 0x243F6A88,0x85A308D3,0x13198A2E,0x03707344,0xA4093822,0x299F31D0,0x082EFA98, 0xEC4E6C89,0x452821E6,0x38D01377,0xBE5466CF,0x34E90C6C,0xC0AC29B7,0xC97C50DD, 0x3F84D5B5,0xB5470917,0x9216D5D9,0x8979FB1B }; static const WORD s_perm[4][256] = { { 0xD1310BA6,0x98DFB5AC,0x2FFD72DB,0xD01ADFB7,0xB8E1AFED,0x6A267E96,0xBA7C9045,0xF12C7F99, 0x24A19947,0xB3916CF7,0x0801F2E2,0x858EFC16,0x636920D8,0x71574E69,0xA458FEA3,0xF4933D7E, 0x0D95748F,0x728EB658,0x718BCD58,0x82154AEE,0x7B54A41D,0xC25A59B5,0x9C30D539,0x2AF26013, 0xC5D1B023,0x286085F0,0xCA417918,0xB8DB38EF,0x8E79DCB0,0x603A180E,0x6C9E0E8B,0xB01E8A3E, 0xD71577C1,0xBD314B27,0x78AF2FDA,0x55605C60,0xE65525F3,0xAA55AB94,0x57489862,0x63E81440, 0x55CA396A,0x2AAB10B6,0xB4CC5C34,0x1141E8CE,0xA15486AF,0x7C72E993,0xB3EE1411,0x636FBC2A, 0x2BA9C55D,0x741831F6,0xCE5C3E16,0x9B87931E,0xAFD6BA33,0x6C24CF5C,0x7A325381,0x28958677, 0x3B8F4898,0x6B4BB9AF,0xC4BFE81B,0x66282193,0x61D809CC,0xFB21A991,0x487CAC60,0x5DEC8032, 0xEF845D5D,0xE98575B1,0xDC262302,0xEB651B88,0x23893E81,0xD396ACC5,0x0F6D6FF3,0x83F44239, 0x2E0B4482,0xA4842004,0x69C8F04A,0x9E1F9B5E,0x21C66842,0xF6E96C9A,0x670C9C61,0xABD388F0, 0x6A51A0D2,0xD8542F68,0x960FA728,0xAB5133A3,0x6EEF0B6C,0x137A3BE4,0xBA3BF050,0x7EFB2A98, 0xA1F1651D,0x39AF0176,0x66CA593E,0x82430E88,0x8CEE8619,0x456F9FB4,0x7D84A5C3,0x3B8B5EBE, 0xE06F75D8,0x85C12073,0x401A449F,0x56C16AA6,0x4ED3AA62,0x363F7706,0x1BFEDF72,0x429B023D, 0x37D0D724,0xD00A1248,0xDB0FEAD3,0x49F1C09B,0x075372C9,0x80991B7B,0x25D479D8,0xF6E8DEF7, 0xE3FE501A,0xB6794C3B,0x976CE0BD,0x04C006BA,0xC1A94FB6,0x409F60C4,0x5E5C9EC2,0x196A2463, 0x68FB6FAF,0x3E6C53B5,0x1339B2EB,0x3B52EC6F,0x6DFC511F,0x9B30952C,0xCC814544,0xAF5EBD09, 0xBEE3D004,0xDE334AFD,0x660F2807,0x192E4BB3,0xC0CBA857,0x45C8740F,0xD20B5F39,0xB9D3FBDB, 0x5579C0BD,0x1A60320A,0xD6A100C6,0x402C7279,0x679F25FE,0xFB1FA3CC,0x8EA5E9F8,0xDB3222F8, 0x3C7516DF,0xFD616B15,0x2F501EC8,0xAD0552AB,0x323DB5FA,0xFD238760,0x53317B48,0x3E00DF82, 0x9E5C57BB,0xCA6F8CA0,0x1A87562E,0xDF1769DB,0xD542A8F6,0x287EFFC3,0xAC6732C6,0x8C4F5573, 0x695B27B0,0xBBCA58C8,0xE1FFA35D,0xB8F011A0,0x10FA3D98,0xFD2183B8,0x4AFCB56C,0x2DD1D35B, 0x9A53E479,0xB6F84565,0xD28E49BC,0x4BFB9790,0xE1DDF2DA,0xA4CB7E33,0x62FB1341,0xCEE4C6E8, 0xEF20CADA,0x36774C01,0xD07E9EFE,0x2BF11FB4,0x95DBDA4D,0xAE909198,0xEAAD8E71,0x6B93D5A0, 0xD08ED1D0,0xAFC725E0,0x8E3C5B2F,0x8E7594B7,0x8FF6E2FB,0xF2122B64,0x8888B812,0x900DF01C, 0x4FAD5EA0,0x688FC31C,0xD1CFF191,0xB3A8C1AD,0x2F2F2218,0xBE0E1777,0xEA752DFE,0x8B021FA1, 0xE5A0CC0F,0xB56F74E8,0x18ACF3D6,0xCE89E299,0xB4A84FE0,0xFD13E0B7,0x7CC43B81,0xD2ADA8D9, 0x165FA266,0x80957705,0x93CC7314,0x211A1477,0xE6AD2065,0x77B5FA86,0xC75442F5,0xFB9D35CF, 0xEBCDAF0C,0x7B3E89A0,0xD6411BD3,0xAE1E7E49,0x00250E2D,0x2071B35E,0x226800BB,0x57B8E0AF, 0x2464369B,0xF009B91E,0x5563911D,0x59DFA6AA,0x78C14389,0xD95A537F,0x207D5BA2,0x02E5B9C5, 0x83260376,0x6295CFA9,0x11C81968,0x4E734A41,0xB3472DCA,0x7B14A94A,0x1B510052,0x9A532915, 0xD60F573F,0xBC9BC6E4,0x2B60A476,0x81E67400,0x08BA6FB5,0x571BE91F,0xF296EC6B,0x2A0DD915, 0xB6636521,0xE7B9F9B6,0xFF34052E,0xC5855664,0x53B02D5D,0xA99F8FA1,0x08BA4799,0x6E85076A },{ 0x4B7A70E9,0xB5B32944,0xDB75092E,0xC4192623,0xAD6EA6B0,0x49A7DF7D,0x9CEE60B8,0x8FEDB266, 0xECAA8C71,0x699A17FF,0x5664526C,0xC2B19EE1,0x193602A5,0x75094C29,0xA0591340,0xE4183A3E, 0x3F54989A,0x5B429D65,0x6B8FE4D6,0x99F73FD6,0xA1D29C07,0xEFE830F5,0x4D2D38E6,0xF0255DC1, 0x4CDD2086,0x8470EB26,0x6382E9C6,0x021ECC5E,0x09686B3F,0x3EBAEFC9,0x3C971814,0x6B6A70A1, 0x687F3584,0x52A0E286,0xB79C5305,0xAA500737,0x3E07841C,0x7FDEAE5C,0x8E7D44EC,0x5716F2B8, 0xB03ADA37,0xF0500C0D,0xF01C1F04,0x0200B3FF,0xAE0CF51A,0x3CB574B2,0x25837A58,0xDC0921BD, 0xD19113F9,0x7CA92FF6,0x94324773,0x22F54701,0x3AE5E581,0x37C2DADC,0xC8B57634,0x9AF3DDA7, 0xA9446146,0x0FD0030E,0xECC8C73E,0xA4751E41,0xE238CD99,0x3BEA0E2F,0x3280BBA1,0x183EB331, 0x4E548B38,0x4F6DB908,0x6F420D03,0xF60A04BF,0x2CB81290,0x24977C79,0x5679B072,0xBCAF89AF, 0xDE9A771F,0xD9930810,0xB38BAE12,0xDCCF3F2E,0x5512721F,0x2E6B7124,0x501ADDE6,0x9F84CD87, 0x7A584718,0x7408DA17,0xBC9F9ABC,0xE94B7D8C,0xEC7AEC3A,0xDB851DFA,0x63094366,0xC464C3D2, 0xEF1C1847,0x3215D908,0xDD433B37,0x24C2BA16,0x12A14D43,0x2A65C451,0x50940002,0x133AE4DD, 0x71DFF89E,0x10314E55,0x81AC77D6,0x5F11199B,0x043556F1,0xD7A3C76B,0x3C11183B,0x5924A509, 0xF28FE6ED,0x97F1FBFA,0x9EBABF2C,0x1E153C6E,0x86E34570,0xEAE96FB1,0x860E5E0A,0x5A3E2AB3, 0x771FE71C,0x4E3D06FA,0x2965DCB9,0x99E71D0F,0x803E89D6,0x5266C825,0x2E4CC978,0x9C10B36A, 0xC6150EBA,0x94E2EA78,0xA5FC3C53,0x1E0A2DF4,0xF2F74EA7,0x361D2B3D,0x1939260F,0x19C27960, 0x5223A708,0xF71312B6,0xEBADFE6E,0xEAC31F66,0xE3BC4595,0xA67BC883,0xB17F37D1,0x018CFF28, 0xC332DDEF,0xBE6C5AA5,0x65582185,0x68AB9802,0xEECEA50F,0xDB2F953B,0x2AEF7DAD,0x5B6E2F84, 0x1521B628,0x29076170,0xECDD4775,0x619F1510,0x13CCA830,0xEB61BD96,0x0334FE1E,0xAA0363CF, 0xB5735C90,0x4C70A239,0xD59E9E0B,0xCBAADE14,0xEECC86BC,0x60622CA7,0x9CAB5CAB,0xB2F3846E, 0x648B1EAF,0x19BDF0CA,0xA02369B9,0x655ABB50,0x40685A32,0x3C2AB4B3,0x319EE9D5,0xC021B8F7, 0x9B540B19,0x875FA099,0x95F7997E,0x623D7DA8,0xF837889A,0x97E32D77,0x11ED935F,0x16681281, 0x0E358829,0xC7E61FD6,0x96DEDFA1,0x7858BA99,0x57F584A5,0x1B227263,0x9B83C3FF,0x1AC24696, 0xCDB30AEB,0x532E3054,0x8FD948E4,0x6DBC3128,0x58EBF2EF,0x34C6FFEA,0xFE28ED61,0xEE7C3C73, 0x5D4A14D9,0xE864B7E3,0x42105D14,0x203E13E0,0x45EEE2B6,0xA3AAABEA,0xDB6C4F15,0xFACB4FD0, 0xC742F442,0xEF6ABBB5,0x654F3B1D,0x41CD2105,0xD81E799E,0x86854DC7,0xE44B476A,0x3D816250, 0xCF62A1F2,0x5B8D2646,0xFC8883A0,0xC1C7B6A3,0x7F1524C3,0x69CB7492,0x47848A0B,0x5692B285, 0x095BBF00,0xAD19489D,0x1462B174,0x23820E00,0x58428D2A,0x0C55F5EA,0x1DADF43E,0x233F7061, 0x3372F092,0x8D937E41,0xD65FECF1,0x6C223BDB,0x7CDE3759,0xCBEE7460,0x4085F2A7,0xCE77326E, 0xA6078084,0x19F8509E,0xE8EFD855,0x61D99735,0xA969A7AA,0xC50C06C2,0x5A04ABFC,0x800BCADC, 0x9E447A2E,0xC3453484,0xFDD56705,0x0E1E9EC9,0xDB73DBD3,0x105588CD,0x675FDA79,0xE3674340, 0xC5C43465,0x713E38D8,0x3D28F89E,0xF16DFF20,0x153E21E7,0x8FB03D4A,0xE6E39F2B,0xDB83ADF7 },{ 0xE93D5A68,0x948140F7,0xF64C261C,0x94692934,0x411520F7,0x7602D4F7,0xBCF46B2E,0xD4A20068, 0xD4082471,0x3320F46A,0x43B7D4B7,0x500061AF,0x1E39F62E,0x97244546,0x14214F74,0xBF8B8840, 0x4D95FC1D,0x96B591AF,0x70F4DDD3,0x66A02F45,0xBFBC09EC,0x03BD9785,0x7FAC6DD0,0x31CB8504, 0x96EB27B3,0x55FD3941,0xDA2547E6,0xABCA0A9A,0x28507825,0x530429F4,0x0A2C86DA,0xE9B66DFB, 0x68DC1462,0xD7486900,0x680EC0A4,0x27A18DEE,0x4F3FFEA2,0xE887AD8C,0xB58CE006,0x7AF4D6B6, 0xAACE1E7C,0xD3375FEC,0xCE78A399,0x406B2A42,0x20FE9E35,0xD9F385B9,0xEE39D7AB,0x3B124E8B, 0x1DC9FAF7,0x4B6D1856,0x26A36631,0xEAE397B2,0x3A6EFA74,0xDD5B4332,0x6841E7F7,0xCA7820FB, 0xFB0AF54E,0xD8FEB397,0x454056AC,0xBA489527,0x55533A3A,0x20838D87,0xFE6BA9B7,0xD096954B, 0x55A867BC,0xA1159A58,0xCCA92963,0x99E1DB33,0xA62A4A56,0x3F3125F9,0x5EF47E1C,0x9029317C, 0xFDF8E802,0x04272F70,0x80BB155C,0x05282CE3,0x95C11548,0xE4C66D22,0x48C1133F,0xC70F86DC, 0x07F9C9EE,0x41041F0F,0x404779A4,0x5D886E17,0x325F51EB,0xD59BC0D1,0xF2BCC18F,0x41113564, 0x257B7834,0x602A9C60,0xDFF8E8A3,0x1F636C1B,0x0E12B4C2,0x02E1329E,0xAF664FD1,0xCAD18115, 0x6B2395E0,0x333E92E1,0x3B240B62,0xEEBEB922,0x85B2A20E,0xE6BA0D99,0xDE720C8C,0x2DA2F728, 0xD0127845,0x95B794FD,0x647D0862,0xE7CCF5F0,0x5449A36F,0x877D48FA,0xC39DFD27,0xF33E8D1E, 0x0A476341,0x992EFF74,0x3A6F6EAB,0xF4F8FD37,0xA812DC60,0xA1EBDDF8,0x991BE14C,0xDB6E6B0D, 0xC67B5510,0x6D672C37,0x2765D43B,0xDCD0E804,0xF1290DC7,0xCC00FFA3,0xB5390F92,0x690FED0B, 0x667B9FFB,0xCEDB7D9C,0xA091CF0B,0xD9155EA3,0xBB132F88,0x515BAD24,0x7B9479BF,0x763BD6EB, 0x37392EB3,0xCC115979,0x8026E297,0xF42E312D,0x6842ADA7,0xC66A2B3B,0x12754CCC,0x782EF11C, 0x6A124237,0xB79251E7,0x06A1BBE6,0x4BFB6350,0x1A6B1018,0x11CAEDFA,0x3D25BDD8,0xE2E1C3C9, 0x44421659,0x0A121386,0xD90CEC6E,0xD5ABEA2A,0x64AF674E,0xDA86A85F,0xBEBFE988,0x64E4C3FE, 0x9DBC8057,0xF0F7C086,0x60787BF8,0x6003604D,0xD1FD8346,0xF6381FB0,0x7745AE04,0xD736FCCC, 0x83426B33,0xF01EAB71,0xB0804187,0x3C005E5F,0x77A057BE,0xBDE8AE24,0x55464299,0xBF582E61, 0x4E58F48F,0xF2DDFDA2,0xF474EF38,0x8789BDC2,0x5366F9C3,0xC8B38E74,0xB475F255,0x46FCD9B9, 0x7AEB2661,0x8B1DDF84,0x846A0E79,0x915F95E2,0x466E598E,0x20B45770,0x8CD55591,0xC902DE4C, 0xB90BACE1,0xBB8205D0,0x11A86248,0x7574A99E,0xB77F19B6,0xE0A9DC09,0x662D09A1,0xC4324633, 0xE85A1F02,0x09F0BE8C,0x4A99A025,0x1D6EFE10,0x1AB93D1D,0x0BA5A4DF,0xA186F20F,0x2868F169, 0xDCB7DA83,0x573906FE,0xA1E2CE9B,0x4FCD7F52,0x50115E01,0xA70683FA,0xA002B5C4,0x0DE6D027, 0x9AF88C27,0x773F8641,0xC3604C06,0x61A806B5,0xF0177A28,0xC0F586E0,0x006058AA,0x30DC7D62, 0x11E69ED7,0x2338EA63,0x53C2DD94,0xC2C21634,0xBBCBEE56,0x90BCB6DE,0xEBFC7DA1,0xCE591D76, 0x6F05E409,0x4B7C0188,0x39720A3D,0x7C927C24,0x86E3725F,0x724D9DB9,0x1AC15BB4,0xD39EB8FC, 0xED545578,0x08FCA5B5,0xD83D7CD3,0x4DAD0FC4,0x1E50EF5E,0xB161E6F8,0xA28514D9,0x6C51133C, 0x6FD5C7E7,0x56E14EC4,0x362ABFCE,0xDDC6C837,0xD79A3234,0x92638212,0x670EFA8E,0x406000E0 },{ 0x3A39CE37,0xD3FAF5CF,0xABC27737,0x5AC52D1B,0x5CB0679E,0x4FA33742,0xD3822740,0x99BC9BBE, 0xD5118E9D,0xBF0F7315,0xD62D1C7E,0xC700C47B,0xB78C1B6B,0x21A19045,0xB26EB1BE,0x6A366EB4, 0x5748AB2F,0xBC946E79,0xC6A376D2,0x6549C2C8,0x530FF8EE,0x468DDE7D,0xD5730A1D,0x4CD04DC6, 0x2939BBDB,0xA9BA4650,0xAC9526E8,0xBE5EE304,0xA1FAD5F0,0x6A2D519A,0x63EF8CE2,0x9A86EE22, 0xC089C2B8,0x43242EF6,0xA51E03AA,0x9CF2D0A4,0x83C061BA,0x9BE96A4D,0x8FE51550,0xBA645BD6, 0x2826A2F9,0xA73A3AE1,0x4BA99586,0xEF5562E9,0xC72FEFD3,0xF752F7DA,0x3F046F69,0x77FA0A59, 0x80E4A915,0x87B08601,0x9B09E6AD,0x3B3EE593,0xE990FD5A,0x9E34D797,0x2CF0B7D9,0x022B8B51, 0x96D5AC3A,0x017DA67D,0xD1CF3ED6,0x7C7D2D28,0x1F9F25CF,0xADF2B89B,0x5AD6B472,0x5A88F54C, 0xE029AC71,0xE019A5E6,0x47B0ACFD,0xED93FA9B,0xE8D3C48D,0x283B57CC,0xF8D56629,0x79132E28, 0x785F0191,0xED756055,0xF7960E44,0xE3D35E8C,0x15056DD4,0x88F46DBA,0x03A16125,0x0564F0BD, 0xC3EB9E15,0x3C9057A2,0x97271AEC,0xA93A072A,0x1B3F6D9B,0x1E6321F5,0xF59C66FB,0x26DCF319, 0x7533D928,0xB155FDF5,0x03563482,0x8ABA3CBB,0x28517711,0xC20AD9F8,0xABCC5167,0xCCAD925F, 0x4DE81751,0x3830DC8E,0x379D5862,0x9320F991,0xEA7A90C2,0xFB3E7BCE,0x5121CE64,0x774FBE32, 0xA8B6E37E,0xC3293D46,0x48DE5369,0x6413E680,0xA2AE0810,0xDD6DB224,0x69852DFD,0x09072166, 0xB39A460A,0x6445C0DD,0x586CDECF,0x1C20C8AE,0x5BBEF7DD,0x1B588D40,0xCCD2017F,0x6BB4E3BB, 0xDDA26A7E,0x3A59FF45,0x3E350A44,0xBCB4CDD5,0x72EACEA8,0xFA6484BB,0x8D6612AE,0xBF3C6F47, 0xD29BE463,0x542F5D9E,0xAEC2771B,0xF64E6370,0x740E0D8D,0xE75B1357,0xF8721671,0xAF537D5D, 0x4040CB08,0x4EB4E2CC,0x34D2466A,0x0115AF84,0xE1B00428,0x95983A1D,0x06B89FB4,0xCE6EA048, 0x6F3F3B82,0x3520AB82,0x011A1D4B,0x277227F8,0x611560B1,0xE7933FDC,0xBB3A792B,0x344525BD, 0xA08839E1,0x51CE794B,0x2F32C9B7,0xA01FBAC9,0xE01CC87E,0xBCC7D1F6,0xCF0111C3,0xA1E8AAC7, 0x1A908749,0xD44FBD9A,0xD0DADECB,0xD50ADA38,0x0339C32A,0xC6913667,0x8DF9317C,0xE0B12B4F, 0xF79E59B7,0x43F5BB3A,0xF2D519FF,0x27D9459C,0xBF97222C,0x15E6FC2A,0x0F91FC71,0x9B941525, 0xFAE59361,0xCEB69CEB,0xC2A86459,0x12BAA8D1,0xB6C1075E,0xE3056A0C,0x10D25065,0xCB03A442, 0xE0EC6E0E,0x1698DB3B,0x4C98A0BE,0x3278E964,0x9F1F9532,0xE0D392DF,0xD3A0342B,0x8971F21E, 0x1B0A7441,0x4BA3348C,0xC5BE7120,0xC37632D8,0xDF359F8D,0x9B992F2E,0xE60B6F47,0x0FE3F11D, 0xE54CDA54,0x1EDAD891,0xCE6279CF,0xCD3E7E6F,0x1618B166,0xFD2C1D05,0x848FD2C5,0xF6FB2299, 0xF523F357,0xA6327623,0x93A83531,0x56CCCD02,0xACF08162,0x5A75EBB5,0x6E163697,0x88D273CC, 0xDE966292,0x81B949D0,0x4C50901B,0x71C65614,0xE6C6C7BD,0x327A140A,0x45E1D006,0xC3F27B9A, 0xC9AA53FD,0x62A80F00,0xBB25BFE2,0x35BDD2F6,0x71126905,0xB2040222,0xB6CBCF7C,0xCD769C2B, 0x53113EC0,0x1640E3D3,0x38ABBD60,0x2547ADF0,0xBA38209C,0xF746CE76,0x77AFA1C5,0x20756060, 0x85CBFE4E,0x8AE88DD8,0x7AAAF9B0,0x4CF9AA7E,0x1948C25C,0x02FB8A8C,0x01C36AE4,0xD6EBE1F9, 0x90D4F869,0xA65CDEA0,0x3F09252D,0xC208E69F,0xB74E6132,0xCE77E25B,0x578FDFE3,0x3AC372E6 } }; /*********************** FUNCTION DEFINITIONS ***********************/ void blowfish_encrypt(const BYTE in[], BYTE out[], const BLOWFISH_KEY *keystruct) { WORD l,r,t; //,i; l = (in[0] << 24) | (in[1] << 16) | (in[2] << 8) | (in[3]); r = (in[4] << 24) | (in[5] << 16) | (in[6] << 8) | (in[7]); ITERATION(l,r,t,0); ITERATION(l,r,t,1); ITERATION(l,r,t,2); ITERATION(l,r,t,3); ITERATION(l,r,t,4); ITERATION(l,r,t,5); ITERATION(l,r,t,6); ITERATION(l,r,t,7); ITERATION(l,r,t,8); ITERATION(l,r,t,9); ITERATION(l,r,t,10); ITERATION(l,r,t,11); ITERATION(l,r,t,12); ITERATION(l,r,t,13); ITERATION(l,r,t,14); l ^= keystruct->p[15]; F(l,t); r^= t; //Last iteration has no swap() r ^= keystruct->p[16]; l ^= keystruct->p[17]; out[0] = l >> 24; out[1] = l >> 16; out[2] = l >> 8; out[3] = l; out[4] = r >> 24; out[5] = r >> 16; out[6] = r >> 8; out[7] = r; } void blowfish_decrypt(const BYTE in[], BYTE out[], const BLOWFISH_KEY *keystruct) { WORD l,r,t; //,i; l = (in[0] << 24) | (in[1] << 16) | (in[2] << 8) | (in[3]); r = (in[4] << 24) | (in[5] << 16) | (in[6] << 8) | (in[7]); ITERATION(l,r,t,17); ITERATION(l,r,t,16); ITERATION(l,r,t,15); ITERATION(l,r,t,14); ITERATION(l,r,t,13); ITERATION(l,r,t,12); ITERATION(l,r,t,11); ITERATION(l,r,t,10); ITERATION(l,r,t,9); ITERATION(l,r,t,8); ITERATION(l,r,t,7); ITERATION(l,r,t,6); ITERATION(l,r,t,5); ITERATION(l,r,t,4); ITERATION(l,r,t,3); l ^= keystruct->p[2]; F(l,t); r^= t; //Last iteration has no swap() r ^= keystruct->p[1]; l ^= keystruct->p[0]; out[0] = l >> 24; out[1] = l >> 16; out[2] = l >> 8; out[3] = l; out[4] = r >> 24; out[5] = r >> 16; out[6] = r >> 8; out[7] = r; } void blowfish_key_setup(const BYTE user_key[], BLOWFISH_KEY *keystruct, size_t len) { BYTE block[8]; int idx,idx2; // Copy over the constant init array vals (so the originals aren't destroyed). memcpy(keystruct->p,p_perm,sizeof(WORD) * 18); memcpy(keystruct->s,s_perm,sizeof(WORD) * 1024); // Combine the key with the P box. Assume key is standard 448 bits (56 bytes) or less. for (idx = 0, idx2 = 0; idx < 18; ++idx, idx2 += 4) keystruct->p[idx] ^= (user_key[idx2 % len] << 24) | (user_key[(idx2+1) % len] << 16) | (user_key[(idx2+2) % len] << 8) | (user_key[(idx2+3) % len]); // Re-calculate the P box. memset(block, 0, 8); for (idx = 0; idx < 18; idx += 2) { blowfish_encrypt(block,block,keystruct); keystruct->p[idx] = (block[0] << 24) | (block[1] << 16) | (block[2] << 8) | block[3]; keystruct->p[idx+1]=(block[4] << 24) | (block[5] << 16) | (block[6] << 8) | block[7]; } // Recalculate the S-boxes. for (idx = 0; idx < 4; ++idx) { for (idx2 = 0; idx2 < 256; idx2 += 2) { blowfish_encrypt(block,block,keystruct); keystruct->s[idx][idx2] = (block[0] << 24) | (block[1] << 16) | (block[2] << 8) | block[3]; keystruct->s[idx][idx2+1] = (block[4] << 24) | (block[5] << 16) | (block[6] << 8) | block[7]; } } } olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/blowfish.h000066400000000000000000000024161311755073500241500ustar00rootroot00000000000000/********************************************************************* * Filename: blowfish.h * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Defines the API for the corresponding Blowfish implementation. *********************************************************************/ #ifndef BLOWFISH_H #define BLOWFISH_H /*************************** HEADER FILES ***************************/ #include /****************************** MACROS ******************************/ #define BLOWFISH_BLOCK_SIZE 8 // Blowfish operates on 8 bytes at a time /**************************** DATA TYPES ****************************/ typedef unsigned char BYTE; // 8-bit byte typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines typedef struct { WORD p[18]; WORD s[4][256]; } BLOWFISH_KEY; /*********************** FUNCTION DECLARATIONS **********************/ void blowfish_key_setup(const BYTE user_key[], BLOWFISH_KEY *keystruct, size_t len); void blowfish_encrypt(const BYTE in[], BYTE out[], const BLOWFISH_KEY *keystruct); void blowfish_decrypt(const BYTE in[], BYTE out[], const BLOWFISH_KEY *keystruct); #endif // BLOWFISH_H olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/blowfish_test.c000066400000000000000000000051111311755073500251750ustar00rootroot00000000000000/********************************************************************* * Filename: blowfish_test.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Performs known-answer tests on the corresponding Blowfish implementation. These tests do not encompass the full range of available test vectors, however, if the tests pass it is very, very likely that the code is correct and was compiled properly. This code also serves as example usage of the functions. *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include #include "blowfish.h" /*********************** FUNCTION DEFINITIONS ***********************/ int blowfish_test() { BYTE key1[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; BYTE key2[8] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}; BYTE key3[24] = {0xF0,0xE1,0xD2,0xC3,0xB4,0xA5,0x96,0x87, 0x78,0x69,0x5A,0x4B,0x3C,0x2D,0x1E,0x0F, 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77}; BYTE p1[BLOWFISH_BLOCK_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; BYTE p2[BLOWFISH_BLOCK_SIZE] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}; BYTE p3[BLOWFISH_BLOCK_SIZE] = {0xFE,0xDC,0xBA,0x98,0x76,0x54,0x32,0x10}; BYTE c1[BLOWFISH_BLOCK_SIZE] = {0x4e,0xf9,0x97,0x45,0x61,0x98,0xdd,0x78}; BYTE c2[BLOWFISH_BLOCK_SIZE] = {0x51,0x86,0x6f,0xd5,0xb8,0x5e,0xcb,0x8a}; BYTE c3[BLOWFISH_BLOCK_SIZE] = {0x05,0x04,0x4b,0x62,0xfa,0x52,0xd0,0x80}; BYTE enc_buf[BLOWFISH_BLOCK_SIZE]; BLOWFISH_KEY key; int pass = 1; // Test vector 1. blowfish_key_setup(key1, &key, BLOWFISH_BLOCK_SIZE); blowfish_encrypt(p1, enc_buf, &key); pass = pass && !memcmp(c1, enc_buf, BLOWFISH_BLOCK_SIZE); blowfish_decrypt(c1, enc_buf, &key); pass = pass && !memcmp(p1, enc_buf, BLOWFISH_BLOCK_SIZE); // Test vector 2. blowfish_key_setup(key2, &key, BLOWFISH_BLOCK_SIZE); blowfish_encrypt(p2, enc_buf, &key); pass = pass && !memcmp(c2, enc_buf, BLOWFISH_BLOCK_SIZE); blowfish_decrypt(c2, enc_buf, &key); pass = pass && !memcmp(p2, enc_buf, BLOWFISH_BLOCK_SIZE); // Test vector 3. blowfish_key_setup(key3, &key, 24); blowfish_encrypt(p3, enc_buf, &key); pass = pass && !memcmp(c3, enc_buf, BLOWFISH_BLOCK_SIZE); blowfish_decrypt(c3, enc_buf, &key); pass = pass && !memcmp(p3, enc_buf, BLOWFISH_BLOCK_SIZE); return(pass); } int main() { printf("Blowfish tests: %s\n", blowfish_test() ? "SUCCEEDED" : "FAILED"); return(0); } olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/des.c000066400000000000000000000305151311755073500231020ustar00rootroot00000000000000/********************************************************************* * Filename: des.c * Author: Brad Conte (brad AT radconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Implementation of the DES encryption algorithm. Modes of operation (such as CBC) are not included. The formal NIST algorithm specification can be found here: * http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include #include "des.h" /****************************** MACROS ******************************/ // Obtain bit "b" from the left and shift it "c" places from the right #define BITNUM(a,b,c) (((a[(b)/8] >> (7 - (b%8))) & 0x01) << (c)) #define BITNUMINTR(a,b,c) ((((a) >> (31 - (b))) & 0x00000001) << (c)) #define BITNUMINTL(a,b,c) ((((a) << (b)) & 0x80000000) >> (c)) // This macro converts a 6 bit block with the S-Box row defined as the first and last // bits to a 6 bit block with the row defined by the first two bits. #define SBOXBIT(a) (((a) & 0x20) | (((a) & 0x1f) >> 1) | (((a) & 0x01) << 4)) /**************************** VARIABLES *****************************/ static const BYTE sbox1[64] = { 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 }; static const BYTE sbox2[64] = { 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 }; static const BYTE sbox3[64] = { 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 }; static const BYTE sbox4[64] = { 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 }; static const BYTE sbox5[64] = { 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 }; static const BYTE sbox6[64] = { 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 }; static const BYTE sbox7[64] = { 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 }; static const BYTE sbox8[64] = { 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 }; /*********************** FUNCTION DEFINITIONS ***********************/ // Initial (Inv)Permutation step void IP(WORD state[], const BYTE in[]) { state[0] = BITNUM(in,57,31) | BITNUM(in,49,30) | BITNUM(in,41,29) | BITNUM(in,33,28) | BITNUM(in,25,27) | BITNUM(in,17,26) | BITNUM(in,9,25) | BITNUM(in,1,24) | BITNUM(in,59,23) | BITNUM(in,51,22) | BITNUM(in,43,21) | BITNUM(in,35,20) | BITNUM(in,27,19) | BITNUM(in,19,18) | BITNUM(in,11,17) | BITNUM(in,3,16) | BITNUM(in,61,15) | BITNUM(in,53,14) | BITNUM(in,45,13) | BITNUM(in,37,12) | BITNUM(in,29,11) | BITNUM(in,21,10) | BITNUM(in,13,9) | BITNUM(in,5,8) | BITNUM(in,63,7) | BITNUM(in,55,6) | BITNUM(in,47,5) | BITNUM(in,39,4) | BITNUM(in,31,3) | BITNUM(in,23,2) | BITNUM(in,15,1) | BITNUM(in,7,0); state[1] = BITNUM(in,56,31) | BITNUM(in,48,30) | BITNUM(in,40,29) | BITNUM(in,32,28) | BITNUM(in,24,27) | BITNUM(in,16,26) | BITNUM(in,8,25) | BITNUM(in,0,24) | BITNUM(in,58,23) | BITNUM(in,50,22) | BITNUM(in,42,21) | BITNUM(in,34,20) | BITNUM(in,26,19) | BITNUM(in,18,18) | BITNUM(in,10,17) | BITNUM(in,2,16) | BITNUM(in,60,15) | BITNUM(in,52,14) | BITNUM(in,44,13) | BITNUM(in,36,12) | BITNUM(in,28,11) | BITNUM(in,20,10) | BITNUM(in,12,9) | BITNUM(in,4,8) | BITNUM(in,62,7) | BITNUM(in,54,6) | BITNUM(in,46,5) | BITNUM(in,38,4) | BITNUM(in,30,3) | BITNUM(in,22,2) | BITNUM(in,14,1) | BITNUM(in,6,0); } void InvIP(WORD state[], BYTE in[]) { in[0] = BITNUMINTR(state[1],7,7) | BITNUMINTR(state[0],7,6) | BITNUMINTR(state[1],15,5) | BITNUMINTR(state[0],15,4) | BITNUMINTR(state[1],23,3) | BITNUMINTR(state[0],23,2) | BITNUMINTR(state[1],31,1) | BITNUMINTR(state[0],31,0); in[1] = BITNUMINTR(state[1],6,7) | BITNUMINTR(state[0],6,6) | BITNUMINTR(state[1],14,5) | BITNUMINTR(state[0],14,4) | BITNUMINTR(state[1],22,3) | BITNUMINTR(state[0],22,2) | BITNUMINTR(state[1],30,1) | BITNUMINTR(state[0],30,0); in[2] = BITNUMINTR(state[1],5,7) | BITNUMINTR(state[0],5,6) | BITNUMINTR(state[1],13,5) | BITNUMINTR(state[0],13,4) | BITNUMINTR(state[1],21,3) | BITNUMINTR(state[0],21,2) | BITNUMINTR(state[1],29,1) | BITNUMINTR(state[0],29,0); in[3] = BITNUMINTR(state[1],4,7) | BITNUMINTR(state[0],4,6) | BITNUMINTR(state[1],12,5) | BITNUMINTR(state[0],12,4) | BITNUMINTR(state[1],20,3) | BITNUMINTR(state[0],20,2) | BITNUMINTR(state[1],28,1) | BITNUMINTR(state[0],28,0); in[4] = BITNUMINTR(state[1],3,7) | BITNUMINTR(state[0],3,6) | BITNUMINTR(state[1],11,5) | BITNUMINTR(state[0],11,4) | BITNUMINTR(state[1],19,3) | BITNUMINTR(state[0],19,2) | BITNUMINTR(state[1],27,1) | BITNUMINTR(state[0],27,0); in[5] = BITNUMINTR(state[1],2,7) | BITNUMINTR(state[0],2,6) | BITNUMINTR(state[1],10,5) | BITNUMINTR(state[0],10,4) | BITNUMINTR(state[1],18,3) | BITNUMINTR(state[0],18,2) | BITNUMINTR(state[1],26,1) | BITNUMINTR(state[0],26,0); in[6] = BITNUMINTR(state[1],1,7) | BITNUMINTR(state[0],1,6) | BITNUMINTR(state[1],9,5) | BITNUMINTR(state[0],9,4) | BITNUMINTR(state[1],17,3) | BITNUMINTR(state[0],17,2) | BITNUMINTR(state[1],25,1) | BITNUMINTR(state[0],25,0); in[7] = BITNUMINTR(state[1],0,7) | BITNUMINTR(state[0],0,6) | BITNUMINTR(state[1],8,5) | BITNUMINTR(state[0],8,4) | BITNUMINTR(state[1],16,3) | BITNUMINTR(state[0],16,2) | BITNUMINTR(state[1],24,1) | BITNUMINTR(state[0],24,0); } WORD f(WORD state, const BYTE key[]) { BYTE lrgstate[6]; //,i; WORD t1,t2; // Expantion Permutation t1 = BITNUMINTL(state,31,0) | ((state & 0xf0000000) >> 1) | BITNUMINTL(state,4,5) | BITNUMINTL(state,3,6) | ((state & 0x0f000000) >> 3) | BITNUMINTL(state,8,11) | BITNUMINTL(state,7,12) | ((state & 0x00f00000) >> 5) | BITNUMINTL(state,12,17) | BITNUMINTL(state,11,18) | ((state & 0x000f0000) >> 7) | BITNUMINTL(state,16,23); t2 = BITNUMINTL(state,15,0) | ((state & 0x0000f000) << 15) | BITNUMINTL(state,20,5) | BITNUMINTL(state,19,6) | ((state & 0x00000f00) << 13) | BITNUMINTL(state,24,11) | BITNUMINTL(state,23,12) | ((state & 0x000000f0) << 11) | BITNUMINTL(state,28,17) | BITNUMINTL(state,27,18) | ((state & 0x0000000f) << 9) | BITNUMINTL(state,0,23); lrgstate[0] = (t1 >> 24) & 0x000000ff; lrgstate[1] = (t1 >> 16) & 0x000000ff; lrgstate[2] = (t1 >> 8) & 0x000000ff; lrgstate[3] = (t2 >> 24) & 0x000000ff; lrgstate[4] = (t2 >> 16) & 0x000000ff; lrgstate[5] = (t2 >> 8) & 0x000000ff; // Key XOR lrgstate[0] ^= key[0]; lrgstate[1] ^= key[1]; lrgstate[2] ^= key[2]; lrgstate[3] ^= key[3]; lrgstate[4] ^= key[4]; lrgstate[5] ^= key[5]; // S-Box Permutation state = (sbox1[SBOXBIT(lrgstate[0] >> 2)] << 28) | (sbox2[SBOXBIT(((lrgstate[0] & 0x03) << 4) | (lrgstate[1] >> 4))] << 24) | (sbox3[SBOXBIT(((lrgstate[1] & 0x0f) << 2) | (lrgstate[2] >> 6))] << 20) | (sbox4[SBOXBIT(lrgstate[2] & 0x3f)] << 16) | (sbox5[SBOXBIT(lrgstate[3] >> 2)] << 12) | (sbox6[SBOXBIT(((lrgstate[3] & 0x03) << 4) | (lrgstate[4] >> 4))] << 8) | (sbox7[SBOXBIT(((lrgstate[4] & 0x0f) << 2) | (lrgstate[5] >> 6))] << 4) | sbox8[SBOXBIT(lrgstate[5] & 0x3f)]; // P-Box Permutation state = BITNUMINTL(state,15,0) | BITNUMINTL(state,6,1) | BITNUMINTL(state,19,2) | BITNUMINTL(state,20,3) | BITNUMINTL(state,28,4) | BITNUMINTL(state,11,5) | BITNUMINTL(state,27,6) | BITNUMINTL(state,16,7) | BITNUMINTL(state,0,8) | BITNUMINTL(state,14,9) | BITNUMINTL(state,22,10) | BITNUMINTL(state,25,11) | BITNUMINTL(state,4,12) | BITNUMINTL(state,17,13) | BITNUMINTL(state,30,14) | BITNUMINTL(state,9,15) | BITNUMINTL(state,1,16) | BITNUMINTL(state,7,17) | BITNUMINTL(state,23,18) | BITNUMINTL(state,13,19) | BITNUMINTL(state,31,20) | BITNUMINTL(state,26,21) | BITNUMINTL(state,2,22) | BITNUMINTL(state,8,23) | BITNUMINTL(state,18,24) | BITNUMINTL(state,12,25) | BITNUMINTL(state,29,26) | BITNUMINTL(state,5,27) | BITNUMINTL(state,21,28) | BITNUMINTL(state,10,29) | BITNUMINTL(state,3,30) | BITNUMINTL(state,24,31); // Return the final state value return(state); } void des_key_setup(const BYTE key[], BYTE schedule[][6], DES_MODE mode) { WORD i, j, to_gen, C, D; const WORD key_rnd_shift[16] = {1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1}; const WORD key_perm_c[28] = {56,48,40,32,24,16,8,0,57,49,41,33,25,17, 9,1,58,50,42,34,26,18,10,2,59,51,43,35}; const WORD key_perm_d[28] = {62,54,46,38,30,22,14,6,61,53,45,37,29,21, 13,5,60,52,44,36,28,20,12,4,27,19,11,3}; const WORD key_compression[48] = {13,16,10,23,0,4,2,27,14,5,20,9, 22,18,11,3,25,7,15,6,26,19,12,1, 40,51,30,36,46,54,29,39,50,44,32,47, 43,48,38,55,33,52,45,41,49,35,28,31}; // Permutated Choice #1 (copy the key in, ignoring parity bits). for (i = 0, j = 31, C = 0; i < 28; ++i, --j) C |= BITNUM(key,key_perm_c[i],j); for (i = 0, j = 31, D = 0; i < 28; ++i, --j) D |= BITNUM(key,key_perm_d[i],j); // Generate the 16 subkeys. for (i = 0; i < 16; ++i) { C = ((C << key_rnd_shift[i]) | (C >> (28-key_rnd_shift[i]))) & 0xfffffff0; D = ((D << key_rnd_shift[i]) | (D >> (28-key_rnd_shift[i]))) & 0xfffffff0; // Decryption subkeys are reverse order of encryption subkeys so // generate them in reverse if the key schedule is for decryption useage. if (mode == DES_DECRYPT) to_gen = 15 - i; else /*(if mode == DES_ENCRYPT)*/ to_gen = i; // Initialize the array for (j = 0; j < 6; ++j) schedule[to_gen][j] = 0; for (j = 0; j < 24; ++j) schedule[to_gen][j/8] |= BITNUMINTR(C,key_compression[j],7 - (j%8)); for ( ; j < 48; ++j) schedule[to_gen][j/8] |= BITNUMINTR(D,key_compression[j] - 28,7 - (j%8)); } } void des_crypt(const BYTE in[], BYTE out[], const BYTE key[][6]) { WORD state[2],idx,t; IP(state,in); for (idx=0; idx < 15; ++idx) { t = state[1]; state[1] = f(state[1],key[idx]) ^ state[0]; state[0] = t; } // Perform the final loop manually as it doesn't switch sides state[0] = f(state[1],key[15]) ^ state[0]; InvIP(state,out); } void three_des_key_setup(const BYTE key[], BYTE schedule[][16][6], DES_MODE mode) { if (mode == DES_ENCRYPT) { des_key_setup(&key[0],schedule[0],mode); des_key_setup(&key[8],schedule[1],!mode); des_key_setup(&key[16],schedule[2],mode); } else /*if (mode == DES_DECRYPT*/ { des_key_setup(&key[16],schedule[0],mode); des_key_setup(&key[8],schedule[1],!mode); des_key_setup(&key[0],schedule[2],mode); } } void three_des_crypt(const BYTE in[], BYTE out[], const BYTE key[][16][6]) { des_crypt(in,out,key[0]); des_crypt(out,out,key[1]); des_crypt(out,out,key[2]); } olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/des.h000066400000000000000000000027141311755073500231070ustar00rootroot00000000000000/********************************************************************* * Filename: des.h * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Defines the API for the corresponding DES implementation. Note that encryption and decryption are defined by how the key setup is performed, the actual en/de-cryption is performed by the same function. *********************************************************************/ #ifndef DES_H #define DESH /*************************** HEADER FILES ***************************/ #include /****************************** MACROS ******************************/ #define DES_BLOCK_SIZE 8 // DES operates on 8 bytes at a time /**************************** DATA TYPES ****************************/ typedef unsigned char BYTE; // 8-bit byte typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines typedef enum { DES_ENCRYPT, DES_DECRYPT } DES_MODE; /*********************** FUNCTION DECLARATIONS **********************/ void des_key_setup(const BYTE key[], BYTE schedule[][6], DES_MODE mode); void des_crypt(const BYTE in[], BYTE out[], const BYTE key[][6]); void three_des_key_setup(const BYTE key[], BYTE schedule[][16][6], DES_MODE mode); void three_des_crypt(const BYTE in[], BYTE out[], const BYTE key[][16][6]); #endif // DES_H olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/des_test.c000066400000000000000000000064561311755073500241500ustar00rootroot00000000000000/********************************************************************* * Filename: des_test.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Performs known-answer tests on the corresponding DES implementation. These tests do not encompass the full range of available test vectors, however, if the tests pass it is very, very likely that the code is correct and was compiled properly. This code also serves as example usage of the functions. *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include #include "des.h" /*********************** FUNCTION DEFINITIONS ***********************/ int des_test() { BYTE pt1[DES_BLOCK_SIZE] = {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xE7}; BYTE pt2[DES_BLOCK_SIZE] = {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}; BYTE pt3[DES_BLOCK_SIZE] = {0x54,0x68,0x65,0x20,0x71,0x75,0x66,0x63}; BYTE ct1[DES_BLOCK_SIZE] = {0xc9,0x57,0x44,0x25,0x6a,0x5e,0xd3,0x1d}; BYTE ct2[DES_BLOCK_SIZE] = {0x85,0xe8,0x13,0x54,0x0f,0x0a,0xb4,0x05}; BYTE ct3[DES_BLOCK_SIZE] = {0xc9,0x57,0x44,0x25,0x6a,0x5e,0xd3,0x1d}; BYTE ct4[DES_BLOCK_SIZE] = {0xA8,0x26,0xFD,0x8C,0xE5,0x3B,0x85,0x5F}; BYTE key1[DES_BLOCK_SIZE] = {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}; BYTE key2[DES_BLOCK_SIZE] = {0x13,0x34,0x57,0x79,0x9B,0xBC,0xDF,0xF1}; BYTE three_key1[DES_BLOCK_SIZE * 3] = {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF, 0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF, 0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF}; BYTE three_key2[DES_BLOCK_SIZE * 3] = {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF, 0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF,0x01, 0x45,0x67,0x89,0xAB,0xCD,0xEF,0x01,0x23}; BYTE schedule[16][6]; BYTE three_schedule[3][16][6]; BYTE buf[DES_BLOCK_SIZE]; int pass = 1; des_key_setup(key1, schedule, DES_ENCRYPT); des_crypt(pt1, buf, schedule); pass = pass && !memcmp(ct1, buf, DES_BLOCK_SIZE); des_key_setup(key1, schedule, DES_DECRYPT); des_crypt(ct1, buf, schedule); pass = pass && !memcmp(pt1, buf, DES_BLOCK_SIZE); des_key_setup(key2, schedule, DES_ENCRYPT); des_crypt(pt2, buf, schedule); pass = pass && !memcmp(ct2, buf, DES_BLOCK_SIZE); des_key_setup(key2, schedule, DES_DECRYPT); des_crypt(ct2, buf, schedule); pass = pass && !memcmp(pt2, buf, DES_BLOCK_SIZE); three_des_key_setup(three_key1, three_schedule, DES_ENCRYPT); three_des_crypt(pt1, buf, three_schedule); pass = pass && !memcmp(ct3, buf, DES_BLOCK_SIZE); three_des_key_setup(three_key1, three_schedule, DES_DECRYPT); three_des_crypt(ct3, buf, three_schedule); pass = pass && !memcmp(pt1, buf, DES_BLOCK_SIZE); three_des_key_setup(three_key2, three_schedule, DES_ENCRYPT); three_des_crypt(pt3, buf, three_schedule); pass = pass && !memcmp(ct4, buf, DES_BLOCK_SIZE); three_des_key_setup(three_key2, three_schedule, DES_DECRYPT); three_des_crypt(ct4, buf, three_schedule); pass = pass && !memcmp(pt3, buf, DES_BLOCK_SIZE); return(pass); } int main() { printf("DES test: %s\n", des_test() ? "SUCCEEDED" : "FAILED"); return(0); } olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/md2.c000066400000000000000000000061061311755073500230100ustar00rootroot00000000000000/********************************************************************* * Filename: md2.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Implementation of the MD2 hashing algorithm. Algorithm specification can be found here: * http://tools.ietf.org/html/rfc1319 . Input is little endian byte order. *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include #include "md2.h" /**************************** VARIABLES *****************************/ static const BYTE s[256] = { 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6, 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63, 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210, 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157, 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27, 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15, 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197, 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65, 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123, 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233, 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228, 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237, 31, 26, 219, 153, 141, 51, 159, 17, 131, 20 }; /*********************** FUNCTION DEFINITIONS ***********************/ void md2_transform(MD2_CTX *ctx, BYTE data[]) { int j,k,t; //memcpy(&ctx->state[16], data); for (j=0; j < 16; ++j) { ctx->state[j + 16] = data[j]; ctx->state[j + 32] = (ctx->state[j+16] ^ ctx->state[j]); } t = 0; for (j = 0; j < 18; ++j) { for (k = 0; k < 48; ++k) { ctx->state[k] ^= s[t]; t = ctx->state[k]; } t = (t+j) & 0xFF; } t = ctx->checksum[15]; for (j=0; j < 16; ++j) { ctx->checksum[j] ^= s[data[j] ^ t]; t = ctx->checksum[j]; } } void md2_init(MD2_CTX *ctx) { int i; for (i=0; i < 48; ++i) ctx->state[i] = 0; for (i=0; i < 16; ++i) ctx->checksum[i] = 0; ctx->len = 0; } void md2_update(MD2_CTX *ctx, const BYTE data[], size_t len) { size_t i; for (i = 0; i < len; ++i) { ctx->data[ctx->len] = data[i]; ctx->len++; if (ctx->len == MD2_BLOCK_SIZE) { md2_transform(ctx, ctx->data); ctx->len = 0; } } } void md2_final(MD2_CTX *ctx, BYTE hash[]) { int to_pad; to_pad = MD2_BLOCK_SIZE - ctx->len; while (ctx->len < MD2_BLOCK_SIZE) ctx->data[ctx->len++] = to_pad; md2_transform(ctx, ctx->data); md2_transform(ctx, ctx->checksum); memcpy(hash, ctx->state, MD2_BLOCK_SIZE); } olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/md2.h000066400000000000000000000020641311755073500230140ustar00rootroot00000000000000/********************************************************************* * Filename: md2.h * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Defines the API for the corresponding MD2 implementation. *********************************************************************/ #ifndef MD2_H #define MD2_H /*************************** HEADER FILES ***************************/ #include /****************************** MACROS ******************************/ #define MD2_BLOCK_SIZE 16 /**************************** DATA TYPES ****************************/ typedef unsigned char BYTE; // 8-bit byte typedef struct { BYTE data[16]; BYTE state[48]; BYTE checksum[16]; int len; } MD2_CTX; /*********************** FUNCTION DECLARATIONS **********************/ void md2_init(MD2_CTX *ctx); void md2_update(MD2_CTX *ctx, const BYTE data[], size_t len); void md2_final(MD2_CTX *ctx, BYTE hash[]); // size of hash must be MD2_BLOCK_SIZE #endif // MD2_H olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/md2_test.c000066400000000000000000000041031311755073500240420ustar00rootroot00000000000000/********************************************************************* * Filename: md2_test.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Performs known-answer tests on the corresponding MD2 implementation. These tests do not encompass the full range of available test vectors, however, if the tests pass it is very, very likely that the code is correct and was compiled properly. This code also serves as example usage of the functions. *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include #include #include "md2.h" /*********************** FUNCTION DEFINITIONS ***********************/ int md2_test() { BYTE text1[] = {"abc"}; BYTE text2[] = {"abcdefghijklmnopqrstuvwxyz"}; BYTE text3_1[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"}; BYTE text3_2[] = {"fghijklmnopqrstuvwxyz0123456789"}; BYTE hash1[MD2_BLOCK_SIZE] = {0xda,0x85,0x3b,0x0d,0x3f,0x88,0xd9,0x9b,0x30,0x28,0x3a,0x69,0xe6,0xde,0xd6,0xbb}; BYTE hash2[MD2_BLOCK_SIZE] = {0x4e,0x8d,0xdf,0xf3,0x65,0x02,0x92,0xab,0x5a,0x41,0x08,0xc3,0xaa,0x47,0x94,0x0b}; BYTE hash3[MD2_BLOCK_SIZE] = {0xda,0x33,0xde,0xf2,0xa4,0x2d,0xf1,0x39,0x75,0x35,0x28,0x46,0xc3,0x03,0x38,0xcd}; BYTE buf[16]; MD2_CTX ctx; int pass = 1; md2_init(&ctx); md2_update(&ctx, text1, strlen(text1)); md2_final(&ctx, buf); pass = pass && !memcmp(hash1, buf, MD2_BLOCK_SIZE); // Note that the MD2 object can be re-used. md2_init(&ctx); md2_update(&ctx, text2, strlen(text2)); md2_final(&ctx, buf); pass = pass && !memcmp(hash2, buf, MD2_BLOCK_SIZE); // Note that the data is added in two chunks. md2_init(&ctx); md2_update(&ctx, text3_1, strlen(text3_1)); md2_update(&ctx, text3_2, strlen(text3_2)); md2_final(&ctx, buf); pass = pass && !memcmp(hash3, buf, MD2_BLOCK_SIZE); return(pass); } int main() { printf("MD2 tests: %s\n", md2_test() ? "SUCCEEDED" : "FAILED"); } olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/md5.c000066400000000000000000000133101311755073500230060ustar00rootroot00000000000000/********************************************************************* * Filename: md5.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Implementation of the MD5 hashing algorithm. Algorithm specification can be found here: * http://tools.ietf.org/html/rfc1321 This implementation uses little endian byte order. *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include #include "md5.h" /****************************** MACROS ******************************/ #define ROTLEFT(a,b) ((a << b) | (a >> (32-b))) #define F(x,y,z) ((x & y) | (~x & z)) #define G(x,y,z) ((x & z) | (y & ~z)) #define H(x,y,z) (x ^ y ^ z) #define I(x,y,z) (y ^ (x | ~z)) #define FF(a,b,c,d,m,s,t) { a += F(b,c,d) + m + t; \ a = b + ROTLEFT(a,s); } #define GG(a,b,c,d,m,s,t) { a += G(b,c,d) + m + t; \ a = b + ROTLEFT(a,s); } #define HH(a,b,c,d,m,s,t) { a += H(b,c,d) + m + t; \ a = b + ROTLEFT(a,s); } #define II(a,b,c,d,m,s,t) { a += I(b,c,d) + m + t; \ a = b + ROTLEFT(a,s); } /*********************** FUNCTION DEFINITIONS ***********************/ void md5_transform(MD5_CTX *ctx, const BYTE data[]) { WORD a, b, c, d, m[16], i, j; // MD5 specifies big endian byte order, but this implementation assumes a little // endian byte order CPU. Reverse all the bytes upon input, and re-reverse them // on output (in md5_final()). for (i = 0, j = 0; i < 16; ++i, j += 4) m[i] = (data[j]) + (data[j + 1] << 8) + (data[j + 2] << 16) + (data[j + 3] << 24); a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3]; FF(a,b,c,d,m[0], 7,0xd76aa478); FF(d,a,b,c,m[1], 12,0xe8c7b756); FF(c,d,a,b,m[2], 17,0x242070db); FF(b,c,d,a,m[3], 22,0xc1bdceee); FF(a,b,c,d,m[4], 7,0xf57c0faf); FF(d,a,b,c,m[5], 12,0x4787c62a); FF(c,d,a,b,m[6], 17,0xa8304613); FF(b,c,d,a,m[7], 22,0xfd469501); FF(a,b,c,d,m[8], 7,0x698098d8); FF(d,a,b,c,m[9], 12,0x8b44f7af); FF(c,d,a,b,m[10],17,0xffff5bb1); FF(b,c,d,a,m[11],22,0x895cd7be); FF(a,b,c,d,m[12], 7,0x6b901122); FF(d,a,b,c,m[13],12,0xfd987193); FF(c,d,a,b,m[14],17,0xa679438e); FF(b,c,d,a,m[15],22,0x49b40821); GG(a,b,c,d,m[1], 5,0xf61e2562); GG(d,a,b,c,m[6], 9,0xc040b340); GG(c,d,a,b,m[11],14,0x265e5a51); GG(b,c,d,a,m[0], 20,0xe9b6c7aa); GG(a,b,c,d,m[5], 5,0xd62f105d); GG(d,a,b,c,m[10], 9,0x02441453); GG(c,d,a,b,m[15],14,0xd8a1e681); GG(b,c,d,a,m[4], 20,0xe7d3fbc8); GG(a,b,c,d,m[9], 5,0x21e1cde6); GG(d,a,b,c,m[14], 9,0xc33707d6); GG(c,d,a,b,m[3], 14,0xf4d50d87); GG(b,c,d,a,m[8], 20,0x455a14ed); GG(a,b,c,d,m[13], 5,0xa9e3e905); GG(d,a,b,c,m[2], 9,0xfcefa3f8); GG(c,d,a,b,m[7], 14,0x676f02d9); GG(b,c,d,a,m[12],20,0x8d2a4c8a); HH(a,b,c,d,m[5], 4,0xfffa3942); HH(d,a,b,c,m[8], 11,0x8771f681); HH(c,d,a,b,m[11],16,0x6d9d6122); HH(b,c,d,a,m[14],23,0xfde5380c); HH(a,b,c,d,m[1], 4,0xa4beea44); HH(d,a,b,c,m[4], 11,0x4bdecfa9); HH(c,d,a,b,m[7], 16,0xf6bb4b60); HH(b,c,d,a,m[10],23,0xbebfbc70); HH(a,b,c,d,m[13], 4,0x289b7ec6); HH(d,a,b,c,m[0], 11,0xeaa127fa); HH(c,d,a,b,m[3], 16,0xd4ef3085); HH(b,c,d,a,m[6], 23,0x04881d05); HH(a,b,c,d,m[9], 4,0xd9d4d039); HH(d,a,b,c,m[12],11,0xe6db99e5); HH(c,d,a,b,m[15],16,0x1fa27cf8); HH(b,c,d,a,m[2], 23,0xc4ac5665); II(a,b,c,d,m[0], 6,0xf4292244); II(d,a,b,c,m[7], 10,0x432aff97); II(c,d,a,b,m[14],15,0xab9423a7); II(b,c,d,a,m[5], 21,0xfc93a039); II(a,b,c,d,m[12], 6,0x655b59c3); II(d,a,b,c,m[3], 10,0x8f0ccc92); II(c,d,a,b,m[10],15,0xffeff47d); II(b,c,d,a,m[1], 21,0x85845dd1); II(a,b,c,d,m[8], 6,0x6fa87e4f); II(d,a,b,c,m[15],10,0xfe2ce6e0); II(c,d,a,b,m[6], 15,0xa3014314); II(b,c,d,a,m[13],21,0x4e0811a1); II(a,b,c,d,m[4], 6,0xf7537e82); II(d,a,b,c,m[11],10,0xbd3af235); II(c,d,a,b,m[2], 15,0x2ad7d2bb); II(b,c,d,a,m[9], 21,0xeb86d391); ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d; } void md5_init(MD5_CTX *ctx) { ctx->datalen = 0; ctx->bitlen = 0; ctx->state[0] = 0x67452301; ctx->state[1] = 0xEFCDAB89; ctx->state[2] = 0x98BADCFE; ctx->state[3] = 0x10325476; } void md5_update(MD5_CTX *ctx, const BYTE data[], size_t len) { size_t i; for (i = 0; i < len; ++i) { ctx->data[ctx->datalen] = data[i]; ctx->datalen++; if (ctx->datalen == 64) { md5_transform(ctx, ctx->data); ctx->bitlen += 512; ctx->datalen = 0; } } } void md5_final(MD5_CTX *ctx, BYTE hash[]) { size_t i; i = ctx->datalen; // Pad whatever data is left in the buffer. if (ctx->datalen < 56) { ctx->data[i++] = 0x80; while (i < 56) ctx->data[i++] = 0x00; } else if (ctx->datalen >= 56) { ctx->data[i++] = 0x80; while (i < 64) ctx->data[i++] = 0x00; md5_transform(ctx, ctx->data); memset(ctx->data, 0, 56); } // Append to the padding the total message's length in bits and transform. ctx->bitlen += ctx->datalen * 8; ctx->data[56] = ctx->bitlen; ctx->data[57] = ctx->bitlen >> 8; ctx->data[58] = ctx->bitlen >> 16; ctx->data[59] = ctx->bitlen >> 24; ctx->data[60] = ctx->bitlen >> 32; ctx->data[61] = ctx->bitlen >> 40; ctx->data[62] = ctx->bitlen >> 48; ctx->data[63] = ctx->bitlen >> 56; md5_transform(ctx, ctx->data); // Since this implementation uses little endian byte ordering and MD uses big endian, // reverse all the bytes when copying the final state to the output hash. for (i = 0; i < 4; ++i) { hash[i] = (ctx->state[0] >> (i * 8)) & 0x000000ff; hash[i + 4] = (ctx->state[1] >> (i * 8)) & 0x000000ff; hash[i + 8] = (ctx->state[2] >> (i * 8)) & 0x000000ff; hash[i + 12] = (ctx->state[3] >> (i * 8)) & 0x000000ff; } } olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/md5.h000066400000000000000000000022421311755073500230150ustar00rootroot00000000000000/********************************************************************* * Filename: md5.h * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Defines the API for the corresponding MD5 implementation. *********************************************************************/ #ifndef MD5_H #define MD5_H /*************************** HEADER FILES ***************************/ #include /****************************** MACROS ******************************/ #define MD5_BLOCK_SIZE 16 // MD5 outputs a 16 byte digest /**************************** DATA TYPES ****************************/ typedef unsigned char BYTE; // 8-bit byte typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines typedef struct { BYTE data[64]; WORD datalen; unsigned long long bitlen; WORD state[4]; } MD5_CTX; /*********************** FUNCTION DECLARATIONS **********************/ void md5_init(MD5_CTX *ctx); void md5_update(MD5_CTX *ctx, const BYTE data[], size_t len); void md5_final(MD5_CTX *ctx, BYTE hash[]); #endif // MD5_H olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/md5_test.c000066400000000000000000000040611311755073500240500ustar00rootroot00000000000000/********************************************************************* * Filename: md5_test.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Performs known-answer tests on the corresponding MD5 implementation. These tests do not encompass the full range of available test vectors, however, if the tests pass it is very, very likely that the code is correct and was compiled properly. This code also serves as example usage of the functions. *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include #include #include "md5.h" /*********************** FUNCTION DEFINITIONS ***********************/ int md5_test() { BYTE text1[] = {""}; BYTE text2[] = {"abc"}; BYTE text3_1[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"}; BYTE text3_2[] = {"fghijklmnopqrstuvwxyz0123456789"}; BYTE hash1[MD5_BLOCK_SIZE] = {0xd4,0x1d,0x8c,0xd9,0x8f,0x00,0xb2,0x04,0xe9,0x80,0x09,0x98,0xec,0xf8,0x42,0x7e}; BYTE hash2[MD5_BLOCK_SIZE] = {0x90,0x01,0x50,0x98,0x3c,0xd2,0x4f,0xb0,0xd6,0x96,0x3f,0x7d,0x28,0xe1,0x7f,0x72}; BYTE hash3[MD5_BLOCK_SIZE] = {0xd1,0x74,0xab,0x98,0xd2,0x77,0xd9,0xf5,0xa5,0x61,0x1c,0x2c,0x9f,0x41,0x9d,0x9f}; BYTE buf[16]; MD5_CTX ctx; int pass = 1; md5_init(&ctx); md5_update(&ctx, text1, strlen(text1)); md5_final(&ctx, buf); pass = pass && !memcmp(hash1, buf, MD5_BLOCK_SIZE); // Note the MD5 object can be reused. md5_init(&ctx); md5_update(&ctx, text2, strlen(text2)); md5_final(&ctx, buf); pass = pass && !memcmp(hash2, buf, MD5_BLOCK_SIZE); // Note the data is being added in two chunks. md5_init(&ctx); md5_update(&ctx, text3_1, strlen(text3_1)); md5_update(&ctx, text3_2, strlen(text3_2)); md5_final(&ctx, buf); pass = pass && !memcmp(hash3, buf, MD5_BLOCK_SIZE); return(pass); } int main() { printf("MD5 tests: %s\n", md5_test() ? "SUCCEEDED" : "FAILED"); return(0); } olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/rot-13.c000066400000000000000000000023611311755073500233520ustar00rootroot00000000000000/********************************************************************* * Filename: rot-13.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Implementation of the ROT-13 encryption algorithm. Algorithm specification can be found here: * This implementation uses little endian byte order. *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include "rot-13.h" /*********************** FUNCTION DEFINITIONS ***********************/ void rot13(char str[]) { int case_type, idx, len; for (idx = 0, len = strlen(str); idx < len; idx++) { // Only process alphabetic characters. if (str[idx] < 'A' || (str[idx] > 'Z' && str[idx] < 'a') || str[idx] > 'z') continue; // Determine if the char is upper or lower case. if (str[idx] >= 'a') case_type = 'a'; else case_type = 'A'; // Rotate the char's value, ensuring it doesn't accidentally "fall off" the end. str[idx] = (str[idx] + 13) % (case_type + 26); if (str[idx] < 26) str[idx] += case_type; } } olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/rot-13.h000066400000000000000000000013711311755073500233570ustar00rootroot00000000000000/********************************************************************* * Filename: rot-13.h * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Defines the API for the corresponding ROT-13 implementation. *********************************************************************/ #ifndef ROT13_H #define ROT13_H /*************************** HEADER FILES ***************************/ #include /*********************** FUNCTION DECLARATIONS **********************/ // Performs IN PLACE rotation of the input. Assumes input is NULL terminated. // Preserves each charcter's case. Ignores non alphabetic characters. void rot13(char str[]); #endif // ROT13_H olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/rot-13_test.c000066400000000000000000000025451311755073500244150ustar00rootroot00000000000000/********************************************************************* * Filename: rot-13_test.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Performs known-answer tests on the corresponding ROT-13 implementation. These tests do not encompass the full range of available test vectors, however, if the tests pass it is very, very likely that the code is correct and was compiled properly. This code also serves as example usage of the functions. *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include #include "rot-13.h" /*********************** FUNCTION DEFINITIONS ***********************/ int rot13_test() { char text[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"}; char code[] = {"NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm"}; char buf[1024]; int pass = 1; // To encode, just apply ROT-13. strcpy(buf, text); rot13(buf); pass = pass && !strcmp(code, buf); // To decode, just re-apply ROT-13. rot13(buf); pass = pass && !strcmp(text, buf); return(pass); } int main() { printf("ROT-13 tests: %s\n", rot13_test() ? "SUCCEEDED" : "FAILED"); return(0); } olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/sha1.c000066400000000000000000000075451311755073500231720ustar00rootroot00000000000000/********************************************************************* * Filename: sha1.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Implementation of the SHA1 hashing algorithm. Algorithm specification can be found here: * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf This implementation uses little endian byte order. *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include #include "sha1.h" /****************************** MACROS ******************************/ #define ROTLEFT(a, b) ((a << b) | (a >> (32 - b))) /*********************** FUNCTION DEFINITIONS ***********************/ void sha1_transform(SHA1_CTX *ctx, const BYTE data[]) { WORD a, b, c, d, e, i, j, t, m[80]; for (i = 0, j = 0; i < 16; ++i, j += 4) m[i] = (data[j] << 24) + (data[j + 1] << 16) + (data[j + 2] << 8) + (data[j + 3]); for ( ; i < 80; ++i) { m[i] = (m[i - 3] ^ m[i - 8] ^ m[i - 14] ^ m[i - 16]); m[i] = (m[i] << 1) | (m[i] >> 31); } a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3]; e = ctx->state[4]; for (i = 0; i < 20; ++i) { t = ROTLEFT(a, 5) + ((b & c) ^ (~b & d)) + e + ctx->k[0] + m[i]; e = d; d = c; c = ROTLEFT(b, 30); b = a; a = t; } for ( ; i < 40; ++i) { t = ROTLEFT(a, 5) + (b ^ c ^ d) + e + ctx->k[1] + m[i]; e = d; d = c; c = ROTLEFT(b, 30); b = a; a = t; } for ( ; i < 60; ++i) { t = ROTLEFT(a, 5) + ((b & c) ^ (b & d) ^ (c & d)) + e + ctx->k[2] + m[i]; e = d; d = c; c = ROTLEFT(b, 30); b = a; a = t; } for ( ; i < 80; ++i) { t = ROTLEFT(a, 5) + (b ^ c ^ d) + e + ctx->k[3] + m[i]; e = d; d = c; c = ROTLEFT(b, 30); b = a; a = t; } ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d; ctx->state[4] += e; } void sha1_init(SHA1_CTX *ctx) { ctx->datalen = 0; ctx->bitlen = 0; ctx->state[0] = 0x67452301; ctx->state[1] = 0xEFCDAB89; ctx->state[2] = 0x98BADCFE; ctx->state[3] = 0x10325476; ctx->state[4] = 0xc3d2e1f0; ctx->k[0] = 0x5a827999; ctx->k[1] = 0x6ed9eba1; ctx->k[2] = 0x8f1bbcdc; ctx->k[3] = 0xca62c1d6; } void sha1_update(SHA1_CTX *ctx, const BYTE data[], size_t len) { size_t i; for (i = 0; i < len; ++i) { ctx->data[ctx->datalen] = data[i]; ctx->datalen++; if (ctx->datalen == 64) { sha1_transform(ctx, ctx->data); ctx->bitlen += 512; ctx->datalen = 0; } } } void sha1_final(SHA1_CTX *ctx, BYTE hash[]) { WORD i; i = ctx->datalen; // Pad whatever data is left in the buffer. if (ctx->datalen < 56) { ctx->data[i++] = 0x80; while (i < 56) ctx->data[i++] = 0x00; } else { ctx->data[i++] = 0x80; while (i < 64) ctx->data[i++] = 0x00; sha1_transform(ctx, ctx->data); memset(ctx->data, 0, 56); } // Append to the padding the total message's length in bits and transform. ctx->bitlen += ctx->datalen * 8; ctx->data[63] = ctx->bitlen; ctx->data[62] = ctx->bitlen >> 8; ctx->data[61] = ctx->bitlen >> 16; ctx->data[60] = ctx->bitlen >> 24; ctx->data[59] = ctx->bitlen >> 32; ctx->data[58] = ctx->bitlen >> 40; ctx->data[57] = ctx->bitlen >> 48; ctx->data[56] = ctx->bitlen >> 56; sha1_transform(ctx, ctx->data); // Since this implementation uses little endian byte ordering and MD uses big endian, // reverse all the bytes when copying the final state to the output hash. for (i = 0; i < 4; ++i) { hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; } } olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/sha1.h000066400000000000000000000022631311755073500231670ustar00rootroot00000000000000/********************************************************************* * Filename: sha1.h * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Defines the API for the corresponding SHA1 implementation. *********************************************************************/ #ifndef SHA1_H #define SHA1_H /*************************** HEADER FILES ***************************/ #include /****************************** MACROS ******************************/ #define SHA1_BLOCK_SIZE 20 // SHA1 outputs a 20 byte digest /**************************** DATA TYPES ****************************/ typedef unsigned char BYTE; // 8-bit byte typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines typedef struct { BYTE data[64]; WORD datalen; unsigned long long bitlen; WORD state[5]; WORD k[4]; } SHA1_CTX; /*********************** FUNCTION DECLARATIONS **********************/ void sha1_init(SHA1_CTX *ctx); void sha1_update(SHA1_CTX *ctx, const BYTE data[], size_t len); void sha1_final(SHA1_CTX *ctx, BYTE hash[]); #endif // SHA1_H olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/sha1_test.c000066400000000000000000000040431311755073500242170ustar00rootroot00000000000000/********************************************************************* * Filename: sha1_test.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Performs known-answer tests on the corresponding SHA1 implementation. These tests do not encompass the full range of available test vectors, however, if the tests pass it is very, very likely that the code is correct and was compiled properly. This code also serves as example usage of the functions. *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include #include #include "sha1.h" /*********************** FUNCTION DEFINITIONS ***********************/ int sha1_test() { BYTE text1[] = {"abc"}; BYTE text2[] = {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}; BYTE text3[] = {"aaaaaaaaaa"}; BYTE hash1[SHA1_BLOCK_SIZE] = {0xa9,0x99,0x3e,0x36,0x47,0x06,0x81,0x6a,0xba,0x3e,0x25,0x71,0x78,0x50,0xc2,0x6c,0x9c,0xd0,0xd8,0x9d}; BYTE hash2[SHA1_BLOCK_SIZE] = {0x84,0x98,0x3e,0x44,0x1c,0x3b,0xd2,0x6e,0xba,0xae,0x4a,0xa1,0xf9,0x51,0x29,0xe5,0xe5,0x46,0x70,0xf1}; BYTE hash3[SHA1_BLOCK_SIZE] = {0x34,0xaa,0x97,0x3c,0xd4,0xc4,0xda,0xa4,0xf6,0x1e,0xeb,0x2b,0xdb,0xad,0x27,0x31,0x65,0x34,0x01,0x6f}; BYTE buf[SHA1_BLOCK_SIZE]; int idx; SHA1_CTX ctx; int pass = 1; sha1_init(&ctx); sha1_update(&ctx, text1, strlen(text1)); sha1_final(&ctx, buf); pass = pass && !memcmp(hash1, buf, SHA1_BLOCK_SIZE); sha1_init(&ctx); sha1_update(&ctx, text2, strlen(text2)); sha1_final(&ctx, buf); pass = pass && !memcmp(hash2, buf, SHA1_BLOCK_SIZE); sha1_init(&ctx); for (idx = 0; idx < 100000; ++idx) sha1_update(&ctx, text3, strlen(text3)); sha1_final(&ctx, buf); pass = pass && !memcmp(hash3, buf, SHA1_BLOCK_SIZE); return(pass); } int main() { printf("SHA1 tests: %s\n", sha1_test() ? "SUCCEEDED" : "FAILED"); return(0); } olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/sha256.c000066400000000000000000000122431311755073500233350ustar00rootroot00000000000000/********************************************************************* * Filename: sha256.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Implementation of the SHA-256 hashing algorithm. SHA-256 is one of the three algorithms in the SHA2 specification. The others, SHA-384 and SHA-512, are not offered in this implementation. Algorithm specification can be found here: * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf This implementation uses little endian byte order. *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include #include #include "sha256.h" /****************************** MACROS ******************************/ #define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) #define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) #define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) #define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) #define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) #define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) #define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) #define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) /**************************** VARIABLES *****************************/ static const WORD k[64] = { 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 }; /*********************** FUNCTION DEFINITIONS ***********************/ void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) { WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; for (i = 0, j = 0; i < 16; ++i, j += 4) m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); for ( ; i < 64; ++i) m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3]; e = ctx->state[4]; f = ctx->state[5]; g = ctx->state[6]; h = ctx->state[7]; for (i = 0; i < 64; ++i) { t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i]; t2 = EP0(a) + MAJ(a,b,c); h = g; g = f; f = e; e = d + t1; d = c; c = b; b = a; a = t1 + t2; } ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d; ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h; } void sha256_init(SHA256_CTX *ctx) { ctx->datalen = 0; ctx->bitlen = 0; ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85; ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a; ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c; ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19; } void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) { WORD i; for (i = 0; i < len; ++i) { ctx->data[ctx->datalen] = data[i]; ctx->datalen++; if (ctx->datalen == 64) { sha256_transform(ctx, ctx->data); ctx->bitlen += 512; ctx->datalen = 0; } } } void sha256_final(SHA256_CTX *ctx, BYTE hash[]) { WORD i; i = ctx->datalen; // Pad whatever data is left in the buffer. if (ctx->datalen < 56) { ctx->data[i++] = 0x80; while (i < 56) ctx->data[i++] = 0x00; } else { ctx->data[i++] = 0x80; while (i < 64) ctx->data[i++] = 0x00; sha256_transform(ctx, ctx->data); memset(ctx->data, 0, 56); } // Append to the padding the total message's length in bits and transform. ctx->bitlen += ctx->datalen * 8; ctx->data[63] = ctx->bitlen; ctx->data[62] = ctx->bitlen >> 8; ctx->data[61] = ctx->bitlen >> 16; ctx->data[60] = ctx->bitlen >> 24; ctx->data[59] = ctx->bitlen >> 32; ctx->data[58] = ctx->bitlen >> 40; ctx->data[57] = ctx->bitlen >> 48; ctx->data[56] = ctx->bitlen >> 56; sha256_transform(ctx, ctx->data); // Since this implementation uses little endian byte ordering and SHA uses big endian, // reverse all the bytes when copying the final state to the output hash. for (i = 0; i < 4; ++i) { hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; } } olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/sha256.h000066400000000000000000000022771311755073500233500ustar00rootroot00000000000000/********************************************************************* * Filename: sha256.h * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Defines the API for the corresponding SHA1 implementation. *********************************************************************/ #ifndef SHA256_H #define SHA256_H /*************************** HEADER FILES ***************************/ #include /****************************** MACROS ******************************/ #define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest /**************************** DATA TYPES ****************************/ typedef unsigned char BYTE; // 8-bit byte typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines typedef struct { BYTE data[64]; WORD datalen; unsigned long long bitlen; WORD state[8]; } SHA256_CTX; /*********************** FUNCTION DECLARATIONS **********************/ void sha256_init(SHA256_CTX *ctx); void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len); void sha256_final(SHA256_CTX *ctx, BYTE hash[]); #endif // SHA256_H olm-2.2.2+git20170526.0fd768e+dfsg/lib/crypto-algorithms/sha256_test.c000066400000000000000000000045501311755073500243760ustar00rootroot00000000000000/********************************************************************* * Filename: sha256.c * Author: Brad Conte (brad AT bradconte.com) * Copyright: * Disclaimer: This code is presented "as is" without any guarantees. * Details: Performs known-answer tests on the corresponding SHA1 implementation. These tests do not encompass the full range of available test vectors, however, if the tests pass it is very, very likely that the code is correct and was compiled properly. This code also serves as example usage of the functions. *********************************************************************/ /*************************** HEADER FILES ***************************/ #include #include #include #include "sha256.h" /*********************** FUNCTION DEFINITIONS ***********************/ int sha256_test() { BYTE text1[] = {"abc"}; BYTE text2[] = {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}; BYTE text3[] = {"aaaaaaaaaa"}; BYTE hash1[SHA256_BLOCK_SIZE] = {0xba,0x78,0x16,0xbf,0x8f,0x01,0xcf,0xea,0x41,0x41,0x40,0xde,0x5d,0xae,0x22,0x23, 0xb0,0x03,0x61,0xa3,0x96,0x17,0x7a,0x9c,0xb4,0x10,0xff,0x61,0xf2,0x00,0x15,0xad}; BYTE hash2[SHA256_BLOCK_SIZE] = {0x24,0x8d,0x6a,0x61,0xd2,0x06,0x38,0xb8,0xe5,0xc0,0x26,0x93,0x0c,0x3e,0x60,0x39, 0xa3,0x3c,0xe4,0x59,0x64,0xff,0x21,0x67,0xf6,0xec,0xed,0xd4,0x19,0xdb,0x06,0xc1}; BYTE hash3[SHA256_BLOCK_SIZE] = {0xcd,0xc7,0x6e,0x5c,0x99,0x14,0xfb,0x92,0x81,0xa1,0xc7,0xe2,0x84,0xd7,0x3e,0x67, 0xf1,0x80,0x9a,0x48,0xa4,0x97,0x20,0x0e,0x04,0x6d,0x39,0xcc,0xc7,0x11,0x2c,0xd0}; BYTE buf[SHA256_BLOCK_SIZE]; SHA256_CTX ctx; int idx; int pass = 1; sha256_init(&ctx); sha256_update(&ctx, text1, strlen(text1)); sha256_final(&ctx, buf); pass = pass && !memcmp(hash1, buf, SHA256_BLOCK_SIZE); sha256_init(&ctx); sha256_update(&ctx, text2, strlen(text2)); sha256_final(&ctx, buf); pass = pass && !memcmp(hash2, buf, SHA256_BLOCK_SIZE); sha256_init(&ctx); for (idx = 0; idx < 100000; ++idx) sha256_update(&ctx, text3, strlen(text3)); sha256_final(&ctx, buf); pass = pass && !memcmp(hash3, buf, SHA256_BLOCK_SIZE); return(pass); } int main() { printf("SHA-256 tests: %s\n", sha256_test() ? "SUCCEEDED" : "FAILEd"); return(0); } olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna.h000066400000000000000000000005711311755073500214730ustar00rootroot00000000000000/* header file for the curve25519-donna implementation, because the * authors of that project don't supply one. */ #ifndef CURVE25519_DONNA_H #define CURVE25519_DONNA_H #ifdef __cplusplus extern "C" { #endif extern int curve25519_donna(unsigned char *output, const unsigned char *a, const unsigned char *b); #ifdef __cplusplus } #endif #endif olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/000077500000000000000000000000001311755073500213175ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/.gitignore000066400000000000000000000003211311755073500233030ustar00rootroot00000000000000/curve25519-donna-c64.a /curve25519-donna.a /test-curve25519-donna /speed-curve25519-donna /test-curve25519-donna-c64 /speed-curve25519-donna-c64 /test-sc-curve25519-donna-c64 /build *.o *.pyc /dist /MANIFEST olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/LICENSE.md000066400000000000000000000040531311755073500227250ustar00rootroot00000000000000Copyright 2008, Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. curve25519-donna: Curve25519 elliptic curve, public key function http://code.google.com/p/curve25519-donna/ Adam Langley Derived from public domain C code by Daniel J. Bernstein More information about curve25519 can be found here http://cr.yp.to/ecdh.html djb's sample implementation of curve25519 is written in a special assembly language called qhasm and uses the floating point registers. This is, almost, a clean room reimplementation from the curve25519 paper. It uses many of the tricks described therein. Only the crecip function is taken from the sample implementation. olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/Makefile000066400000000000000000000043661311755073500227700ustar00rootroot00000000000000CFLAGS=-Wmissing-prototypes -Wdeclaration-after-statement -O2 -Wall CFLAGS_32=-m32 targets: curve25519-donna.a curve25519-donna-c64.a test: test-donna test-donna-c64 clean: rm -f *.o *.a *.pp test-curve25519-donna test-curve25519-donna-c64 speed-curve25519-donna speed-curve25519-donna-c64 test-noncanon-curve25519-donna test-noncanon-curve25519-donna-c64 curve25519-donna.a: curve25519-donna.o ar -rc curve25519-donna.a curve25519-donna.o ranlib curve25519-donna.a curve25519-donna.o: curve25519-donna.c gcc -c curve25519-donna.c $(CFLAGS) $(CFLAGS_32) curve25519-donna-c64.a: curve25519-donna-c64.o ar -rc curve25519-donna-c64.a curve25519-donna-c64.o ranlib curve25519-donna-c64.a curve25519-donna-c64.o: curve25519-donna-c64.c gcc -c curve25519-donna-c64.c $(CFLAGS) test-donna: test-curve25519-donna ./test-curve25519-donna | head -123456 | tail -1 test-donna-c64: test-curve25519-donna-c64 ./test-curve25519-donna-c64 | head -123456 | tail -1 test-curve25519-donna: test-curve25519.c curve25519-donna.a gcc -o test-curve25519-donna test-curve25519.c curve25519-donna.a $(CFLAGS) $(CFLAGS_32) test-curve25519-donna-c64: test-curve25519.c curve25519-donna-c64.a gcc -o test-curve25519-donna-c64 test-curve25519.c curve25519-donna-c64.a $(CFLAGS) speed-curve25519-donna: speed-curve25519.c curve25519-donna.a gcc -o speed-curve25519-donna speed-curve25519.c curve25519-donna.a $(CFLAGS) $(CFLAGS_32) speed-curve25519-donna-c64: speed-curve25519.c curve25519-donna-c64.a gcc -o speed-curve25519-donna-c64 speed-curve25519.c curve25519-donna-c64.a $(CFLAGS) test-sc-curve25519-donna-c64: test-sc-curve25519.c curve25519-donna-c64.a gcc -o test-sc-curve25519-donna-c64 -O test-sc-curve25519.c curve25519-donna-c64.a test-sc-curve25519.s $(CFLAGS) test-noncanon-donna: test-noncanon-curve25519-donna ./test-noncanon-curve25519-donna test-noncanon-donna-c64: test-noncanon-curve25519-donna-c64 ./test-noncanon-curve25519-donna-c64 test-noncanon-curve25519-donna: test-noncanon.c curve25519-donna.a gcc -o test-noncanon-curve25519-donna test-noncanon.c curve25519-donna.a $(CFLAGS) $(CFLAGS_32) test-noncanon-curve25519-donna-c64: test-noncanon.c curve25519-donna-c64.a gcc -o test-noncanon-curve25519-donna-c64 test-noncanon.c curve25519-donna-c64.a $(CFLAGS) olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/README000066400000000000000000000023451311755073500222030ustar00rootroot00000000000000See http://code.google.com/p/curve25519-donna/ for details. BUILDING: If you run `make`, two .a archives will be built, similar to djb's curve25519 code. Alternatively, read on: The C implementation is contained within curve25519-donna.c. It has no external dependancies and is BSD licenced. You can copy/include/link it directly in with your program. Recommended C flags: -O2 The x86-64 bit implementation is contained within curve25519-donna-x86-64.c and curve25519-donna-x86-64.s. Build like this: % cpp curve25519-donna-x86-64.s > curve25519-donna-x86-64.s.pp % as -o curve25519-donna-x86-64.s.o curve25519-donna-x86-64.s.pp % gcc -O2 -c curve25519-donna-x86-64.c Then the two .o files can be linked in USAGE: The usage is exactly the same as djb's code (as described at http://cr.yp.to/ecdh.html) expect that the function is called curve25519_donna. In short, To generate a private key just generate 32 random bytes. To generate the public key, just do: static const uint8_t basepoint[32] = {9}; curve25519_donna(mypublic, mysecret, basepoint); To generate an agreed key do: uint8_t shared_key[32]; curve25519_donna(shared_key, mysecret, theirpublic); And hash the shared_key with a cryptographic hash function before using. olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/contrib/000077500000000000000000000000001311755073500227575ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/contrib/Curve25519Donna.c000066400000000000000000000061451311755073500256030ustar00rootroot00000000000000/* James Robson Public domain. */ #include "Curve25519Donna.h" #include #include extern void curve25519_donna(unsigned char *output, const unsigned char *a, const unsigned char *b); unsigned char* as_unsigned_char_array(JNIEnv* env, jbyteArray array, int* len); jbyteArray as_byte_array(JNIEnv* env, unsigned char* buf, int len); jbyteArray as_byte_array(JNIEnv* env, unsigned char* buf, int len) { jbyteArray array = (*env)->NewByteArray(env, len); (*env)->SetByteArrayRegion(env, array, 0, len, (jbyte*)buf); //int i; //for (i = 0;i < len;++i) printf("%02x",(unsigned int) buf[i]); printf(" "); //printf("\n"); return array; } unsigned char* as_unsigned_char_array(JNIEnv* env, jbyteArray array, int* len) { *len = (*env)->GetArrayLength(env, array); unsigned char* buf = (unsigned char*)calloc(*len+1, sizeof(char)); (*env)->GetByteArrayRegion (env, array, 0, *len, (jbyte*)buf); return buf; } JNIEXPORT jbyteArray JNICALL Java_Curve25519Donna_curve25519Donna (JNIEnv *env, jobject obj, jbyteArray a, jbyteArray b) { unsigned char o[32] = {0}; int l1, l2; unsigned char* a1 = as_unsigned_char_array(env, a, &l1); unsigned char* b1 = as_unsigned_char_array(env, b, &l2); if ( !(l1 == 32 && l2 == 32) ) { fprintf(stderr, "Error, must be length 32"); return NULL; } curve25519_donna(o, (const unsigned char*)a1, (const unsigned char*)b1); free(a1); free(b1); return as_byte_array(env, (unsigned char*)o, 32); } JNIEXPORT jbyteArray JNICALL Java_Curve25519Donna_makePrivate (JNIEnv *env, jobject obj, jbyteArray secret) { int len; unsigned char* k = as_unsigned_char_array(env, secret, &len); if (len != 32) { fprintf(stderr, "Error, must be length 32"); return NULL; } k[0] &= 248; k[31] &= 127; k[31] |= 64; return as_byte_array(env, k, 32); } JNIEXPORT jbyteArray JNICALL Java_Curve25519Donna_getPublic (JNIEnv *env, jobject obj, jbyteArray privkey) { int len; unsigned char* private = as_unsigned_char_array(env, privkey, &len); if (len != 32) { fprintf(stderr, "Error, must be length 32"); return NULL; } unsigned char pubkey[32]; unsigned char basepoint[32] = {9}; curve25519_donna(pubkey, private, basepoint); return as_byte_array(env, (unsigned char*)pubkey, 32); } JNIEXPORT jbyteArray JNICALL Java_Curve25519Donna_makeSharedSecret (JNIEnv *env, jobject obj, jbyteArray privkey, jbyteArray their_pubkey) { unsigned char shared_secret[32]; int l1, l2; unsigned char* private = as_unsigned_char_array(env, privkey, &l1); unsigned char* pubkey = as_unsigned_char_array(env, their_pubkey, &l2); if ( !(l1 == 32 && l2 == 32) ) { fprintf(stderr, "Error, must be length 32"); return NULL; } curve25519_donna(shared_secret, private, pubkey); return as_byte_array(env, (unsigned char*)shared_secret, 32); } JNIEXPORT void JNICALL Java_Curve25519Donna_helowrld (JNIEnv *env, jobject obj) { printf("helowrld\n"); } olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/contrib/Curve25519Donna.h000066400000000000000000000022321311755073500256010ustar00rootroot00000000000000/* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class Curve25519Donna */ #ifndef _Included_Curve25519Donna #define _Included_Curve25519Donna #ifdef __cplusplus extern "C" { #endif /* * Class: Curve25519Donna * Method: curve25519Donna * Signature: ([B[B)[B */ JNIEXPORT jbyteArray JNICALL Java_Curve25519Donna_curve25519Donna (JNIEnv *, jobject, jbyteArray, jbyteArray); /* * Class: Curve25519Donna * Method: makePrivate * Signature: ([B)[B */ JNIEXPORT jbyteArray JNICALL Java_Curve25519Donna_makePrivate (JNIEnv *, jobject, jbyteArray); /* * Class: Curve25519Donna * Method: getPublic * Signature: ([B)[B */ JNIEXPORT jbyteArray JNICALL Java_Curve25519Donna_getPublic (JNIEnv *, jobject, jbyteArray); /* * Class: Curve25519Donna * Method: makeSharedSecret * Signature: ([B[B)[B */ JNIEXPORT jbyteArray JNICALL Java_Curve25519Donna_makeSharedSecret (JNIEnv *, jobject, jbyteArray, jbyteArray); /* * Class: Curve25519Donna * Method: helowrld * Signature: ()V */ JNIEXPORT void JNICALL Java_Curve25519Donna_helowrld (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/contrib/Curve25519Donna.java000066400000000000000000000050471311755073500263020ustar00rootroot00000000000000/* James Robson Public domain. */ public class Curve25519Donna { final protected static char[] hexArray = "0123456789ABCDEF".toCharArray(); public static String bytesToHex(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; int v; for ( int j = 0; j < bytes.length; j++ ) { v = bytes[j] & 0xFF; hexChars[j * 2] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } return new String(hexChars); } public native byte[] curve25519Donna(byte[] a, byte[] b); public native byte[] makePrivate(byte[] secret); public native byte[] getPublic(byte[] privkey); public native byte[] makeSharedSecret(byte[] privkey, byte[] theirPubKey); public native void helowrld(); // Uncomment if your Java is 32-bit: //static { System.loadLibrary("Curve25519Donna"); } // Otherwise, load this 64-bit .jnilib: static { System.loadLibrary("Curve25519Donna_64"); } /* To give the old tires a kick (OSX): java -cp `pwd` Curve25519Donna */ public static void main (String[] args) { Curve25519Donna c = new Curve25519Donna(); // These should be 32 bytes long byte[] user1Secret = "abcdefghijklmnopqrstuvwxyz123456".getBytes(); byte[] user2Secret = "654321zyxwvutsrqponmlkjihgfedcba".getBytes(); // You can use the curve function directly... //byte[] o = c.curve25519Donna(a, b); //System.out.println("o = " + bytesToHex(o)); // ... but it's not really necessary. Just use the following // convenience methods: byte[] privKey = c.makePrivate(user1Secret); byte[] pubKey = c.getPublic(privKey); byte[] privKey2 = c.makePrivate(user2Secret); byte[] pubKey2 = c.getPublic(privKey2); System.out.println("'user1' privKey = " + bytesToHex(privKey)); System.out.println("'user1' pubKey = " + bytesToHex(pubKey)); System.out.println("==================================================="); System.out.println("'user2' privKey = " + bytesToHex(privKey2)); System.out.println("'user2' pubKey = " + bytesToHex(pubKey2)); System.out.println("==================================================="); byte[] ss1 = c.makeSharedSecret(privKey, pubKey2); System.out.println("'user1' computes shared secret: " + bytesToHex(ss1)); byte[] ss2 = c.makeSharedSecret(privKey2, pubKey); System.out.println("'user2' computes shared secret: " + bytesToHex(ss2)); } } olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/contrib/make-snippets000066400000000000000000000053171311755073500254700ustar00rootroot00000000000000CFLAGS=-Wmissing-prototypes -Wdeclaration-after-statement -O2 -Wall CC=clang targets: curve25519-donna.a curve25519-donna-c64.a test: test-donna test-donna-c64 clean: rm -f java-src/*.class java-src/*.jnilib *.dylib *.o *.a *.pp test-curve25519-donna test-curve25519-donna-c64 speed-curve25519-donna speed-curve25519-donna-c64 curve25519-donna.a: curve25519-donna.o ar -rc curve25519-donna.a curve25519-donna.o ranlib curve25519-donna.a ##### OSX dynamic library (32- & 64-bit) curve25519donna.dylib: curve25519-donna.a curve25519-donna-c64.a $(CC) -m32 -fpic -shared -Wl,-all_load curve25519-donna.a -Wl,-all_load -o libcurve25519donna.dylib $(CC) -fpic -shared -Wl,-all_load curve25519-donna-c64.a -Wl,-all_load -o libcurve25519donna_64.dylib ##### OSX/Java section hence # Java JNI - compiled for OSX (32- & 64-bit) Curve25519Donna.class: cd java-src; javah -jni Curve25519Donna; cd .. cd java-src; javac Curve25519Donna.java; cd .. Curve25519Donna.jnilib: curve25519-donna.a curve25519-donna-c64.a Curve25519Donna.class @echo "Building 32-bit..." clang -o java-src/libCurve25519Donna.jnilib $(CFLAGS) -lc -shared -m32 -I /System/Library/Frameworks/JavaVM.framework/Headers curve25519-donna.o java-src/Curve25519Donna.c @echo "Building 64-bit..." clang -o java-src/libCurve25519Donna_64.jnilib $(CFLAGS) -lc -shared -I /System/Library/Frameworks/JavaVM.framework/Headers curve25519-donna-c64.o java-src/Curve25519Donna.c ##### OSX/Java section end curve25519-donna.o: curve25519-donna.c $(CC) -c curve25519-donna.c $(CFLAGS) -m32 curve25519-donna-c64.a: curve25519-donna-c64.o ar -rc curve25519-donna-c64.a curve25519-donna-c64.o ranlib curve25519-donna-c64.a curve25519-donna-c64.o: curve25519-donna-c64.c $(CC) -c curve25519-donna-c64.c $(CFLAGS) test-donna: test-curve25519-donna ./test-curve25519-donna | head -123456 | tail -1 test-donna-c64: test-curve25519-donna-c64 ./test-curve25519-donna-c64 | head -123456 | tail -1 test-curve25519-donna: test-curve25519.c curve25519-donna.a $(CC) -o test-curve25519-donna test-curve25519.c curve25519-donna.a $(CFLAGS) -m32 test-curve25519-donna-c64: test-curve25519.c curve25519-donna-c64.a $(CC) -o test-curve25519-donna-c64 test-curve25519.c curve25519-donna-c64.a $(CFLAGS) speed-curve25519-donna: speed-curve25519.c curve25519-donna.a $(CC) -o speed-curve25519-donna speed-curve25519.c curve25519-donna.a $(CFLAGS) -m32 speed-curve25519-donna-c64: speed-curve25519.c curve25519-donna-c64.a $(CC) -o speed-curve25519-donna-c64 speed-curve25519.c curve25519-donna-c64.a $(CFLAGS) test-sc-curve25519-donna-c64: test-sc-curve25519.c curve25519-donna-c64.a $(CC) -o test-sc-curve25519-donna-c64 -O test-sc-curve25519.c curve25519-donna-c64.a test-sc-curve25519.s $(CFLAGS) olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/curve25519-donna-c64.c000066400000000000000000000323431311755073500247110ustar00rootroot00000000000000/* Copyright 2008, Google Inc. * All rights reserved. * * Code released into the public domain. * * curve25519-donna: Curve25519 elliptic curve, public key function * * http://code.google.com/p/curve25519-donna/ * * Adam Langley * * Derived from public domain C code by Daniel J. Bernstein * * More information about curve25519 can be found here * http://cr.yp.to/ecdh.html * * djb's sample implementation of curve25519 is written in a special assembly * language called qhasm and uses the floating point registers. * * This is, almost, a clean room reimplementation from the curve25519 paper. It * uses many of the tricks described therein. Only the crecip function is taken * from the sample implementation. */ #include #include typedef uint8_t u8; typedef uint64_t limb; typedef limb felem[5]; // This is a special gcc mode for 128-bit integers. It's implemented on 64-bit // platforms only as far as I know. typedef unsigned uint128_t __attribute__((mode(TI))); #undef force_inline #define force_inline __attribute__((always_inline)) /* Sum two numbers: output += in */ static inline void force_inline fsum(limb *output, const limb *in) { output[0] += in[0]; output[1] += in[1]; output[2] += in[2]; output[3] += in[3]; output[4] += in[4]; } /* Find the difference of two numbers: output = in - output * (note the order of the arguments!) * * Assumes that out[i] < 2**52 * On return, out[i] < 2**55 */ static inline void force_inline fdifference_backwards(felem out, const felem in) { /* 152 is 19 << 3 */ static const limb two54m152 = (((limb)1) << 54) - 152; static const limb two54m8 = (((limb)1) << 54) - 8; out[0] = in[0] + two54m152 - out[0]; out[1] = in[1] + two54m8 - out[1]; out[2] = in[2] + two54m8 - out[2]; out[3] = in[3] + two54m8 - out[3]; out[4] = in[4] + two54m8 - out[4]; } /* Multiply a number by a scalar: output = in * scalar */ static inline void force_inline fscalar_product(felem output, const felem in, const limb scalar) { uint128_t a; a = ((uint128_t) in[0]) * scalar; output[0] = ((limb)a) & 0x7ffffffffffff; a = ((uint128_t) in[1]) * scalar + ((limb) (a >> 51)); output[1] = ((limb)a) & 0x7ffffffffffff; a = ((uint128_t) in[2]) * scalar + ((limb) (a >> 51)); output[2] = ((limb)a) & 0x7ffffffffffff; a = ((uint128_t) in[3]) * scalar + ((limb) (a >> 51)); output[3] = ((limb)a) & 0x7ffffffffffff; a = ((uint128_t) in[4]) * scalar + ((limb) (a >> 51)); output[4] = ((limb)a) & 0x7ffffffffffff; output[0] += (a >> 51) * 19; } /* Multiply two numbers: output = in2 * in * * output must be distinct to both inputs. The inputs are reduced coefficient * form, the output is not. * * Assumes that in[i] < 2**55 and likewise for in2. * On return, output[i] < 2**52 */ static inline void force_inline fmul(felem output, const felem in2, const felem in) { uint128_t t[5]; limb r0,r1,r2,r3,r4,s0,s1,s2,s3,s4,c; r0 = in[0]; r1 = in[1]; r2 = in[2]; r3 = in[3]; r4 = in[4]; s0 = in2[0]; s1 = in2[1]; s2 = in2[2]; s3 = in2[3]; s4 = in2[4]; t[0] = ((uint128_t) r0) * s0; t[1] = ((uint128_t) r0) * s1 + ((uint128_t) r1) * s0; t[2] = ((uint128_t) r0) * s2 + ((uint128_t) r2) * s0 + ((uint128_t) r1) * s1; t[3] = ((uint128_t) r0) * s3 + ((uint128_t) r3) * s0 + ((uint128_t) r1) * s2 + ((uint128_t) r2) * s1; t[4] = ((uint128_t) r0) * s4 + ((uint128_t) r4) * s0 + ((uint128_t) r3) * s1 + ((uint128_t) r1) * s3 + ((uint128_t) r2) * s2; r4 *= 19; r1 *= 19; r2 *= 19; r3 *= 19; t[0] += ((uint128_t) r4) * s1 + ((uint128_t) r1) * s4 + ((uint128_t) r2) * s3 + ((uint128_t) r3) * s2; t[1] += ((uint128_t) r4) * s2 + ((uint128_t) r2) * s4 + ((uint128_t) r3) * s3; t[2] += ((uint128_t) r4) * s3 + ((uint128_t) r3) * s4; t[3] += ((uint128_t) r4) * s4; r0 = (limb)t[0] & 0x7ffffffffffff; c = (limb)(t[0] >> 51); t[1] += c; r1 = (limb)t[1] & 0x7ffffffffffff; c = (limb)(t[1] >> 51); t[2] += c; r2 = (limb)t[2] & 0x7ffffffffffff; c = (limb)(t[2] >> 51); t[3] += c; r3 = (limb)t[3] & 0x7ffffffffffff; c = (limb)(t[3] >> 51); t[4] += c; r4 = (limb)t[4] & 0x7ffffffffffff; c = (limb)(t[4] >> 51); r0 += c * 19; c = r0 >> 51; r0 = r0 & 0x7ffffffffffff; r1 += c; c = r1 >> 51; r1 = r1 & 0x7ffffffffffff; r2 += c; output[0] = r0; output[1] = r1; output[2] = r2; output[3] = r3; output[4] = r4; } static inline void force_inline fsquare_times(felem output, const felem in, limb count) { uint128_t t[5]; limb r0,r1,r2,r3,r4,c; limb d0,d1,d2,d4,d419; r0 = in[0]; r1 = in[1]; r2 = in[2]; r3 = in[3]; r4 = in[4]; do { d0 = r0 * 2; d1 = r1 * 2; d2 = r2 * 2 * 19; d419 = r4 * 19; d4 = d419 * 2; t[0] = ((uint128_t) r0) * r0 + ((uint128_t) d4) * r1 + (((uint128_t) d2) * (r3 )); t[1] = ((uint128_t) d0) * r1 + ((uint128_t) d4) * r2 + (((uint128_t) r3) * (r3 * 19)); t[2] = ((uint128_t) d0) * r2 + ((uint128_t) r1) * r1 + (((uint128_t) d4) * (r3 )); t[3] = ((uint128_t) d0) * r3 + ((uint128_t) d1) * r2 + (((uint128_t) r4) * (d419 )); t[4] = ((uint128_t) d0) * r4 + ((uint128_t) d1) * r3 + (((uint128_t) r2) * (r2 )); r0 = (limb)t[0] & 0x7ffffffffffff; c = (limb)(t[0] >> 51); t[1] += c; r1 = (limb)t[1] & 0x7ffffffffffff; c = (limb)(t[1] >> 51); t[2] += c; r2 = (limb)t[2] & 0x7ffffffffffff; c = (limb)(t[2] >> 51); t[3] += c; r3 = (limb)t[3] & 0x7ffffffffffff; c = (limb)(t[3] >> 51); t[4] += c; r4 = (limb)t[4] & 0x7ffffffffffff; c = (limb)(t[4] >> 51); r0 += c * 19; c = r0 >> 51; r0 = r0 & 0x7ffffffffffff; r1 += c; c = r1 >> 51; r1 = r1 & 0x7ffffffffffff; r2 += c; } while(--count); output[0] = r0; output[1] = r1; output[2] = r2; output[3] = r3; output[4] = r4; } /* Load a little-endian 64-bit number */ static limb load_limb(const u8 *in) { return ((limb)in[0]) | (((limb)in[1]) << 8) | (((limb)in[2]) << 16) | (((limb)in[3]) << 24) | (((limb)in[4]) << 32) | (((limb)in[5]) << 40) | (((limb)in[6]) << 48) | (((limb)in[7]) << 56); } static void store_limb(u8 *out, limb in) { out[0] = in & 0xff; out[1] = (in >> 8) & 0xff; out[2] = (in >> 16) & 0xff; out[3] = (in >> 24) & 0xff; out[4] = (in >> 32) & 0xff; out[5] = (in >> 40) & 0xff; out[6] = (in >> 48) & 0xff; out[7] = (in >> 56) & 0xff; } /* Take a little-endian, 32-byte number and expand it into polynomial form */ static void fexpand(limb *output, const u8 *in) { output[0] = load_limb(in) & 0x7ffffffffffff; output[1] = (load_limb(in+6) >> 3) & 0x7ffffffffffff; output[2] = (load_limb(in+12) >> 6) & 0x7ffffffffffff; output[3] = (load_limb(in+19) >> 1) & 0x7ffffffffffff; output[4] = (load_limb(in+24) >> 12) & 0x7ffffffffffff; } /* Take a fully reduced polynomial form number and contract it into a * little-endian, 32-byte array */ static void fcontract(u8 *output, const felem input) { uint128_t t[5]; t[0] = input[0]; t[1] = input[1]; t[2] = input[2]; t[3] = input[3]; t[4] = input[4]; t[1] += t[0] >> 51; t[0] &= 0x7ffffffffffff; t[2] += t[1] >> 51; t[1] &= 0x7ffffffffffff; t[3] += t[2] >> 51; t[2] &= 0x7ffffffffffff; t[4] += t[3] >> 51; t[3] &= 0x7ffffffffffff; t[0] += 19 * (t[4] >> 51); t[4] &= 0x7ffffffffffff; t[1] += t[0] >> 51; t[0] &= 0x7ffffffffffff; t[2] += t[1] >> 51; t[1] &= 0x7ffffffffffff; t[3] += t[2] >> 51; t[2] &= 0x7ffffffffffff; t[4] += t[3] >> 51; t[3] &= 0x7ffffffffffff; t[0] += 19 * (t[4] >> 51); t[4] &= 0x7ffffffffffff; /* now t is between 0 and 2^255-1, properly carried. */ /* case 1: between 0 and 2^255-20. case 2: between 2^255-19 and 2^255-1. */ t[0] += 19; t[1] += t[0] >> 51; t[0] &= 0x7ffffffffffff; t[2] += t[1] >> 51; t[1] &= 0x7ffffffffffff; t[3] += t[2] >> 51; t[2] &= 0x7ffffffffffff; t[4] += t[3] >> 51; t[3] &= 0x7ffffffffffff; t[0] += 19 * (t[4] >> 51); t[4] &= 0x7ffffffffffff; /* now between 19 and 2^255-1 in both cases, and offset by 19. */ t[0] += 0x8000000000000 - 19; t[1] += 0x8000000000000 - 1; t[2] += 0x8000000000000 - 1; t[3] += 0x8000000000000 - 1; t[4] += 0x8000000000000 - 1; /* now between 2^255 and 2^256-20, and offset by 2^255. */ t[1] += t[0] >> 51; t[0] &= 0x7ffffffffffff; t[2] += t[1] >> 51; t[1] &= 0x7ffffffffffff; t[3] += t[2] >> 51; t[2] &= 0x7ffffffffffff; t[4] += t[3] >> 51; t[3] &= 0x7ffffffffffff; t[4] &= 0x7ffffffffffff; store_limb(output, t[0] | (t[1] << 51)); store_limb(output+8, (t[1] >> 13) | (t[2] << 38)); store_limb(output+16, (t[2] >> 26) | (t[3] << 25)); store_limb(output+24, (t[3] >> 39) | (t[4] << 12)); } /* Input: Q, Q', Q-Q' * Output: 2Q, Q+Q' * * x2 z3: long form * x3 z3: long form * x z: short form, destroyed * xprime zprime: short form, destroyed * qmqp: short form, preserved */ static void fmonty(limb *x2, limb *z2, /* output 2Q */ limb *x3, limb *z3, /* output Q + Q' */ limb *x, limb *z, /* input Q */ limb *xprime, limb *zprime, /* input Q' */ const limb *qmqp /* input Q - Q' */) { limb origx[5], origxprime[5], zzz[5], xx[5], zz[5], xxprime[5], zzprime[5], zzzprime[5]; memcpy(origx, x, 5 * sizeof(limb)); fsum(x, z); fdifference_backwards(z, origx); // does x - z memcpy(origxprime, xprime, sizeof(limb) * 5); fsum(xprime, zprime); fdifference_backwards(zprime, origxprime); fmul(xxprime, xprime, z); fmul(zzprime, x, zprime); memcpy(origxprime, xxprime, sizeof(limb) * 5); fsum(xxprime, zzprime); fdifference_backwards(zzprime, origxprime); fsquare_times(x3, xxprime, 1); fsquare_times(zzzprime, zzprime, 1); fmul(z3, zzzprime, qmqp); fsquare_times(xx, x, 1); fsquare_times(zz, z, 1); fmul(x2, xx, zz); fdifference_backwards(zz, xx); // does zz = xx - zz fscalar_product(zzz, zz, 121665); fsum(zzz, xx); fmul(z2, zz, zzz); } // ----------------------------------------------------------------------------- // Maybe swap the contents of two limb arrays (@a and @b), each @len elements // long. Perform the swap iff @swap is non-zero. // // This function performs the swap without leaking any side-channel // information. // ----------------------------------------------------------------------------- static void swap_conditional(limb a[5], limb b[5], limb iswap) { unsigned i; const limb swap = -iswap; for (i = 0; i < 5; ++i) { const limb x = swap & (a[i] ^ b[i]); a[i] ^= x; b[i] ^= x; } } /* Calculates nQ where Q is the x-coordinate of a point on the curve * * resultx/resultz: the x coordinate of the resulting curve point (short form) * n: a little endian, 32-byte number * q: a point of the curve (short form) */ static void cmult(limb *resultx, limb *resultz, const u8 *n, const limb *q) { limb a[5] = {0}, b[5] = {1}, c[5] = {1}, d[5] = {0}; limb *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t; limb e[5] = {0}, f[5] = {1}, g[5] = {0}, h[5] = {1}; limb *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h; unsigned i, j; memcpy(nqpqx, q, sizeof(limb) * 5); for (i = 0; i < 32; ++i) { u8 byte = n[31 - i]; for (j = 0; j < 8; ++j) { const limb bit = byte >> 7; swap_conditional(nqx, nqpqx, bit); swap_conditional(nqz, nqpqz, bit); fmonty(nqx2, nqz2, nqpqx2, nqpqz2, nqx, nqz, nqpqx, nqpqz, q); swap_conditional(nqx2, nqpqx2, bit); swap_conditional(nqz2, nqpqz2, bit); t = nqx; nqx = nqx2; nqx2 = t; t = nqz; nqz = nqz2; nqz2 = t; t = nqpqx; nqpqx = nqpqx2; nqpqx2 = t; t = nqpqz; nqpqz = nqpqz2; nqpqz2 = t; byte <<= 1; } } memcpy(resultx, nqx, sizeof(limb) * 5); memcpy(resultz, nqz, sizeof(limb) * 5); } // ----------------------------------------------------------------------------- // Shamelessly copied from djb's code, tightened a little // ----------------------------------------------------------------------------- static void crecip(felem out, const felem z) { felem a,t0,b,c; /* 2 */ fsquare_times(a, z, 1); // a = 2 /* 8 */ fsquare_times(t0, a, 2); /* 9 */ fmul(b, t0, z); // b = 9 /* 11 */ fmul(a, b, a); // a = 11 /* 22 */ fsquare_times(t0, a, 1); /* 2^5 - 2^0 = 31 */ fmul(b, t0, b); /* 2^10 - 2^5 */ fsquare_times(t0, b, 5); /* 2^10 - 2^0 */ fmul(b, t0, b); /* 2^20 - 2^10 */ fsquare_times(t0, b, 10); /* 2^20 - 2^0 */ fmul(c, t0, b); /* 2^40 - 2^20 */ fsquare_times(t0, c, 20); /* 2^40 - 2^0 */ fmul(t0, t0, c); /* 2^50 - 2^10 */ fsquare_times(t0, t0, 10); /* 2^50 - 2^0 */ fmul(b, t0, b); /* 2^100 - 2^50 */ fsquare_times(t0, b, 50); /* 2^100 - 2^0 */ fmul(c, t0, b); /* 2^200 - 2^100 */ fsquare_times(t0, c, 100); /* 2^200 - 2^0 */ fmul(t0, t0, c); /* 2^250 - 2^50 */ fsquare_times(t0, t0, 50); /* 2^250 - 2^0 */ fmul(t0, t0, b); /* 2^255 - 2^5 */ fsquare_times(t0, t0, 5); /* 2^255 - 21 */ fmul(out, t0, a); } int curve25519_donna(u8 *, const u8 *, const u8 *); int curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) { limb bp[5], x[5], z[5], zmone[5]; uint8_t e[32]; int i; for (i = 0;i < 32;++i) e[i] = secret[i]; e[0] &= 248; e[31] &= 127; e[31] |= 64; fexpand(bp, basepoint); cmult(x, z, e, bp); crecip(zmone, z); fmul(z, x, zmone); fcontract(mypublic, z); return 0; } olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/curve25519-donna.c000066400000000000000000000757231311755073500243300ustar00rootroot00000000000000/* Copyright 2008, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * curve25519-donna: Curve25519 elliptic curve, public key function * * http://code.google.com/p/curve25519-donna/ * * Adam Langley * * Derived from public domain C code by Daniel J. Bernstein * * More information about curve25519 can be found here * http://cr.yp.to/ecdh.html * * djb's sample implementation of curve25519 is written in a special assembly * language called qhasm and uses the floating point registers. * * This is, almost, a clean room reimplementation from the curve25519 paper. It * uses many of the tricks described therein. Only the crecip function is taken * from the sample implementation. */ #include #include #ifdef _MSC_VER #define inline __inline #endif typedef uint8_t u8; typedef int32_t s32; typedef int64_t limb; /* Field element representation: * * Field elements are written as an array of signed, 64-bit limbs, least * significant first. The value of the field element is: * x[0] + 2^26·x[1] + x^51·x[2] + 2^102·x[3] + ... * * i.e. the limbs are 26, 25, 26, 25, ... bits wide. */ /* Sum two numbers: output += in */ static void fsum(limb *output, const limb *in) { unsigned i; for (i = 0; i < 10; i += 2) { output[0+i] = output[0+i] + in[0+i]; output[1+i] = output[1+i] + in[1+i]; } } /* Find the difference of two numbers: output = in - output * (note the order of the arguments!). */ static void fdifference(limb *output, const limb *in) { unsigned i; for (i = 0; i < 10; ++i) { output[i] = in[i] - output[i]; } } /* Multiply a number by a scalar: output = in * scalar */ static void fscalar_product(limb *output, const limb *in, const limb scalar) { unsigned i; for (i = 0; i < 10; ++i) { output[i] = in[i] * scalar; } } /* Multiply two numbers: output = in2 * in * * output must be distinct to both inputs. The inputs are reduced coefficient * form, the output is not. * * output[x] <= 14 * the largest product of the input limbs. */ static void fproduct(limb *output, const limb *in2, const limb *in) { output[0] = ((limb) ((s32) in2[0])) * ((s32) in[0]); output[1] = ((limb) ((s32) in2[0])) * ((s32) in[1]) + ((limb) ((s32) in2[1])) * ((s32) in[0]); output[2] = 2 * ((limb) ((s32) in2[1])) * ((s32) in[1]) + ((limb) ((s32) in2[0])) * ((s32) in[2]) + ((limb) ((s32) in2[2])) * ((s32) in[0]); output[3] = ((limb) ((s32) in2[1])) * ((s32) in[2]) + ((limb) ((s32) in2[2])) * ((s32) in[1]) + ((limb) ((s32) in2[0])) * ((s32) in[3]) + ((limb) ((s32) in2[3])) * ((s32) in[0]); output[4] = ((limb) ((s32) in2[2])) * ((s32) in[2]) + 2 * (((limb) ((s32) in2[1])) * ((s32) in[3]) + ((limb) ((s32) in2[3])) * ((s32) in[1])) + ((limb) ((s32) in2[0])) * ((s32) in[4]) + ((limb) ((s32) in2[4])) * ((s32) in[0]); output[5] = ((limb) ((s32) in2[2])) * ((s32) in[3]) + ((limb) ((s32) in2[3])) * ((s32) in[2]) + ((limb) ((s32) in2[1])) * ((s32) in[4]) + ((limb) ((s32) in2[4])) * ((s32) in[1]) + ((limb) ((s32) in2[0])) * ((s32) in[5]) + ((limb) ((s32) in2[5])) * ((s32) in[0]); output[6] = 2 * (((limb) ((s32) in2[3])) * ((s32) in[3]) + ((limb) ((s32) in2[1])) * ((s32) in[5]) + ((limb) ((s32) in2[5])) * ((s32) in[1])) + ((limb) ((s32) in2[2])) * ((s32) in[4]) + ((limb) ((s32) in2[4])) * ((s32) in[2]) + ((limb) ((s32) in2[0])) * ((s32) in[6]) + ((limb) ((s32) in2[6])) * ((s32) in[0]); output[7] = ((limb) ((s32) in2[3])) * ((s32) in[4]) + ((limb) ((s32) in2[4])) * ((s32) in[3]) + ((limb) ((s32) in2[2])) * ((s32) in[5]) + ((limb) ((s32) in2[5])) * ((s32) in[2]) + ((limb) ((s32) in2[1])) * ((s32) in[6]) + ((limb) ((s32) in2[6])) * ((s32) in[1]) + ((limb) ((s32) in2[0])) * ((s32) in[7]) + ((limb) ((s32) in2[7])) * ((s32) in[0]); output[8] = ((limb) ((s32) in2[4])) * ((s32) in[4]) + 2 * (((limb) ((s32) in2[3])) * ((s32) in[5]) + ((limb) ((s32) in2[5])) * ((s32) in[3]) + ((limb) ((s32) in2[1])) * ((s32) in[7]) + ((limb) ((s32) in2[7])) * ((s32) in[1])) + ((limb) ((s32) in2[2])) * ((s32) in[6]) + ((limb) ((s32) in2[6])) * ((s32) in[2]) + ((limb) ((s32) in2[0])) * ((s32) in[8]) + ((limb) ((s32) in2[8])) * ((s32) in[0]); output[9] = ((limb) ((s32) in2[4])) * ((s32) in[5]) + ((limb) ((s32) in2[5])) * ((s32) in[4]) + ((limb) ((s32) in2[3])) * ((s32) in[6]) + ((limb) ((s32) in2[6])) * ((s32) in[3]) + ((limb) ((s32) in2[2])) * ((s32) in[7]) + ((limb) ((s32) in2[7])) * ((s32) in[2]) + ((limb) ((s32) in2[1])) * ((s32) in[8]) + ((limb) ((s32) in2[8])) * ((s32) in[1]) + ((limb) ((s32) in2[0])) * ((s32) in[9]) + ((limb) ((s32) in2[9])) * ((s32) in[0]); output[10] = 2 * (((limb) ((s32) in2[5])) * ((s32) in[5]) + ((limb) ((s32) in2[3])) * ((s32) in[7]) + ((limb) ((s32) in2[7])) * ((s32) in[3]) + ((limb) ((s32) in2[1])) * ((s32) in[9]) + ((limb) ((s32) in2[9])) * ((s32) in[1])) + ((limb) ((s32) in2[4])) * ((s32) in[6]) + ((limb) ((s32) in2[6])) * ((s32) in[4]) + ((limb) ((s32) in2[2])) * ((s32) in[8]) + ((limb) ((s32) in2[8])) * ((s32) in[2]); output[11] = ((limb) ((s32) in2[5])) * ((s32) in[6]) + ((limb) ((s32) in2[6])) * ((s32) in[5]) + ((limb) ((s32) in2[4])) * ((s32) in[7]) + ((limb) ((s32) in2[7])) * ((s32) in[4]) + ((limb) ((s32) in2[3])) * ((s32) in[8]) + ((limb) ((s32) in2[8])) * ((s32) in[3]) + ((limb) ((s32) in2[2])) * ((s32) in[9]) + ((limb) ((s32) in2[9])) * ((s32) in[2]); output[12] = ((limb) ((s32) in2[6])) * ((s32) in[6]) + 2 * (((limb) ((s32) in2[5])) * ((s32) in[7]) + ((limb) ((s32) in2[7])) * ((s32) in[5]) + ((limb) ((s32) in2[3])) * ((s32) in[9]) + ((limb) ((s32) in2[9])) * ((s32) in[3])) + ((limb) ((s32) in2[4])) * ((s32) in[8]) + ((limb) ((s32) in2[8])) * ((s32) in[4]); output[13] = ((limb) ((s32) in2[6])) * ((s32) in[7]) + ((limb) ((s32) in2[7])) * ((s32) in[6]) + ((limb) ((s32) in2[5])) * ((s32) in[8]) + ((limb) ((s32) in2[8])) * ((s32) in[5]) + ((limb) ((s32) in2[4])) * ((s32) in[9]) + ((limb) ((s32) in2[9])) * ((s32) in[4]); output[14] = 2 * (((limb) ((s32) in2[7])) * ((s32) in[7]) + ((limb) ((s32) in2[5])) * ((s32) in[9]) + ((limb) ((s32) in2[9])) * ((s32) in[5])) + ((limb) ((s32) in2[6])) * ((s32) in[8]) + ((limb) ((s32) in2[8])) * ((s32) in[6]); output[15] = ((limb) ((s32) in2[7])) * ((s32) in[8]) + ((limb) ((s32) in2[8])) * ((s32) in[7]) + ((limb) ((s32) in2[6])) * ((s32) in[9]) + ((limb) ((s32) in2[9])) * ((s32) in[6]); output[16] = ((limb) ((s32) in2[8])) * ((s32) in[8]) + 2 * (((limb) ((s32) in2[7])) * ((s32) in[9]) + ((limb) ((s32) in2[9])) * ((s32) in[7])); output[17] = ((limb) ((s32) in2[8])) * ((s32) in[9]) + ((limb) ((s32) in2[9])) * ((s32) in[8]); output[18] = 2 * ((limb) ((s32) in2[9])) * ((s32) in[9]); } /* Reduce a long form to a short form by taking the input mod 2^255 - 19. * * On entry: |output[i]| < 14*2^54 * On exit: |output[0..8]| < 280*2^54 */ static void freduce_degree(limb *output) { /* Each of these shifts and adds ends up multiplying the value by 19. * * For output[0..8], the absolute entry value is < 14*2^54 and we add, at * most, 19*14*2^54 thus, on exit, |output[0..8]| < 280*2^54. */ output[8] += output[18] << 4; output[8] += output[18] << 1; output[8] += output[18]; output[7] += output[17] << 4; output[7] += output[17] << 1; output[7] += output[17]; output[6] += output[16] << 4; output[6] += output[16] << 1; output[6] += output[16]; output[5] += output[15] << 4; output[5] += output[15] << 1; output[5] += output[15]; output[4] += output[14] << 4; output[4] += output[14] << 1; output[4] += output[14]; output[3] += output[13] << 4; output[3] += output[13] << 1; output[3] += output[13]; output[2] += output[12] << 4; output[2] += output[12] << 1; output[2] += output[12]; output[1] += output[11] << 4; output[1] += output[11] << 1; output[1] += output[11]; output[0] += output[10] << 4; output[0] += output[10] << 1; output[0] += output[10]; } #if (-1 & 3) != 3 #error "This code only works on a two's complement system" #endif /* return v / 2^26, using only shifts and adds. * * On entry: v can take any value. */ static inline limb div_by_2_26(const limb v) { /* High word of v; no shift needed. */ const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32); /* Set to all 1s if v was negative; else set to 0s. */ const int32_t sign = ((int32_t) highword) >> 31; /* Set to 0x3ffffff if v was negative; else set to 0. */ const int32_t roundoff = ((uint32_t) sign) >> 6; /* Should return v / (1<<26) */ return (v + roundoff) >> 26; } /* return v / (2^25), using only shifts and adds. * * On entry: v can take any value. */ static inline limb div_by_2_25(const limb v) { /* High word of v; no shift needed*/ const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32); /* Set to all 1s if v was negative; else set to 0s. */ const int32_t sign = ((int32_t) highword) >> 31; /* Set to 0x1ffffff if v was negative; else set to 0. */ const int32_t roundoff = ((uint32_t) sign) >> 7; /* Should return v / (1<<25) */ return (v + roundoff) >> 25; } /* Reduce all coefficients of the short form input so that |x| < 2^26. * * On entry: |output[i]| < 280*2^54 */ static void freduce_coefficients(limb *output) { unsigned i; output[10] = 0; for (i = 0; i < 10; i += 2) { limb over = div_by_2_26(output[i]); /* The entry condition (that |output[i]| < 280*2^54) means that over is, at * most, 280*2^28 in the first iteration of this loop. This is added to the * next limb and we can approximate the resulting bound of that limb by * 281*2^54. */ output[i] -= over << 26; output[i+1] += over; /* For the first iteration, |output[i+1]| < 281*2^54, thus |over| < * 281*2^29. When this is added to the next limb, the resulting bound can * be approximated as 281*2^54. * * For subsequent iterations of the loop, 281*2^54 remains a conservative * bound and no overflow occurs. */ over = div_by_2_25(output[i+1]); output[i+1] -= over << 25; output[i+2] += over; } /* Now |output[10]| < 281*2^29 and all other coefficients are reduced. */ output[0] += output[10] << 4; output[0] += output[10] << 1; output[0] += output[10]; output[10] = 0; /* Now output[1..9] are reduced, and |output[0]| < 2^26 + 19*281*2^29 * So |over| will be no more than 2^16. */ { limb over = div_by_2_26(output[0]); output[0] -= over << 26; output[1] += over; } /* Now output[0,2..9] are reduced, and |output[1]| < 2^25 + 2^16 < 2^26. The * bound on |output[1]| is sufficient to meet our needs. */ } /* A helpful wrapper around fproduct: output = in * in2. * * On entry: |in[i]| < 2^27 and |in2[i]| < 2^27. * * output must be distinct to both inputs. The output is reduced degree * (indeed, one need only provide storage for 10 limbs) and |output[i]| < 2^26. */ static void fmul(limb *output, const limb *in, const limb *in2) { limb t[19]; fproduct(t, in, in2); /* |t[i]| < 14*2^54 */ freduce_degree(t); freduce_coefficients(t); /* |t[i]| < 2^26 */ memcpy(output, t, sizeof(limb) * 10); } /* Square a number: output = in**2 * * output must be distinct from the input. The inputs are reduced coefficient * form, the output is not. * * output[x] <= 14 * the largest product of the input limbs. */ static void fsquare_inner(limb *output, const limb *in) { output[0] = ((limb) ((s32) in[0])) * ((s32) in[0]); output[1] = 2 * ((limb) ((s32) in[0])) * ((s32) in[1]); output[2] = 2 * (((limb) ((s32) in[1])) * ((s32) in[1]) + ((limb) ((s32) in[0])) * ((s32) in[2])); output[3] = 2 * (((limb) ((s32) in[1])) * ((s32) in[2]) + ((limb) ((s32) in[0])) * ((s32) in[3])); output[4] = ((limb) ((s32) in[2])) * ((s32) in[2]) + 4 * ((limb) ((s32) in[1])) * ((s32) in[3]) + 2 * ((limb) ((s32) in[0])) * ((s32) in[4]); output[5] = 2 * (((limb) ((s32) in[2])) * ((s32) in[3]) + ((limb) ((s32) in[1])) * ((s32) in[4]) + ((limb) ((s32) in[0])) * ((s32) in[5])); output[6] = 2 * (((limb) ((s32) in[3])) * ((s32) in[3]) + ((limb) ((s32) in[2])) * ((s32) in[4]) + ((limb) ((s32) in[0])) * ((s32) in[6]) + 2 * ((limb) ((s32) in[1])) * ((s32) in[5])); output[7] = 2 * (((limb) ((s32) in[3])) * ((s32) in[4]) + ((limb) ((s32) in[2])) * ((s32) in[5]) + ((limb) ((s32) in[1])) * ((s32) in[6]) + ((limb) ((s32) in[0])) * ((s32) in[7])); output[8] = ((limb) ((s32) in[4])) * ((s32) in[4]) + 2 * (((limb) ((s32) in[2])) * ((s32) in[6]) + ((limb) ((s32) in[0])) * ((s32) in[8]) + 2 * (((limb) ((s32) in[1])) * ((s32) in[7]) + ((limb) ((s32) in[3])) * ((s32) in[5]))); output[9] = 2 * (((limb) ((s32) in[4])) * ((s32) in[5]) + ((limb) ((s32) in[3])) * ((s32) in[6]) + ((limb) ((s32) in[2])) * ((s32) in[7]) + ((limb) ((s32) in[1])) * ((s32) in[8]) + ((limb) ((s32) in[0])) * ((s32) in[9])); output[10] = 2 * (((limb) ((s32) in[5])) * ((s32) in[5]) + ((limb) ((s32) in[4])) * ((s32) in[6]) + ((limb) ((s32) in[2])) * ((s32) in[8]) + 2 * (((limb) ((s32) in[3])) * ((s32) in[7]) + ((limb) ((s32) in[1])) * ((s32) in[9]))); output[11] = 2 * (((limb) ((s32) in[5])) * ((s32) in[6]) + ((limb) ((s32) in[4])) * ((s32) in[7]) + ((limb) ((s32) in[3])) * ((s32) in[8]) + ((limb) ((s32) in[2])) * ((s32) in[9])); output[12] = ((limb) ((s32) in[6])) * ((s32) in[6]) + 2 * (((limb) ((s32) in[4])) * ((s32) in[8]) + 2 * (((limb) ((s32) in[5])) * ((s32) in[7]) + ((limb) ((s32) in[3])) * ((s32) in[9]))); output[13] = 2 * (((limb) ((s32) in[6])) * ((s32) in[7]) + ((limb) ((s32) in[5])) * ((s32) in[8]) + ((limb) ((s32) in[4])) * ((s32) in[9])); output[14] = 2 * (((limb) ((s32) in[7])) * ((s32) in[7]) + ((limb) ((s32) in[6])) * ((s32) in[8]) + 2 * ((limb) ((s32) in[5])) * ((s32) in[9])); output[15] = 2 * (((limb) ((s32) in[7])) * ((s32) in[8]) + ((limb) ((s32) in[6])) * ((s32) in[9])); output[16] = ((limb) ((s32) in[8])) * ((s32) in[8]) + 4 * ((limb) ((s32) in[7])) * ((s32) in[9]); output[17] = 2 * ((limb) ((s32) in[8])) * ((s32) in[9]); output[18] = 2 * ((limb) ((s32) in[9])) * ((s32) in[9]); } /* fsquare sets output = in^2. * * On entry: The |in| argument is in reduced coefficients form and |in[i]| < * 2^27. * * On exit: The |output| argument is in reduced coefficients form (indeed, one * need only provide storage for 10 limbs) and |out[i]| < 2^26. */ static void fsquare(limb *output, const limb *in) { limb t[19]; fsquare_inner(t, in); /* |t[i]| < 14*2^54 because the largest product of two limbs will be < * 2^(27+27) and fsquare_inner adds together, at most, 14 of those * products. */ freduce_degree(t); freduce_coefficients(t); /* |t[i]| < 2^26 */ memcpy(output, t, sizeof(limb) * 10); } /* Take a little-endian, 32-byte number and expand it into polynomial form */ static void fexpand(limb *output, const u8 *input) { #define F(n,start,shift,mask) \ output[n] = ((((limb) input[start + 0]) | \ ((limb) input[start + 1]) << 8 | \ ((limb) input[start + 2]) << 16 | \ ((limb) input[start + 3]) << 24) >> shift) & mask; F(0, 0, 0, 0x3ffffff); F(1, 3, 2, 0x1ffffff); F(2, 6, 3, 0x3ffffff); F(3, 9, 5, 0x1ffffff); F(4, 12, 6, 0x3ffffff); F(5, 16, 0, 0x1ffffff); F(6, 19, 1, 0x3ffffff); F(7, 22, 3, 0x1ffffff); F(8, 25, 4, 0x3ffffff); F(9, 28, 6, 0x1ffffff); #undef F } #if (-32 >> 1) != -16 #error "This code only works when >> does sign-extension on negative numbers" #endif /* s32_eq returns 0xffffffff iff a == b and zero otherwise. */ static s32 s32_eq(s32 a, s32 b) { a = ~(a ^ b); a &= a << 16; a &= a << 8; a &= a << 4; a &= a << 2; a &= a << 1; return a >> 31; } /* s32_gte returns 0xffffffff if a >= b and zero otherwise, where a and b are * both non-negative. */ static s32 s32_gte(s32 a, s32 b) { a -= b; /* a >= 0 iff a >= b. */ return ~(a >> 31); } /* Take a fully reduced polynomial form number and contract it into a * little-endian, 32-byte array. * * On entry: |input_limbs[i]| < 2^26 */ static void fcontract(u8 *output, limb *input_limbs) { int i; int j; s32 input[10]; s32 mask; /* |input_limbs[i]| < 2^26, so it's valid to convert to an s32. */ for (i = 0; i < 10; i++) { input[i] = input_limbs[i]; } for (j = 0; j < 2; ++j) { for (i = 0; i < 9; ++i) { if ((i & 1) == 1) { /* This calculation is a time-invariant way to make input[i] * non-negative by borrowing from the next-larger limb. */ const s32 mask = input[i] >> 31; const s32 carry = -((input[i] & mask) >> 25); input[i] = input[i] + (carry << 25); input[i+1] = input[i+1] - carry; } else { const s32 mask = input[i] >> 31; const s32 carry = -((input[i] & mask) >> 26); input[i] = input[i] + (carry << 26); input[i+1] = input[i+1] - carry; } } /* There's no greater limb for input[9] to borrow from, but we can multiply * by 19 and borrow from input[0], which is valid mod 2^255-19. */ { const s32 mask = input[9] >> 31; const s32 carry = -((input[9] & mask) >> 25); input[9] = input[9] + (carry << 25); input[0] = input[0] - (carry * 19); } /* After the first iteration, input[1..9] are non-negative and fit within * 25 or 26 bits, depending on position. However, input[0] may be * negative. */ } /* The first borrow-propagation pass above ended with every limb except (possibly) input[0] non-negative. If input[0] was negative after the first pass, then it was because of a carry from input[9]. On entry, input[9] < 2^26 so the carry was, at most, one, since (2**26-1) >> 25 = 1. Thus input[0] >= -19. In the second pass, each limb is decreased by at most one. Thus the second borrow-propagation pass could only have wrapped around to decrease input[0] again if the first pass left input[0] negative *and* input[1] through input[9] were all zero. In that case, input[1] is now 2^25 - 1, and this last borrow-propagation step will leave input[1] non-negative. */ { const s32 mask = input[0] >> 31; const s32 carry = -((input[0] & mask) >> 26); input[0] = input[0] + (carry << 26); input[1] = input[1] - carry; } /* All input[i] are now non-negative. However, there might be values between * 2^25 and 2^26 in a limb which is, nominally, 25 bits wide. */ for (j = 0; j < 2; j++) { for (i = 0; i < 9; i++) { if ((i & 1) == 1) { const s32 carry = input[i] >> 25; input[i] &= 0x1ffffff; input[i+1] += carry; } else { const s32 carry = input[i] >> 26; input[i] &= 0x3ffffff; input[i+1] += carry; } } { const s32 carry = input[9] >> 25; input[9] &= 0x1ffffff; input[0] += 19*carry; } } /* If the first carry-chain pass, just above, ended up with a carry from * input[9], and that caused input[0] to be out-of-bounds, then input[0] was * < 2^26 + 2*19, because the carry was, at most, two. * * If the second pass carried from input[9] again then input[0] is < 2*19 and * the input[9] -> input[0] carry didn't push input[0] out of bounds. */ /* It still remains the case that input might be between 2^255-19 and 2^255. * In this case, input[1..9] must take their maximum value and input[0] must * be >= (2^255-19) & 0x3ffffff, which is 0x3ffffed. */ mask = s32_gte(input[0], 0x3ffffed); for (i = 1; i < 10; i++) { if ((i & 1) == 1) { mask &= s32_eq(input[i], 0x1ffffff); } else { mask &= s32_eq(input[i], 0x3ffffff); } } /* mask is either 0xffffffff (if input >= 2^255-19) and zero otherwise. Thus * this conditionally subtracts 2^255-19. */ input[0] -= mask & 0x3ffffed; for (i = 1; i < 10; i++) { if ((i & 1) == 1) { input[i] -= mask & 0x1ffffff; } else { input[i] -= mask & 0x3ffffff; } } input[1] <<= 2; input[2] <<= 3; input[3] <<= 5; input[4] <<= 6; input[6] <<= 1; input[7] <<= 3; input[8] <<= 4; input[9] <<= 6; #define F(i, s) \ output[s+0] |= input[i] & 0xff; \ output[s+1] = (input[i] >> 8) & 0xff; \ output[s+2] = (input[i] >> 16) & 0xff; \ output[s+3] = (input[i] >> 24) & 0xff; output[0] = 0; output[16] = 0; F(0,0); F(1,3); F(2,6); F(3,9); F(4,12); F(5,16); F(6,19); F(7,22); F(8,25); F(9,28); #undef F } /* Input: Q, Q', Q-Q' * Output: 2Q, Q+Q' * * x2 z3: long form * x3 z3: long form * x z: short form, destroyed * xprime zprime: short form, destroyed * qmqp: short form, preserved * * On entry and exit, the absolute value of the limbs of all inputs and outputs * are < 2^26. */ static void fmonty(limb *x2, limb *z2, /* output 2Q */ limb *x3, limb *z3, /* output Q + Q' */ limb *x, limb *z, /* input Q */ limb *xprime, limb *zprime, /* input Q' */ const limb *qmqp /* input Q - Q' */) { limb origx[10], origxprime[10], zzz[19], xx[19], zz[19], xxprime[19], zzprime[19], zzzprime[19], xxxprime[19]; memcpy(origx, x, 10 * sizeof(limb)); fsum(x, z); /* |x[i]| < 2^27 */ fdifference(z, origx); /* does x - z */ /* |z[i]| < 2^27 */ memcpy(origxprime, xprime, sizeof(limb) * 10); fsum(xprime, zprime); /* |xprime[i]| < 2^27 */ fdifference(zprime, origxprime); /* |zprime[i]| < 2^27 */ fproduct(xxprime, xprime, z); /* |xxprime[i]| < 14*2^54: the largest product of two limbs will be < * 2^(27+27) and fproduct adds together, at most, 14 of those products. * (Approximating that to 2^58 doesn't work out.) */ fproduct(zzprime, x, zprime); /* |zzprime[i]| < 14*2^54 */ freduce_degree(xxprime); freduce_coefficients(xxprime); /* |xxprime[i]| < 2^26 */ freduce_degree(zzprime); freduce_coefficients(zzprime); /* |zzprime[i]| < 2^26 */ memcpy(origxprime, xxprime, sizeof(limb) * 10); fsum(xxprime, zzprime); /* |xxprime[i]| < 2^27 */ fdifference(zzprime, origxprime); /* |zzprime[i]| < 2^27 */ fsquare(xxxprime, xxprime); /* |xxxprime[i]| < 2^26 */ fsquare(zzzprime, zzprime); /* |zzzprime[i]| < 2^26 */ fproduct(zzprime, zzzprime, qmqp); /* |zzprime[i]| < 14*2^52 */ freduce_degree(zzprime); freduce_coefficients(zzprime); /* |zzprime[i]| < 2^26 */ memcpy(x3, xxxprime, sizeof(limb) * 10); memcpy(z3, zzprime, sizeof(limb) * 10); fsquare(xx, x); /* |xx[i]| < 2^26 */ fsquare(zz, z); /* |zz[i]| < 2^26 */ fproduct(x2, xx, zz); /* |x2[i]| < 14*2^52 */ freduce_degree(x2); freduce_coefficients(x2); /* |x2[i]| < 2^26 */ fdifference(zz, xx); // does zz = xx - zz /* |zz[i]| < 2^27 */ memset(zzz + 10, 0, sizeof(limb) * 9); fscalar_product(zzz, zz, 121665); /* |zzz[i]| < 2^(27+17) */ /* No need to call freduce_degree here: fscalar_product doesn't increase the degree of its input. */ freduce_coefficients(zzz); /* |zzz[i]| < 2^26 */ fsum(zzz, xx); /* |zzz[i]| < 2^27 */ fproduct(z2, zz, zzz); /* |z2[i]| < 14*2^(26+27) */ freduce_degree(z2); freduce_coefficients(z2); /* |z2|i| < 2^26 */ } /* Conditionally swap two reduced-form limb arrays if 'iswap' is 1, but leave * them unchanged if 'iswap' is 0. Runs in data-invariant time to avoid * side-channel attacks. * * NOTE that this function requires that 'iswap' be 1 or 0; other values give * wrong results. Also, the two limb arrays must be in reduced-coefficient, * reduced-degree form: the values in a[10..19] or b[10..19] aren't swapped, * and all all values in a[0..9],b[0..9] must have magnitude less than * INT32_MAX. */ static void swap_conditional(limb a[19], limb b[19], limb iswap) { unsigned i; const s32 swap = (s32) -iswap; for (i = 0; i < 10; ++i) { const s32 x = swap & ( ((s32)a[i]) ^ ((s32)b[i]) ); a[i] = ((s32)a[i]) ^ x; b[i] = ((s32)b[i]) ^ x; } } /* Calculates nQ where Q is the x-coordinate of a point on the curve * * resultx/resultz: the x coordinate of the resulting curve point (short form) * n: a little endian, 32-byte number * q: a point of the curve (short form) */ static void cmult(limb *resultx, limb *resultz, const u8 *n, const limb *q) { limb a[19] = {0}, b[19] = {1}, c[19] = {1}, d[19] = {0}; limb *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t; limb e[19] = {0}, f[19] = {1}, g[19] = {0}, h[19] = {1}; limb *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h; unsigned i, j; memcpy(nqpqx, q, sizeof(limb) * 10); for (i = 0; i < 32; ++i) { u8 byte = n[31 - i]; for (j = 0; j < 8; ++j) { const limb bit = byte >> 7; swap_conditional(nqx, nqpqx, bit); swap_conditional(nqz, nqpqz, bit); fmonty(nqx2, nqz2, nqpqx2, nqpqz2, nqx, nqz, nqpqx, nqpqz, q); swap_conditional(nqx2, nqpqx2, bit); swap_conditional(nqz2, nqpqz2, bit); t = nqx; nqx = nqx2; nqx2 = t; t = nqz; nqz = nqz2; nqz2 = t; t = nqpqx; nqpqx = nqpqx2; nqpqx2 = t; t = nqpqz; nqpqz = nqpqz2; nqpqz2 = t; byte <<= 1; } } memcpy(resultx, nqx, sizeof(limb) * 10); memcpy(resultz, nqz, sizeof(limb) * 10); } // ----------------------------------------------------------------------------- // Shamelessly copied from djb's code // ----------------------------------------------------------------------------- static void crecip(limb *out, const limb *z) { limb z2[10]; limb z9[10]; limb z11[10]; limb z2_5_0[10]; limb z2_10_0[10]; limb z2_20_0[10]; limb z2_50_0[10]; limb z2_100_0[10]; limb t0[10]; limb t1[10]; int i; /* 2 */ fsquare(z2,z); /* 4 */ fsquare(t1,z2); /* 8 */ fsquare(t0,t1); /* 9 */ fmul(z9,t0,z); /* 11 */ fmul(z11,z9,z2); /* 22 */ fsquare(t0,z11); /* 2^5 - 2^0 = 31 */ fmul(z2_5_0,t0,z9); /* 2^6 - 2^1 */ fsquare(t0,z2_5_0); /* 2^7 - 2^2 */ fsquare(t1,t0); /* 2^8 - 2^3 */ fsquare(t0,t1); /* 2^9 - 2^4 */ fsquare(t1,t0); /* 2^10 - 2^5 */ fsquare(t0,t1); /* 2^10 - 2^0 */ fmul(z2_10_0,t0,z2_5_0); /* 2^11 - 2^1 */ fsquare(t0,z2_10_0); /* 2^12 - 2^2 */ fsquare(t1,t0); /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } /* 2^20 - 2^0 */ fmul(z2_20_0,t1,z2_10_0); /* 2^21 - 2^1 */ fsquare(t0,z2_20_0); /* 2^22 - 2^2 */ fsquare(t1,t0); /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } /* 2^40 - 2^0 */ fmul(t0,t1,z2_20_0); /* 2^41 - 2^1 */ fsquare(t1,t0); /* 2^42 - 2^2 */ fsquare(t0,t1); /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t1,t0); fsquare(t0,t1); } /* 2^50 - 2^0 */ fmul(z2_50_0,t0,z2_10_0); /* 2^51 - 2^1 */ fsquare(t0,z2_50_0); /* 2^52 - 2^2 */ fsquare(t1,t0); /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } /* 2^100 - 2^0 */ fmul(z2_100_0,t1,z2_50_0); /* 2^101 - 2^1 */ fsquare(t1,z2_100_0); /* 2^102 - 2^2 */ fsquare(t0,t1); /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fsquare(t1,t0); fsquare(t0,t1); } /* 2^200 - 2^0 */ fmul(t1,t0,z2_100_0); /* 2^201 - 2^1 */ fsquare(t0,t1); /* 2^202 - 2^2 */ fsquare(t1,t0); /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } /* 2^250 - 2^0 */ fmul(t0,t1,z2_50_0); /* 2^251 - 2^1 */ fsquare(t1,t0); /* 2^252 - 2^2 */ fsquare(t0,t1); /* 2^253 - 2^3 */ fsquare(t1,t0); /* 2^254 - 2^4 */ fsquare(t0,t1); /* 2^255 - 2^5 */ fsquare(t1,t0); /* 2^255 - 21 */ fmul(out,t1,z11); } int curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) { limb bp[10], x[10], z[11], zmone[10]; uint8_t e[32]; int i; for (i = 0; i < 32; ++i) e[i] = secret[i]; e[0] &= 248; e[31] &= 127; e[31] |= 64; fexpand(bp, basepoint); cmult(x, z, e, bp); crecip(zmone, z); fmul(z, x, zmone); fcontract(mypublic, z); return 0; } olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/curve25519-donna.podspec000066400000000000000000000011521311755073500255240ustar00rootroot00000000000000Pod::Spec.new do |s| s.name = "curve25519-donna" s.version = "1.2.1" s.summary = "Implementations of a fast elliptic-curve, Diffie-Hellman primitive" s.description = <<-DESC Curve25519 is a state-of-the-art Diffie-Hellman function suitable for a wide variety of applications. DESC s.homepage = "http://code.google.com/p/curve25519-donna" s.license = 'BSD 3-Clause' s.author = 'Dan Bernstein' s.source = { :git => "https://github.com/agl/curve25519-donna.git", :tag => "1.2.1" } s.source_files = 'curve25519-donna.c' end olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/python-src/000077500000000000000000000000001311755073500234255ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/python-src/curve25519/000077500000000000000000000000001311755073500251575ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/python-src/curve25519/__init__.py000066400000000000000000000001311311755073500272630ustar00rootroot00000000000000 from .keys import Private, Public hush_pyflakes = [Private, Public]; del hush_pyflakes olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/python-src/curve25519/curve25519module.c000066400000000000000000000056211311755073500302670ustar00rootroot00000000000000/* tell python that PyArg_ParseTuple(t#) means Py_ssize_t, not int */ #define PY_SSIZE_T_CLEAN #include #if (PY_VERSION_HEX < 0x02050000) typedef int Py_ssize_t; #endif /* This is required for compatibility with Python 2. */ #if PY_MAJOR_VERSION >= 3 #include #define y "y" #else #define PyBytes_FromStringAndSize PyString_FromStringAndSize #define y "t" #endif int curve25519_donna(char *mypublic, const char *secret, const char *basepoint); static PyObject * pycurve25519_makeprivate(PyObject *self, PyObject *args) { char *in1; Py_ssize_t in1len; if (!PyArg_ParseTuple(args, y"#:clamp", &in1, &in1len)) return NULL; if (in1len != 32) { PyErr_SetString(PyExc_ValueError, "input must be 32-byte string"); return NULL; } in1[0] &= 248; in1[31] &= 127; in1[31] |= 64; return PyBytes_FromStringAndSize((char *)in1, 32); } static PyObject * pycurve25519_makepublic(PyObject *self, PyObject *args) { const char *private; char mypublic[32]; char basepoint[32] = {9}; Py_ssize_t privatelen; if (!PyArg_ParseTuple(args, y"#:makepublic", &private, &privatelen)) return NULL; if (privatelen != 32) { PyErr_SetString(PyExc_ValueError, "input must be 32-byte string"); return NULL; } curve25519_donna(mypublic, private, basepoint); return PyBytes_FromStringAndSize((char *)mypublic, 32); } static PyObject * pycurve25519_makeshared(PyObject *self, PyObject *args) { const char *myprivate, *theirpublic; char shared_key[32]; Py_ssize_t myprivatelen, theirpubliclen; if (!PyArg_ParseTuple(args, y"#"y"#:generate", &myprivate, &myprivatelen, &theirpublic, &theirpubliclen)) return NULL; if (myprivatelen != 32) { PyErr_SetString(PyExc_ValueError, "input must be 32-byte string"); return NULL; } if (theirpubliclen != 32) { PyErr_SetString(PyExc_ValueError, "input must be 32-byte string"); return NULL; } curve25519_donna(shared_key, myprivate, theirpublic); return PyBytes_FromStringAndSize((char *)shared_key, 32); } static PyMethodDef curve25519_functions[] = { {"make_private", pycurve25519_makeprivate, METH_VARARGS, "data->private"}, {"make_public", pycurve25519_makepublic, METH_VARARGS, "private->public"}, {"make_shared", pycurve25519_makeshared, METH_VARARGS, "private+public->shared"}, {NULL, NULL, 0, NULL}, }; #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef curve25519_module = { PyModuleDef_HEAD_INIT, "_curve25519", NULL, NULL, curve25519_functions, }; PyObject * PyInit__curve25519(void) { return PyModule_Create(&curve25519_module); } #else PyMODINIT_FUNC init_curve25519(void) { (void)Py_InitModule("_curve25519", curve25519_functions); } #endifolm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/python-src/curve25519/keys.py000066400000000000000000000027621311755073500265130ustar00rootroot00000000000000from . import _curve25519 from hashlib import sha256 import os # the curve25519 functions are really simple, and could be used without an # OOP layer, but it's a bit too easy to accidentally swap the private and # public keys that way. def _hash_shared(shared): return sha256(b"curve25519-shared:"+shared).digest() class Private: def __init__(self, secret=None, seed=None): if secret is None: if seed is None: secret = os.urandom(32) else: secret = sha256(b"curve25519-private:"+seed).digest() else: assert seed is None, "provide secret, seed, or neither, not both" if not isinstance(secret, bytes) or len(secret) != 32: raise TypeError("secret= must be 32-byte string") self.private = _curve25519.make_private(secret) def serialize(self): return self.private def get_public(self): return Public(_curve25519.make_public(self.private)) def get_shared_key(self, public, hashfunc=None): if not isinstance(public, Public): raise ValueError("'public' must be an instance of Public") if hashfunc is None: hashfunc = _hash_shared shared = _curve25519.make_shared(self.private, public.public) return hashfunc(shared) class Public: def __init__(self, public): assert isinstance(public, bytes) assert len(public) == 32 self.public = public def serialize(self): return self.public olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/python-src/curve25519/test/000077500000000000000000000000001311755073500261365ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/python-src/curve25519/test/__init__.py000066400000000000000000000000001311755073500302350ustar00rootroot00000000000000test_curve25519.py000077500000000000000000000075061311755073500312350ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/python-src/curve25519/test#! /usr/bin/env python import unittest from curve25519 import Private, Public from hashlib import sha1, sha256 from binascii import hexlify class Basic(unittest.TestCase): def test_basic(self): secret1 = b"abcdefghijklmnopqrstuvwxyz123456" self.assertEqual(len(secret1), 32) secret2 = b"654321zyxwvutsrqponmlkjihgfedcba" self.assertEqual(len(secret2), 32) priv1 = Private(secret=secret1) pub1 = priv1.get_public() priv2 = Private(secret=secret2) pub2 = priv2.get_public() shared12 = priv1.get_shared_key(pub2) e = b"b0818125eab42a8ac1af5e8b9b9c15ed2605c2bbe9675de89e5e6e7f442b9598" self.assertEqual(hexlify(shared12), e) shared21 = priv2.get_shared_key(pub1) self.assertEqual(shared12, shared21) pub2a = Public(pub2.serialize()) shared12a = priv1.get_shared_key(pub2a) self.assertEqual(hexlify(shared12a), e) def test_errors(self): priv1 = Private() self.assertRaises(ValueError, priv1.get_shared_key, priv1) def test_seed(self): # use 32-byte secret self.assertRaises(TypeError, Private, secret=123) self.assertRaises(TypeError, Private, secret=b"too short") secret1 = b"abcdefghijklmnopqrstuvwxyz123456" assert len(secret1) == 32 priv1 = Private(secret=secret1) priv1a = Private(secret=secret1) priv1b = Private(priv1.serialize()) self.assertEqual(priv1.serialize(), priv1a.serialize()) self.assertEqual(priv1.serialize(), priv1b.serialize()) e = b"6062636465666768696a6b6c6d6e6f707172737475767778797a313233343576" self.assertEqual(hexlify(priv1.serialize()), e) # the private key is a clamped form of the secret, so they won't # quite be the same p = Private(secret=b"\x00"*32) self.assertEqual(hexlify(p.serialize()), b"00"*31+b"40") p = Private(secret=b"\xff"*32) self.assertEqual(hexlify(p.serialize()), b"f8"+b"ff"*30+b"7f") # use arbitrary-length seed self.assertRaises(TypeError, Private, seed=123) priv1 = Private(seed=b"abc") priv1a = Private(seed=b"abc") priv1b = Private(priv1.serialize()) self.assertEqual(priv1.serialize(), priv1a.serialize()) self.assertEqual(priv1.serialize(), priv1b.serialize()) self.assertRaises(AssertionError, Private, seed=b"abc", secret=b"no") priv1 = Private(seed=b"abc") priv1a = Private(priv1.serialize()) self.assertEqual(priv1.serialize(), priv1a.serialize()) self.assertRaises(AssertionError, Private, seed=b"abc", secret=b"no") # use built-in os.urandom priv2 = Private() priv2a = Private(priv2.private) self.assertEqual(priv2.serialize(), priv2a.serialize()) # attempt to use both secret= and seed=, not allowed self.assertRaises(AssertionError, Private, seed=b"abc", secret=b"no") def test_hashfunc(self): priv1 = Private(seed=b"abc") priv2 = Private(seed=b"def") shared_sha256 = priv1.get_shared_key(priv2.get_public()) e = b"da959ffe77ebeb4757fe5ba310e28ede425ae0d0ff5ec9c884e2d08f311cf5e5" self.assertEqual(hexlify(shared_sha256), e) # confirm the hash function remains what we think it is def myhash(shared_key): return sha256(b"curve25519-shared:"+shared_key).digest() shared_myhash = priv1.get_shared_key(priv2.get_public(), myhash) self.assertEqual(hexlify(shared_myhash), e) def hexhash(shared_key): return sha1(shared_key).hexdigest().encode() shared_hexhash = priv1.get_shared_key(priv2.get_public(), hexhash) self.assertEqual(shared_hexhash, b"80eec98222c8edc4324fb9477a3c775ce7c6c93a") if __name__ == "__main__": unittest.main() olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/python-src/curve25519/test/test_speed.py000077500000000000000000000023321311755073500306520ustar00rootroot00000000000000#! /usr/bin/env python from time import time from curve25519 import Private count = 10000 elapsed_get_public = 0.0 elapsed_get_shared = 0.0 def abbreviate_time(data): # 1.23s, 790ms, 132us if data is None: return "" s = float(data) if s >= 10: #return abbreviate.abbreviate_time(data) return "%d" % s if s >= 1.0: return "%.2fs" % s if s >= 0.01: return "%dms" % (1000*s) if s >= 0.001: return "%.1fms" % (1000*s) if s >= 0.000001: return "%.1fus" % (1000000*s) return "%dns" % (1000000000*s) def nohash(key): return key for i in range(count): p = Private() start = time() pub = p.get_public() elapsed_get_public += time() - start pub2 = Private().get_public() start = time() shared = p.get_shared_key(pub2) #, hashfunc=nohash) elapsed_get_shared += time() - start print("get_public: %s" % abbreviate_time(elapsed_get_public / count)) print("get_shared: %s" % abbreviate_time(elapsed_get_shared / count)) # these take about 560us-570us each (with the default compiler settings, -Os) # on my laptop, same with -O2 # of which the python overhead is about 5us # and the get_shared_key() hash step adds about 5us olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/setup.py000077500000000000000000000027131311755073500230370ustar00rootroot00000000000000#! /usr/bin/env python from subprocess import Popen, PIPE from distutils.core import setup, Extension version = Popen(["git", "describe", "--tags"], stdout=PIPE).communicate()[0]\ .strip().decode("utf8") ext_modules = [Extension("curve25519._curve25519", ["python-src/curve25519/curve25519module.c", "curve25519-donna.c"], )] short_description="Python wrapper for the Curve25519 cryptographic library" long_description="""\ Curve25519 is a fast elliptic-curve key-agreement protocol, in which two parties Alice and Bob each generate a (public,private) keypair, exchange public keys, and can then compute the same shared key. Specifically, Alice computes F(Aprivate, Bpublic), Bob computes F(Bprivate, Apublic), and both get the same value (and nobody else can guess that shared value, even if they know Apublic and Bpublic). This is a Python wrapper for the portable 'curve25519-donna' implementation of this algorithm, written by Adam Langley, hosted at http://code.google.com/p/curve25519-donna/ """ setup(name="curve25519-donna", version=version, description=short_description, long_description=long_description, author="Brian Warner", author_email="warner-pycurve25519-donna@lothar.com", license="BSD", packages=["curve25519", "curve25519.test"], package_dir={"curve25519": "python-src/curve25519"}, ext_modules=ext_modules, ) olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/speed-curve25519.c000066400000000000000000000016361311755073500243210ustar00rootroot00000000000000#include #include #include #include #include typedef uint8_t u8; extern void curve25519_donna(u8 *output, const u8 *secret, const u8 *bp); static uint64_t time_now() { struct timeval tv; uint64_t ret; gettimeofday(&tv, NULL); ret = tv.tv_sec; ret *= 1000000; ret += tv.tv_usec; return ret; } int main() { static const unsigned char basepoint[32] = {9}; unsigned char mysecret[32], mypublic[32]; unsigned i; uint64_t start, end; memset(mysecret, 42, 32); mysecret[0] &= 248; mysecret[31] &= 127; mysecret[31] |= 64; // Load the caches for (i = 0; i < 1000; ++i) { curve25519_donna(mypublic, mysecret, basepoint); } start = time_now(); for (i = 0; i < 30000; ++i) { curve25519_donna(mypublic, mysecret, basepoint); } end = time_now(); printf("%luus\n", (unsigned long) ((end - start) / 30000)); return 0; } olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/test-curve25519.c000066400000000000000000000023261311755073500241750ustar00rootroot00000000000000/* test-curve25519 version 20050915 D. J. Bernstein Public domain. Tiny modifications by agl */ #include extern void curve25519_donna(unsigned char *output, const unsigned char *a, const unsigned char *b); void doit(unsigned char *ek,unsigned char *e,unsigned char *k); void doit(unsigned char *ek,unsigned char *e,unsigned char *k) { int i; for (i = 0;i < 32;++i) printf("%02x",(unsigned int) e[i]); printf(" "); for (i = 0;i < 32;++i) printf("%02x",(unsigned int) k[i]); printf(" "); curve25519_donna(ek,e,k); for (i = 0;i < 32;++i) printf("%02x",(unsigned int) ek[i]); printf("\n"); } unsigned char e1k[32]; unsigned char e2k[32]; unsigned char e1e2k[32]; unsigned char e2e1k[32]; unsigned char e1[32] = {3}; unsigned char e2[32] = {5}; unsigned char k[32] = {9}; int main() { int loop; int i; for (loop = 0;loop < 10000;++loop) { doit(e1k,e1,k); doit(e2e1k,e2,e1k); doit(e2k,e2,k); doit(e1e2k,e1,e2k); for (i = 0;i < 32;++i) if (e1e2k[i] != e2e1k[i]) { printf("fail\n"); return 1; } for (i = 0;i < 32;++i) e1[i] ^= e2k[i]; for (i = 0;i < 32;++i) e2[i] ^= e1k[i]; for (i = 0;i < 32;++i) k[i] ^= e1e2k[i]; } return 0; } olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/test-noncanon.c000066400000000000000000000022171311755073500242530ustar00rootroot00000000000000/* This file can be used to test whether the code handles non-canonical curve * points (i.e. points with the 256th bit set) in the same way as the reference * implementation. */ #include #include #include extern void curve25519_donna(unsigned char *output, const unsigned char *a, const unsigned char *b); int main() { static const uint8_t point1[32] = { 0x25,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; static const uint8_t point2[32] = { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, }; static const uint8_t scalar[32] = { 1 }; uint8_t out1[32], out2[32]; curve25519_donna(out1, scalar, point1); curve25519_donna(out2, scalar, point2); if (0 == memcmp(out1, out2, sizeof(out1))) { fprintf(stderr, "Top bit not ignored.\n"); return 1; } fprintf(stderr, "Top bit correctly ignored.\n"); return 0; } olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/test-sc-curve25519.c000066400000000000000000000035341311755073500246020ustar00rootroot00000000000000#define _GNU_SOURCE #include #include #include #include extern void curve25519_donna(uint8_t *, const uint8_t *, const uint8_t *); extern uint64_t tsc_read(); int main(int argc, char **argv) { uint8_t private_key[32], public[32], peer1[32], peer2[32], output[32]; static const uint8_t basepoint[32] = {9}; unsigned i; uint64_t sum = 0, sum_squares = 0, skipped = 0, mean; static const unsigned count = 200000; memset(private_key, 42, sizeof(private_key)); private_key[0] &= 248; private_key[31] &= 127; private_key[31] |= 64; curve25519_donna(public, private_key, basepoint); memset(peer1, 0, sizeof(peer1)); memset(peer2, 255, sizeof(peer2)); for (i = 0; i < count; ++i) { const uint64_t start = tsc_read(); curve25519_donna(output, peer1, public); const uint64_t end = tsc_read(); const uint64_t delta = end - start; if (delta > 650000) { // something terrible happened (task switch etc) skipped++; continue; } sum += delta; sum_squares += (delta * delta); } mean = sum / ((uint64_t) count); printf("all 0: mean:%lu sd:%f skipped:%lu\n", mean, sqrt((double)(sum_squares/((uint64_t) count) - mean*mean)), skipped); sum = sum_squares = skipped = 0; for (i = 0; i < count; ++i) { const uint64_t start = tsc_read(); curve25519_donna(output, peer2, public); const uint64_t end = tsc_read(); const uint64_t delta = end - start; if (delta > 650000) { // something terrible happened (task switch etc) skipped++; continue; } sum += delta; sum_squares += (delta * delta); } mean = sum / ((uint64_t) count); printf("all 1: mean:%lu sd:%f skipped:%lu\n", mean, sqrt((double)(sum_squares/((uint64_t) count) - mean*mean)), skipped); return 0; } olm-2.2.2+git20170526.0fd768e+dfsg/lib/curve25519-donna/test-sc-curve25519.s000066400000000000000000000001051311755073500246110ustar00rootroot00000000000000.text .globl tsc_read tsc_read: rdtsc shl $32,%rdx or %rdx,%rax ret olm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/000077500000000000000000000000001311755073500174665ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/readme.md000066400000000000000000000132321311755073500212460ustar00rootroot00000000000000Ed25519 ======= This is a portable implementation of [Ed25519](http://ed25519.cr.yp.to/) based on the SUPERCOP "ref10" implementation. Additionally there is key exchanging and scalar addition included to further aid building a PKI using Ed25519. All code is in the public domain. All code is pure ANSI C without any dependencies, except for the random seed generation which uses standard OS cryptography APIs (`CryptGenRandom` on Windows, `/dev/urandom` on nix). If you wish to be entirely portable define `ED25519_NO_SEED`. This disables the `ed25519_create_seed` function, so if your application requires key generation you must supply your own seeding function (which is simply a 256 bit (32 byte) cryptographic random number generator). Performance ----------- On a Windows machine with an Intel Pentium B970 @ 2.3GHz I got the following speeds (running on only one a single core): Seed generation: 64us (15625 per second) Key generation: 88us (11364 per second) Message signing (short message): 87us (11494 per second) Message verifying (short message): 228us (4386 per second) Scalar addition: 100us (10000 per second) Key exchange: 220us (4545 per second) The speeds on other machines may vary. Sign/verify times will be higher with longer messages. The implementation significantly benefits from 64 bit architectures, if possible compile as 64 bit. Usage ----- Simply add all .c and .h files in the `src/` folder to your project and include `ed25519.h` in any file you want to use the API. If you prefer to use a shared library, only copy `ed25519.h` and define `ED25519_DLL` before importing. A windows DLL is pre-built. There are no defined types for seeds, private keys, public keys, shared secrets or signatures. Instead simple `unsigned char` buffers are used with the following sizes: ```c unsigned char seed[32]; unsigned char signature[64]; unsigned char public_key[32]; unsigned char private_key[64]; unsigned char scalar[32]; unsigned char shared_secret[32]; ``` API --- ```c int ed25519_create_seed(unsigned char *seed); ``` Creates a 32 byte random seed in `seed` for key generation. `seed` must be a writable 32 byte buffer. Returns 0 on success, and nonzero on failure. ```c void ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed); ``` Creates a new key pair from the given seed. `public_key` must be a writable 32 byte buffer, `private_key` must be a writable 64 byte buffer and `seed` must be a 32 byte buffer. ```c void ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key); ``` Creates a signature of the given message with the given key pair. `signature` must be a writable 64 byte buffer. `message` must have at least `message_len` bytes to be read. ```c int ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key); ``` Verifies the signature on the given message using `public_key`. `signature` must be a readable 64 byte buffer. `message` must have at least `message_len` bytes to be read. Returns 1 if the signature matches, 0 otherwise. ```c void ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar); ``` Adds `scalar` to the given key pair where scalar is a 32 byte buffer (possibly generated with `ed25519_create_seed`), generating a new key pair. You can calculate the public key sum without knowing the private key and vice versa by passing in `NULL` for the key you don't know. This is useful for enforcing randomness on a key pair by a third party while only knowing the public key, among other things. Warning: the last bit of the scalar is ignored - if comparing scalars make sure to clear it with `scalar[31] &= 127`. ```c void ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key); ``` Performs a key exchange on the given public key and private key, producing a shared secret. It is recommended to hash the shared secret before using it. `shared_secret` must be a 32 byte writable buffer where the shared secret will be stored. Example ------- ```c unsigned char seed[32], public_key[32], private_key[64], signature[64]; unsigned char other_public_key[32], other_private_key[64], shared_secret[32]; const unsigned char message[] = "TEST MESSAGE"; /* create a random seed, and a key pair out of that seed */ if (ed25519_create_seed(seed)) { printf("error while generating seed\n"); exit(1); } ed25519_create_keypair(public_key, private_key, seed); /* create signature on the message with the key pair */ ed25519_sign(signature, message, strlen(message), public_key, private_key); /* verify the signature */ if (ed25519_verify(signature, message, strlen(message), public_key)) { printf("valid signature\n"); } else { printf("invalid signature\n"); } /* create a dummy keypair to use for a key exchange, normally you'd only have the public key and receive it through some communication channel */ if (ed25519_create_seed(seed)) { printf("error while generating seed\n"); exit(1); } ed25519_create_keypair(other_public_key, other_private_key, seed); /* do a key exchange with other_public_key */ ed25519_key_exchange(shared_secret, other_public_key, private_key); /* the magic here is that ed25519_key_exchange(shared_secret, public_key, other_private_key); would result in the same shared_secret */ ``` License ------- All code is in the public domain. olm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/src/000077500000000000000000000000001311755073500202555ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/src/add_scalar.c000066400000000000000000000033751311755073500225060ustar00rootroot00000000000000#include "ed25519.h" #include "ge.h" #include "sc.h" /* see http://crypto.stackexchange.com/a/6215/4697 */ void ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar) { const unsigned char SC_1[32] = {1}; /* scalar with value 1 */ unsigned char n[32]; ge_p3 nB; ge_p1p1 A_p1p1; ge_p3 A; ge_p3 public_key_unpacked; ge_cached T; int i; /* copy the scalar and clear highest bit */ for (i = 0; i < 31; ++i) { n[i] = scalar[i]; } n[31] = scalar[31] & 127; /* private key: a = n + t */ if (private_key) { sc_muladd(private_key, SC_1, n, private_key); } /* public key: A = nB + T */ if (public_key) { /* if we know the private key we don't need a point addition, which is faster */ /* using a "timing attack" you could find out wether or not we know the private key, but this information seems rather useless - if this is important pass public_key and private_key seperately in 2 function calls */ if (private_key) { ge_scalarmult_base(&A, private_key); } else { /* unpack public key into T */ ge_frombytes_negate_vartime(&public_key_unpacked, public_key); fe_neg(public_key_unpacked.X, public_key_unpacked.X); /* undo negate */ fe_neg(public_key_unpacked.T, public_key_unpacked.T); /* undo negate */ ge_p3_to_cached(&T, &public_key_unpacked); /* calculate n*B */ ge_scalarmult_base(&nB, n); /* A = n*B + T */ ge_add(&A_p1p1, &nB, &T); ge_p1p1_to_p3(&A, &A_p1p1); } /* pack public key */ ge_p3_tobytes(public_key, &A); } } olm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/src/ed25519.h000066400000000000000000000023431311755073500214260ustar00rootroot00000000000000#ifndef ED25519_H #define ED25519_H #include #if defined(_WIN32) #if defined(ED25519_BUILD_DLL) #define ED25519_DECLSPEC __declspec(dllexport) #elif defined(ED25519_DLL) #define ED25519_DECLSPEC __declspec(dllimport) #else #define ED25519_DECLSPEC #endif #else #define ED25519_DECLSPEC #endif #ifdef __cplusplus extern "C" { #endif #ifndef ED25519_NO_SEED int ED25519_DECLSPEC ed25519_create_seed(unsigned char *seed); #endif void ED25519_DECLSPEC ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed); void ED25519_DECLSPEC ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key); int ED25519_DECLSPEC ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key); void ED25519_DECLSPEC ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar); void ED25519_DECLSPEC ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key); #ifdef __cplusplus } #endif #endif olm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/src/fe.c000066400000000000000000001135451311755073500210240ustar00rootroot00000000000000#include "fixedint.h" #include "fe.h" #ifndef ED25519_LOAD_BYTES #define ED25519_LOAD_BYTES /* helper functions */ static uint64_t load_3(const unsigned char *in) { uint64_t result; result = (uint64_t) in[0]; result |= ((uint64_t) in[1]) << 8; result |= ((uint64_t) in[2]) << 16; return result; } static uint64_t load_4(const unsigned char *in) { uint64_t result; result = (uint64_t) in[0]; result |= ((uint64_t) in[1]) << 8; result |= ((uint64_t) in[2]) << 16; result |= ((uint64_t) in[3]) << 24; return result; } #endif /* h = 0 */ void fe_0(fe h) { h[0] = 0; h[1] = 0; h[2] = 0; h[3] = 0; h[4] = 0; h[5] = 0; h[6] = 0; h[7] = 0; h[8] = 0; h[9] = 0; } /* h = 1 */ void fe_1(fe h) { h[0] = 1; h[1] = 0; h[2] = 0; h[3] = 0; h[4] = 0; h[5] = 0; h[6] = 0; h[7] = 0; h[8] = 0; h[9] = 0; } /* h = f + g Can overlap h with f or g. Preconditions: |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. Postconditions: |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. */ void fe_add(fe h, const fe f, const fe g) { int32_t f0 = f[0]; int32_t f1 = f[1]; int32_t f2 = f[2]; int32_t f3 = f[3]; int32_t f4 = f[4]; int32_t f5 = f[5]; int32_t f6 = f[6]; int32_t f7 = f[7]; int32_t f8 = f[8]; int32_t f9 = f[9]; int32_t g0 = g[0]; int32_t g1 = g[1]; int32_t g2 = g[2]; int32_t g3 = g[3]; int32_t g4 = g[4]; int32_t g5 = g[5]; int32_t g6 = g[6]; int32_t g7 = g[7]; int32_t g8 = g[8]; int32_t g9 = g[9]; int32_t h0 = f0 + g0; int32_t h1 = f1 + g1; int32_t h2 = f2 + g2; int32_t h3 = f3 + g3; int32_t h4 = f4 + g4; int32_t h5 = f5 + g5; int32_t h6 = f6 + g6; int32_t h7 = f7 + g7; int32_t h8 = f8 + g8; int32_t h9 = f9 + g9; h[0] = h0; h[1] = h1; h[2] = h2; h[3] = h3; h[4] = h4; h[5] = h5; h[6] = h6; h[7] = h7; h[8] = h8; h[9] = h9; } /* Replace (f,g) with (g,g) if b == 1; replace (f,g) with (f,g) if b == 0. Preconditions: b in {0,1}. */ void fe_cmov(fe f, const fe g, unsigned int b) { int32_t f0 = f[0]; int32_t f1 = f[1]; int32_t f2 = f[2]; int32_t f3 = f[3]; int32_t f4 = f[4]; int32_t f5 = f[5]; int32_t f6 = f[6]; int32_t f7 = f[7]; int32_t f8 = f[8]; int32_t f9 = f[9]; int32_t g0 = g[0]; int32_t g1 = g[1]; int32_t g2 = g[2]; int32_t g3 = g[3]; int32_t g4 = g[4]; int32_t g5 = g[5]; int32_t g6 = g[6]; int32_t g7 = g[7]; int32_t g8 = g[8]; int32_t g9 = g[9]; int32_t x0 = f0 ^ g0; int32_t x1 = f1 ^ g1; int32_t x2 = f2 ^ g2; int32_t x3 = f3 ^ g3; int32_t x4 = f4 ^ g4; int32_t x5 = f5 ^ g5; int32_t x6 = f6 ^ g6; int32_t x7 = f7 ^ g7; int32_t x8 = f8 ^ g8; int32_t x9 = f9 ^ g9; b = (unsigned int) (- (int) b); /* silence warning */ x0 &= b; x1 &= b; x2 &= b; x3 &= b; x4 &= b; x5 &= b; x6 &= b; x7 &= b; x8 &= b; x9 &= b; f[0] = f0 ^ x0; f[1] = f1 ^ x1; f[2] = f2 ^ x2; f[3] = f3 ^ x3; f[4] = f4 ^ x4; f[5] = f5 ^ x5; f[6] = f6 ^ x6; f[7] = f7 ^ x7; f[8] = f8 ^ x8; f[9] = f9 ^ x9; } /* Replace (f,g) with (g,f) if b == 1; replace (f,g) with (f,g) if b == 0. Preconditions: b in {0,1}. */ void fe_cswap(fe f,fe g,unsigned int b) { int32_t f0 = f[0]; int32_t f1 = f[1]; int32_t f2 = f[2]; int32_t f3 = f[3]; int32_t f4 = f[4]; int32_t f5 = f[5]; int32_t f6 = f[6]; int32_t f7 = f[7]; int32_t f8 = f[8]; int32_t f9 = f[9]; int32_t g0 = g[0]; int32_t g1 = g[1]; int32_t g2 = g[2]; int32_t g3 = g[3]; int32_t g4 = g[4]; int32_t g5 = g[5]; int32_t g6 = g[6]; int32_t g7 = g[7]; int32_t g8 = g[8]; int32_t g9 = g[9]; int32_t x0 = f0 ^ g0; int32_t x1 = f1 ^ g1; int32_t x2 = f2 ^ g2; int32_t x3 = f3 ^ g3; int32_t x4 = f4 ^ g4; int32_t x5 = f5 ^ g5; int32_t x6 = f6 ^ g6; int32_t x7 = f7 ^ g7; int32_t x8 = f8 ^ g8; int32_t x9 = f9 ^ g9; b = -b; x0 &= b; x1 &= b; x2 &= b; x3 &= b; x4 &= b; x5 &= b; x6 &= b; x7 &= b; x8 &= b; x9 &= b; f[0] = f0 ^ x0; f[1] = f1 ^ x1; f[2] = f2 ^ x2; f[3] = f3 ^ x3; f[4] = f4 ^ x4; f[5] = f5 ^ x5; f[6] = f6 ^ x6; f[7] = f7 ^ x7; f[8] = f8 ^ x8; f[9] = f9 ^ x9; g[0] = g0 ^ x0; g[1] = g1 ^ x1; g[2] = g2 ^ x2; g[3] = g3 ^ x3; g[4] = g4 ^ x4; g[5] = g5 ^ x5; g[6] = g6 ^ x6; g[7] = g7 ^ x7; g[8] = g8 ^ x8; g[9] = g9 ^ x9; } /* h = f */ void fe_copy(fe h, const fe f) { int32_t f0 = f[0]; int32_t f1 = f[1]; int32_t f2 = f[2]; int32_t f3 = f[3]; int32_t f4 = f[4]; int32_t f5 = f[5]; int32_t f6 = f[6]; int32_t f7 = f[7]; int32_t f8 = f[8]; int32_t f9 = f[9]; h[0] = f0; h[1] = f1; h[2] = f2; h[3] = f3; h[4] = f4; h[5] = f5; h[6] = f6; h[7] = f7; h[8] = f8; h[9] = f9; } /* Ignores top bit of h. */ void fe_frombytes(fe h, const unsigned char *s) { int64_t h0 = load_4(s); int64_t h1 = load_3(s + 4) << 6; int64_t h2 = load_3(s + 7) << 5; int64_t h3 = load_3(s + 10) << 3; int64_t h4 = load_3(s + 13) << 2; int64_t h5 = load_4(s + 16); int64_t h6 = load_3(s + 20) << 7; int64_t h7 = load_3(s + 23) << 5; int64_t h8 = load_3(s + 26) << 4; int64_t h9 = (load_3(s + 29) & 8388607) << 2; int64_t carry0; int64_t carry1; int64_t carry2; int64_t carry3; int64_t carry4; int64_t carry5; int64_t carry6; int64_t carry7; int64_t carry8; int64_t carry9; carry9 = (h9 + (int64_t) (1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; carry1 = (h1 + (int64_t) (1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; carry3 = (h3 + (int64_t) (1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; carry5 = (h5 + (int64_t) (1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; carry7 = (h7 + (int64_t) (1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; carry0 = (h0 + (int64_t) (1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; carry2 = (h2 + (int64_t) (1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; carry4 = (h4 + (int64_t) (1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; carry6 = (h6 + (int64_t) (1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; carry8 = (h8 + (int64_t) (1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26; h[0] = (int32_t) h0; h[1] = (int32_t) h1; h[2] = (int32_t) h2; h[3] = (int32_t) h3; h[4] = (int32_t) h4; h[5] = (int32_t) h5; h[6] = (int32_t) h6; h[7] = (int32_t) h7; h[8] = (int32_t) h8; h[9] = (int32_t) h9; } void fe_invert(fe out, const fe z) { fe t0; fe t1; fe t2; fe t3; int i; fe_sq(t0, z); for (i = 1; i < 1; ++i) { fe_sq(t0, t0); } fe_sq(t1, t0); for (i = 1; i < 2; ++i) { fe_sq(t1, t1); } fe_mul(t1, z, t1); fe_mul(t0, t0, t1); fe_sq(t2, t0); for (i = 1; i < 1; ++i) { fe_sq(t2, t2); } fe_mul(t1, t1, t2); fe_sq(t2, t1); for (i = 1; i < 5; ++i) { fe_sq(t2, t2); } fe_mul(t1, t2, t1); fe_sq(t2, t1); for (i = 1; i < 10; ++i) { fe_sq(t2, t2); } fe_mul(t2, t2, t1); fe_sq(t3, t2); for (i = 1; i < 20; ++i) { fe_sq(t3, t3); } fe_mul(t2, t3, t2); fe_sq(t2, t2); for (i = 1; i < 10; ++i) { fe_sq(t2, t2); } fe_mul(t1, t2, t1); fe_sq(t2, t1); for (i = 1; i < 50; ++i) { fe_sq(t2, t2); } fe_mul(t2, t2, t1); fe_sq(t3, t2); for (i = 1; i < 100; ++i) { fe_sq(t3, t3); } fe_mul(t2, t3, t2); fe_sq(t2, t2); for (i = 1; i < 50; ++i) { fe_sq(t2, t2); } fe_mul(t1, t2, t1); fe_sq(t1, t1); for (i = 1; i < 5; ++i) { fe_sq(t1, t1); } fe_mul(out, t1, t0); } /* return 1 if f is in {1,3,5,...,q-2} return 0 if f is in {0,2,4,...,q-1} Preconditions: |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. */ int fe_isnegative(const fe f) { unsigned char s[32]; fe_tobytes(s, f); return s[0] & 1; } /* return 1 if f == 0 return 0 if f != 0 Preconditions: |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. */ int fe_isnonzero(const fe f) { unsigned char s[32]; unsigned char r; fe_tobytes(s, f); r = s[0]; #define F(i) r |= s[i] F(1); F(2); F(3); F(4); F(5); F(6); F(7); F(8); F(9); F(10); F(11); F(12); F(13); F(14); F(15); F(16); F(17); F(18); F(19); F(20); F(21); F(22); F(23); F(24); F(25); F(26); F(27); F(28); F(29); F(30); F(31); #undef F return r != 0; } /* h = f * g Can overlap h with f or g. Preconditions: |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. |g| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. Postconditions: |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. */ /* Notes on implementation strategy: Using schoolbook multiplication. Karatsuba would save a little in some cost models. Most multiplications by 2 and 19 are 32-bit precomputations; cheaper than 64-bit postcomputations. There is one remaining multiplication by 19 in the carry chain; one *19 precomputation can be merged into this, but the resulting data flow is considerably less clean. There are 12 carries below. 10 of them are 2-way parallelizable and vectorizable. Can get away with 11 carries, but then data flow is much deeper. With tighter constraints on inputs can squeeze carries into int32. */ void fe_mul(fe h, const fe f, const fe g) { int32_t f0 = f[0]; int32_t f1 = f[1]; int32_t f2 = f[2]; int32_t f3 = f[3]; int32_t f4 = f[4]; int32_t f5 = f[5]; int32_t f6 = f[6]; int32_t f7 = f[7]; int32_t f8 = f[8]; int32_t f9 = f[9]; int32_t g0 = g[0]; int32_t g1 = g[1]; int32_t g2 = g[2]; int32_t g3 = g[3]; int32_t g4 = g[4]; int32_t g5 = g[5]; int32_t g6 = g[6]; int32_t g7 = g[7]; int32_t g8 = g[8]; int32_t g9 = g[9]; int32_t g1_19 = 19 * g1; /* 1.959375*2^29 */ int32_t g2_19 = 19 * g2; /* 1.959375*2^30; still ok */ int32_t g3_19 = 19 * g3; int32_t g4_19 = 19 * g4; int32_t g5_19 = 19 * g5; int32_t g6_19 = 19 * g6; int32_t g7_19 = 19 * g7; int32_t g8_19 = 19 * g8; int32_t g9_19 = 19 * g9; int32_t f1_2 = 2 * f1; int32_t f3_2 = 2 * f3; int32_t f5_2 = 2 * f5; int32_t f7_2 = 2 * f7; int32_t f9_2 = 2 * f9; int64_t f0g0 = f0 * (int64_t) g0; int64_t f0g1 = f0 * (int64_t) g1; int64_t f0g2 = f0 * (int64_t) g2; int64_t f0g3 = f0 * (int64_t) g3; int64_t f0g4 = f0 * (int64_t) g4; int64_t f0g5 = f0 * (int64_t) g5; int64_t f0g6 = f0 * (int64_t) g6; int64_t f0g7 = f0 * (int64_t) g7; int64_t f0g8 = f0 * (int64_t) g8; int64_t f0g9 = f0 * (int64_t) g9; int64_t f1g0 = f1 * (int64_t) g0; int64_t f1g1_2 = f1_2 * (int64_t) g1; int64_t f1g2 = f1 * (int64_t) g2; int64_t f1g3_2 = f1_2 * (int64_t) g3; int64_t f1g4 = f1 * (int64_t) g4; int64_t f1g5_2 = f1_2 * (int64_t) g5; int64_t f1g6 = f1 * (int64_t) g6; int64_t f1g7_2 = f1_2 * (int64_t) g7; int64_t f1g8 = f1 * (int64_t) g8; int64_t f1g9_38 = f1_2 * (int64_t) g9_19; int64_t f2g0 = f2 * (int64_t) g0; int64_t f2g1 = f2 * (int64_t) g1; int64_t f2g2 = f2 * (int64_t) g2; int64_t f2g3 = f2 * (int64_t) g3; int64_t f2g4 = f2 * (int64_t) g4; int64_t f2g5 = f2 * (int64_t) g5; int64_t f2g6 = f2 * (int64_t) g6; int64_t f2g7 = f2 * (int64_t) g7; int64_t f2g8_19 = f2 * (int64_t) g8_19; int64_t f2g9_19 = f2 * (int64_t) g9_19; int64_t f3g0 = f3 * (int64_t) g0; int64_t f3g1_2 = f3_2 * (int64_t) g1; int64_t f3g2 = f3 * (int64_t) g2; int64_t f3g3_2 = f3_2 * (int64_t) g3; int64_t f3g4 = f3 * (int64_t) g4; int64_t f3g5_2 = f3_2 * (int64_t) g5; int64_t f3g6 = f3 * (int64_t) g6; int64_t f3g7_38 = f3_2 * (int64_t) g7_19; int64_t f3g8_19 = f3 * (int64_t) g8_19; int64_t f3g9_38 = f3_2 * (int64_t) g9_19; int64_t f4g0 = f4 * (int64_t) g0; int64_t f4g1 = f4 * (int64_t) g1; int64_t f4g2 = f4 * (int64_t) g2; int64_t f4g3 = f4 * (int64_t) g3; int64_t f4g4 = f4 * (int64_t) g4; int64_t f4g5 = f4 * (int64_t) g5; int64_t f4g6_19 = f4 * (int64_t) g6_19; int64_t f4g7_19 = f4 * (int64_t) g7_19; int64_t f4g8_19 = f4 * (int64_t) g8_19; int64_t f4g9_19 = f4 * (int64_t) g9_19; int64_t f5g0 = f5 * (int64_t) g0; int64_t f5g1_2 = f5_2 * (int64_t) g1; int64_t f5g2 = f5 * (int64_t) g2; int64_t f5g3_2 = f5_2 * (int64_t) g3; int64_t f5g4 = f5 * (int64_t) g4; int64_t f5g5_38 = f5_2 * (int64_t) g5_19; int64_t f5g6_19 = f5 * (int64_t) g6_19; int64_t f5g7_38 = f5_2 * (int64_t) g7_19; int64_t f5g8_19 = f5 * (int64_t) g8_19; int64_t f5g9_38 = f5_2 * (int64_t) g9_19; int64_t f6g0 = f6 * (int64_t) g0; int64_t f6g1 = f6 * (int64_t) g1; int64_t f6g2 = f6 * (int64_t) g2; int64_t f6g3 = f6 * (int64_t) g3; int64_t f6g4_19 = f6 * (int64_t) g4_19; int64_t f6g5_19 = f6 * (int64_t) g5_19; int64_t f6g6_19 = f6 * (int64_t) g6_19; int64_t f6g7_19 = f6 * (int64_t) g7_19; int64_t f6g8_19 = f6 * (int64_t) g8_19; int64_t f6g9_19 = f6 * (int64_t) g9_19; int64_t f7g0 = f7 * (int64_t) g0; int64_t f7g1_2 = f7_2 * (int64_t) g1; int64_t f7g2 = f7 * (int64_t) g2; int64_t f7g3_38 = f7_2 * (int64_t) g3_19; int64_t f7g4_19 = f7 * (int64_t) g4_19; int64_t f7g5_38 = f7_2 * (int64_t) g5_19; int64_t f7g6_19 = f7 * (int64_t) g6_19; int64_t f7g7_38 = f7_2 * (int64_t) g7_19; int64_t f7g8_19 = f7 * (int64_t) g8_19; int64_t f7g9_38 = f7_2 * (int64_t) g9_19; int64_t f8g0 = f8 * (int64_t) g0; int64_t f8g1 = f8 * (int64_t) g1; int64_t f8g2_19 = f8 * (int64_t) g2_19; int64_t f8g3_19 = f8 * (int64_t) g3_19; int64_t f8g4_19 = f8 * (int64_t) g4_19; int64_t f8g5_19 = f8 * (int64_t) g5_19; int64_t f8g6_19 = f8 * (int64_t) g6_19; int64_t f8g7_19 = f8 * (int64_t) g7_19; int64_t f8g8_19 = f8 * (int64_t) g8_19; int64_t f8g9_19 = f8 * (int64_t) g9_19; int64_t f9g0 = f9 * (int64_t) g0; int64_t f9g1_38 = f9_2 * (int64_t) g1_19; int64_t f9g2_19 = f9 * (int64_t) g2_19; int64_t f9g3_38 = f9_2 * (int64_t) g3_19; int64_t f9g4_19 = f9 * (int64_t) g4_19; int64_t f9g5_38 = f9_2 * (int64_t) g5_19; int64_t f9g6_19 = f9 * (int64_t) g6_19; int64_t f9g7_38 = f9_2 * (int64_t) g7_19; int64_t f9g8_19 = f9 * (int64_t) g8_19; int64_t f9g9_38 = f9_2 * (int64_t) g9_19; int64_t h0 = f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38; int64_t h1 = f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19; int64_t h2 = f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38; int64_t h3 = f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19; int64_t h4 = f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38; int64_t h5 = f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19; int64_t h6 = f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38; int64_t h7 = f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19; int64_t h8 = f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38; int64_t h9 = f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0 ; int64_t carry0; int64_t carry1; int64_t carry2; int64_t carry3; int64_t carry4; int64_t carry5; int64_t carry6; int64_t carry7; int64_t carry8; int64_t carry9; carry0 = (h0 + (int64_t) (1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; carry4 = (h4 + (int64_t) (1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; carry1 = (h1 + (int64_t) (1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; carry5 = (h5 + (int64_t) (1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; carry2 = (h2 + (int64_t) (1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; carry6 = (h6 + (int64_t) (1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; carry3 = (h3 + (int64_t) (1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; carry7 = (h7 + (int64_t) (1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; carry4 = (h4 + (int64_t) (1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; carry8 = (h8 + (int64_t) (1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26; carry9 = (h9 + (int64_t) (1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; carry0 = (h0 + (int64_t) (1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; h[0] = (int32_t) h0; h[1] = (int32_t) h1; h[2] = (int32_t) h2; h[3] = (int32_t) h3; h[4] = (int32_t) h4; h[5] = (int32_t) h5; h[6] = (int32_t) h6; h[7] = (int32_t) h7; h[8] = (int32_t) h8; h[9] = (int32_t) h9; } /* h = f * 121666 Can overlap h with f. Preconditions: |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. Postconditions: |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. */ void fe_mul121666(fe h, fe f) { int32_t f0 = f[0]; int32_t f1 = f[1]; int32_t f2 = f[2]; int32_t f3 = f[3]; int32_t f4 = f[4]; int32_t f5 = f[5]; int32_t f6 = f[6]; int32_t f7 = f[7]; int32_t f8 = f[8]; int32_t f9 = f[9]; int64_t h0 = f0 * (int64_t) 121666; int64_t h1 = f1 * (int64_t) 121666; int64_t h2 = f2 * (int64_t) 121666; int64_t h3 = f3 * (int64_t) 121666; int64_t h4 = f4 * (int64_t) 121666; int64_t h5 = f5 * (int64_t) 121666; int64_t h6 = f6 * (int64_t) 121666; int64_t h7 = f7 * (int64_t) 121666; int64_t h8 = f8 * (int64_t) 121666; int64_t h9 = f9 * (int64_t) 121666; int64_t carry0; int64_t carry1; int64_t carry2; int64_t carry3; int64_t carry4; int64_t carry5; int64_t carry6; int64_t carry7; int64_t carry8; int64_t carry9; carry9 = (h9 + (int64_t) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; carry1 = (h1 + (int64_t) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; carry3 = (h3 + (int64_t) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; carry5 = (h5 + (int64_t) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; carry7 = (h7 + (int64_t) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; carry2 = (h2 + (int64_t) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; carry4 = (h4 + (int64_t) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; carry6 = (h6 + (int64_t) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; carry8 = (h8 + (int64_t) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; h[0] = h0; h[1] = h1; h[2] = h2; h[3] = h3; h[4] = h4; h[5] = h5; h[6] = h6; h[7] = h7; h[8] = h8; h[9] = h9; } /* h = -f Preconditions: |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. Postconditions: |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. */ void fe_neg(fe h, const fe f) { int32_t f0 = f[0]; int32_t f1 = f[1]; int32_t f2 = f[2]; int32_t f3 = f[3]; int32_t f4 = f[4]; int32_t f5 = f[5]; int32_t f6 = f[6]; int32_t f7 = f[7]; int32_t f8 = f[8]; int32_t f9 = f[9]; int32_t h0 = -f0; int32_t h1 = -f1; int32_t h2 = -f2; int32_t h3 = -f3; int32_t h4 = -f4; int32_t h5 = -f5; int32_t h6 = -f6; int32_t h7 = -f7; int32_t h8 = -f8; int32_t h9 = -f9; h[0] = h0; h[1] = h1; h[2] = h2; h[3] = h3; h[4] = h4; h[5] = h5; h[6] = h6; h[7] = h7; h[8] = h8; h[9] = h9; } void fe_pow22523(fe out, const fe z) { fe t0; fe t1; fe t2; int i; fe_sq(t0, z); for (i = 1; i < 1; ++i) { fe_sq(t0, t0); } fe_sq(t1, t0); for (i = 1; i < 2; ++i) { fe_sq(t1, t1); } fe_mul(t1, z, t1); fe_mul(t0, t0, t1); fe_sq(t0, t0); for (i = 1; i < 1; ++i) { fe_sq(t0, t0); } fe_mul(t0, t1, t0); fe_sq(t1, t0); for (i = 1; i < 5; ++i) { fe_sq(t1, t1); } fe_mul(t0, t1, t0); fe_sq(t1, t0); for (i = 1; i < 10; ++i) { fe_sq(t1, t1); } fe_mul(t1, t1, t0); fe_sq(t2, t1); for (i = 1; i < 20; ++i) { fe_sq(t2, t2); } fe_mul(t1, t2, t1); fe_sq(t1, t1); for (i = 1; i < 10; ++i) { fe_sq(t1, t1); } fe_mul(t0, t1, t0); fe_sq(t1, t0); for (i = 1; i < 50; ++i) { fe_sq(t1, t1); } fe_mul(t1, t1, t0); fe_sq(t2, t1); for (i = 1; i < 100; ++i) { fe_sq(t2, t2); } fe_mul(t1, t2, t1); fe_sq(t1, t1); for (i = 1; i < 50; ++i) { fe_sq(t1, t1); } fe_mul(t0, t1, t0); fe_sq(t0, t0); for (i = 1; i < 2; ++i) { fe_sq(t0, t0); } fe_mul(out, t0, z); return; } /* h = f * f Can overlap h with f. Preconditions: |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. Postconditions: |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. */ /* See fe_mul.c for discussion of implementation strategy. */ void fe_sq(fe h, const fe f) { int32_t f0 = f[0]; int32_t f1 = f[1]; int32_t f2 = f[2]; int32_t f3 = f[3]; int32_t f4 = f[4]; int32_t f5 = f[5]; int32_t f6 = f[6]; int32_t f7 = f[7]; int32_t f8 = f[8]; int32_t f9 = f[9]; int32_t f0_2 = 2 * f0; int32_t f1_2 = 2 * f1; int32_t f2_2 = 2 * f2; int32_t f3_2 = 2 * f3; int32_t f4_2 = 2 * f4; int32_t f5_2 = 2 * f5; int32_t f6_2 = 2 * f6; int32_t f7_2 = 2 * f7; int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */ int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */ int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */ int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */ int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */ int64_t f0f0 = f0 * (int64_t) f0; int64_t f0f1_2 = f0_2 * (int64_t) f1; int64_t f0f2_2 = f0_2 * (int64_t) f2; int64_t f0f3_2 = f0_2 * (int64_t) f3; int64_t f0f4_2 = f0_2 * (int64_t) f4; int64_t f0f5_2 = f0_2 * (int64_t) f5; int64_t f0f6_2 = f0_2 * (int64_t) f6; int64_t f0f7_2 = f0_2 * (int64_t) f7; int64_t f0f8_2 = f0_2 * (int64_t) f8; int64_t f0f9_2 = f0_2 * (int64_t) f9; int64_t f1f1_2 = f1_2 * (int64_t) f1; int64_t f1f2_2 = f1_2 * (int64_t) f2; int64_t f1f3_4 = f1_2 * (int64_t) f3_2; int64_t f1f4_2 = f1_2 * (int64_t) f4; int64_t f1f5_4 = f1_2 * (int64_t) f5_2; int64_t f1f6_2 = f1_2 * (int64_t) f6; int64_t f1f7_4 = f1_2 * (int64_t) f7_2; int64_t f1f8_2 = f1_2 * (int64_t) f8; int64_t f1f9_76 = f1_2 * (int64_t) f9_38; int64_t f2f2 = f2 * (int64_t) f2; int64_t f2f3_2 = f2_2 * (int64_t) f3; int64_t f2f4_2 = f2_2 * (int64_t) f4; int64_t f2f5_2 = f2_2 * (int64_t) f5; int64_t f2f6_2 = f2_2 * (int64_t) f6; int64_t f2f7_2 = f2_2 * (int64_t) f7; int64_t f2f8_38 = f2_2 * (int64_t) f8_19; int64_t f2f9_38 = f2 * (int64_t) f9_38; int64_t f3f3_2 = f3_2 * (int64_t) f3; int64_t f3f4_2 = f3_2 * (int64_t) f4; int64_t f3f5_4 = f3_2 * (int64_t) f5_2; int64_t f3f6_2 = f3_2 * (int64_t) f6; int64_t f3f7_76 = f3_2 * (int64_t) f7_38; int64_t f3f8_38 = f3_2 * (int64_t) f8_19; int64_t f3f9_76 = f3_2 * (int64_t) f9_38; int64_t f4f4 = f4 * (int64_t) f4; int64_t f4f5_2 = f4_2 * (int64_t) f5; int64_t f4f6_38 = f4_2 * (int64_t) f6_19; int64_t f4f7_38 = f4 * (int64_t) f7_38; int64_t f4f8_38 = f4_2 * (int64_t) f8_19; int64_t f4f9_38 = f4 * (int64_t) f9_38; int64_t f5f5_38 = f5 * (int64_t) f5_38; int64_t f5f6_38 = f5_2 * (int64_t) f6_19; int64_t f5f7_76 = f5_2 * (int64_t) f7_38; int64_t f5f8_38 = f5_2 * (int64_t) f8_19; int64_t f5f9_76 = f5_2 * (int64_t) f9_38; int64_t f6f6_19 = f6 * (int64_t) f6_19; int64_t f6f7_38 = f6 * (int64_t) f7_38; int64_t f6f8_38 = f6_2 * (int64_t) f8_19; int64_t f6f9_38 = f6 * (int64_t) f9_38; int64_t f7f7_38 = f7 * (int64_t) f7_38; int64_t f7f8_38 = f7_2 * (int64_t) f8_19; int64_t f7f9_76 = f7_2 * (int64_t) f9_38; int64_t f8f8_19 = f8 * (int64_t) f8_19; int64_t f8f9_38 = f8 * (int64_t) f9_38; int64_t f9f9_38 = f9 * (int64_t) f9_38; int64_t h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38; int64_t h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38; int64_t h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19; int64_t h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38; int64_t h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38; int64_t h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38; int64_t h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19; int64_t h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38; int64_t h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38; int64_t h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2; int64_t carry0; int64_t carry1; int64_t carry2; int64_t carry3; int64_t carry4; int64_t carry5; int64_t carry6; int64_t carry7; int64_t carry8; int64_t carry9; carry0 = (h0 + (int64_t) (1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; carry4 = (h4 + (int64_t) (1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; carry1 = (h1 + (int64_t) (1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; carry5 = (h5 + (int64_t) (1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; carry2 = (h2 + (int64_t) (1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; carry6 = (h6 + (int64_t) (1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; carry3 = (h3 + (int64_t) (1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; carry7 = (h7 + (int64_t) (1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; carry4 = (h4 + (int64_t) (1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; carry8 = (h8 + (int64_t) (1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26; carry9 = (h9 + (int64_t) (1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; carry0 = (h0 + (int64_t) (1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; h[0] = (int32_t) h0; h[1] = (int32_t) h1; h[2] = (int32_t) h2; h[3] = (int32_t) h3; h[4] = (int32_t) h4; h[5] = (int32_t) h5; h[6] = (int32_t) h6; h[7] = (int32_t) h7; h[8] = (int32_t) h8; h[9] = (int32_t) h9; } /* h = 2 * f * f Can overlap h with f. Preconditions: |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. Postconditions: |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. */ /* See fe_mul.c for discussion of implementation strategy. */ void fe_sq2(fe h, const fe f) { int32_t f0 = f[0]; int32_t f1 = f[1]; int32_t f2 = f[2]; int32_t f3 = f[3]; int32_t f4 = f[4]; int32_t f5 = f[5]; int32_t f6 = f[6]; int32_t f7 = f[7]; int32_t f8 = f[8]; int32_t f9 = f[9]; int32_t f0_2 = 2 * f0; int32_t f1_2 = 2 * f1; int32_t f2_2 = 2 * f2; int32_t f3_2 = 2 * f3; int32_t f4_2 = 2 * f4; int32_t f5_2 = 2 * f5; int32_t f6_2 = 2 * f6; int32_t f7_2 = 2 * f7; int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */ int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */ int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */ int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */ int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */ int64_t f0f0 = f0 * (int64_t) f0; int64_t f0f1_2 = f0_2 * (int64_t) f1; int64_t f0f2_2 = f0_2 * (int64_t) f2; int64_t f0f3_2 = f0_2 * (int64_t) f3; int64_t f0f4_2 = f0_2 * (int64_t) f4; int64_t f0f5_2 = f0_2 * (int64_t) f5; int64_t f0f6_2 = f0_2 * (int64_t) f6; int64_t f0f7_2 = f0_2 * (int64_t) f7; int64_t f0f8_2 = f0_2 * (int64_t) f8; int64_t f0f9_2 = f0_2 * (int64_t) f9; int64_t f1f1_2 = f1_2 * (int64_t) f1; int64_t f1f2_2 = f1_2 * (int64_t) f2; int64_t f1f3_4 = f1_2 * (int64_t) f3_2; int64_t f1f4_2 = f1_2 * (int64_t) f4; int64_t f1f5_4 = f1_2 * (int64_t) f5_2; int64_t f1f6_2 = f1_2 * (int64_t) f6; int64_t f1f7_4 = f1_2 * (int64_t) f7_2; int64_t f1f8_2 = f1_2 * (int64_t) f8; int64_t f1f9_76 = f1_2 * (int64_t) f9_38; int64_t f2f2 = f2 * (int64_t) f2; int64_t f2f3_2 = f2_2 * (int64_t) f3; int64_t f2f4_2 = f2_2 * (int64_t) f4; int64_t f2f5_2 = f2_2 * (int64_t) f5; int64_t f2f6_2 = f2_2 * (int64_t) f6; int64_t f2f7_2 = f2_2 * (int64_t) f7; int64_t f2f8_38 = f2_2 * (int64_t) f8_19; int64_t f2f9_38 = f2 * (int64_t) f9_38; int64_t f3f3_2 = f3_2 * (int64_t) f3; int64_t f3f4_2 = f3_2 * (int64_t) f4; int64_t f3f5_4 = f3_2 * (int64_t) f5_2; int64_t f3f6_2 = f3_2 * (int64_t) f6; int64_t f3f7_76 = f3_2 * (int64_t) f7_38; int64_t f3f8_38 = f3_2 * (int64_t) f8_19; int64_t f3f9_76 = f3_2 * (int64_t) f9_38; int64_t f4f4 = f4 * (int64_t) f4; int64_t f4f5_2 = f4_2 * (int64_t) f5; int64_t f4f6_38 = f4_2 * (int64_t) f6_19; int64_t f4f7_38 = f4 * (int64_t) f7_38; int64_t f4f8_38 = f4_2 * (int64_t) f8_19; int64_t f4f9_38 = f4 * (int64_t) f9_38; int64_t f5f5_38 = f5 * (int64_t) f5_38; int64_t f5f6_38 = f5_2 * (int64_t) f6_19; int64_t f5f7_76 = f5_2 * (int64_t) f7_38; int64_t f5f8_38 = f5_2 * (int64_t) f8_19; int64_t f5f9_76 = f5_2 * (int64_t) f9_38; int64_t f6f6_19 = f6 * (int64_t) f6_19; int64_t f6f7_38 = f6 * (int64_t) f7_38; int64_t f6f8_38 = f6_2 * (int64_t) f8_19; int64_t f6f9_38 = f6 * (int64_t) f9_38; int64_t f7f7_38 = f7 * (int64_t) f7_38; int64_t f7f8_38 = f7_2 * (int64_t) f8_19; int64_t f7f9_76 = f7_2 * (int64_t) f9_38; int64_t f8f8_19 = f8 * (int64_t) f8_19; int64_t f8f9_38 = f8 * (int64_t) f9_38; int64_t f9f9_38 = f9 * (int64_t) f9_38; int64_t h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38; int64_t h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38; int64_t h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19; int64_t h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38; int64_t h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38; int64_t h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38; int64_t h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19; int64_t h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38; int64_t h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38; int64_t h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2; int64_t carry0; int64_t carry1; int64_t carry2; int64_t carry3; int64_t carry4; int64_t carry5; int64_t carry6; int64_t carry7; int64_t carry8; int64_t carry9; h0 += h0; h1 += h1; h2 += h2; h3 += h3; h4 += h4; h5 += h5; h6 += h6; h7 += h7; h8 += h8; h9 += h9; carry0 = (h0 + (int64_t) (1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; carry4 = (h4 + (int64_t) (1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; carry1 = (h1 + (int64_t) (1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; carry5 = (h5 + (int64_t) (1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; carry2 = (h2 + (int64_t) (1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; carry6 = (h6 + (int64_t) (1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; carry3 = (h3 + (int64_t) (1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; carry7 = (h7 + (int64_t) (1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; carry4 = (h4 + (int64_t) (1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; carry8 = (h8 + (int64_t) (1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26; carry9 = (h9 + (int64_t) (1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; carry0 = (h0 + (int64_t) (1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; h[0] = (int32_t) h0; h[1] = (int32_t) h1; h[2] = (int32_t) h2; h[3] = (int32_t) h3; h[4] = (int32_t) h4; h[5] = (int32_t) h5; h[6] = (int32_t) h6; h[7] = (int32_t) h7; h[8] = (int32_t) h8; h[9] = (int32_t) h9; } /* h = f - g Can overlap h with f or g. Preconditions: |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. Postconditions: |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. */ void fe_sub(fe h, const fe f, const fe g) { int32_t f0 = f[0]; int32_t f1 = f[1]; int32_t f2 = f[2]; int32_t f3 = f[3]; int32_t f4 = f[4]; int32_t f5 = f[5]; int32_t f6 = f[6]; int32_t f7 = f[7]; int32_t f8 = f[8]; int32_t f9 = f[9]; int32_t g0 = g[0]; int32_t g1 = g[1]; int32_t g2 = g[2]; int32_t g3 = g[3]; int32_t g4 = g[4]; int32_t g5 = g[5]; int32_t g6 = g[6]; int32_t g7 = g[7]; int32_t g8 = g[8]; int32_t g9 = g[9]; int32_t h0 = f0 - g0; int32_t h1 = f1 - g1; int32_t h2 = f2 - g2; int32_t h3 = f3 - g3; int32_t h4 = f4 - g4; int32_t h5 = f5 - g5; int32_t h6 = f6 - g6; int32_t h7 = f7 - g7; int32_t h8 = f8 - g8; int32_t h9 = f9 - g9; h[0] = h0; h[1] = h1; h[2] = h2; h[3] = h3; h[4] = h4; h[5] = h5; h[6] = h6; h[7] = h7; h[8] = h8; h[9] = h9; } /* Preconditions: |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. Write p=2^255-19; q=floor(h/p). Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). Proof: Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4. Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). Then 0> 25; q = (h0 + q) >> 26; q = (h1 + q) >> 25; q = (h2 + q) >> 26; q = (h3 + q) >> 25; q = (h4 + q) >> 26; q = (h5 + q) >> 25; q = (h6 + q) >> 26; q = (h7 + q) >> 25; q = (h8 + q) >> 26; q = (h9 + q) >> 25; /* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */ h0 += 19 * q; /* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */ carry0 = h0 >> 26; h1 += carry0; h0 -= carry0 << 26; carry1 = h1 >> 25; h2 += carry1; h1 -= carry1 << 25; carry2 = h2 >> 26; h3 += carry2; h2 -= carry2 << 26; carry3 = h3 >> 25; h4 += carry3; h3 -= carry3 << 25; carry4 = h4 >> 26; h5 += carry4; h4 -= carry4 << 26; carry5 = h5 >> 25; h6 += carry5; h5 -= carry5 << 25; carry6 = h6 >> 26; h7 += carry6; h6 -= carry6 << 26; carry7 = h7 >> 25; h8 += carry7; h7 -= carry7 << 25; carry8 = h8 >> 26; h9 += carry8; h8 -= carry8 << 26; carry9 = h9 >> 25; h9 -= carry9 << 25; /* h10 = carry9 */ /* Goal: Output h0+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. Have h0+...+2^230 h9 between 0 and 2^255-1; evidently 2^255 h10-2^255 q = 0. Goal: Output h0+...+2^230 h9. */ s[0] = (unsigned char) (h0 >> 0); s[1] = (unsigned char) (h0 >> 8); s[2] = (unsigned char) (h0 >> 16); s[3] = (unsigned char) ((h0 >> 24) | (h1 << 2)); s[4] = (unsigned char) (h1 >> 6); s[5] = (unsigned char) (h1 >> 14); s[6] = (unsigned char) ((h1 >> 22) | (h2 << 3)); s[7] = (unsigned char) (h2 >> 5); s[8] = (unsigned char) (h2 >> 13); s[9] = (unsigned char) ((h2 >> 21) | (h3 << 5)); s[10] = (unsigned char) (h3 >> 3); s[11] = (unsigned char) (h3 >> 11); s[12] = (unsigned char) ((h3 >> 19) | (h4 << 6)); s[13] = (unsigned char) (h4 >> 2); s[14] = (unsigned char) (h4 >> 10); s[15] = (unsigned char) (h4 >> 18); s[16] = (unsigned char) (h5 >> 0); s[17] = (unsigned char) (h5 >> 8); s[18] = (unsigned char) (h5 >> 16); s[19] = (unsigned char) ((h5 >> 24) | (h6 << 1)); s[20] = (unsigned char) (h6 >> 7); s[21] = (unsigned char) (h6 >> 15); s[22] = (unsigned char) ((h6 >> 23) | (h7 << 3)); s[23] = (unsigned char) (h7 >> 5); s[24] = (unsigned char) (h7 >> 13); s[25] = (unsigned char) ((h7 >> 21) | (h8 << 4)); s[26] = (unsigned char) (h8 >> 4); s[27] = (unsigned char) (h8 >> 12); s[28] = (unsigned char) ((h8 >> 20) | (h9 << 6)); s[29] = (unsigned char) (h9 >> 2); s[30] = (unsigned char) (h9 >> 10); s[31] = (unsigned char) (h9 >> 18); } olm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/src/fe.h000066400000000000000000000017241311755073500210240ustar00rootroot00000000000000#ifndef FE_H #define FE_H #include "fixedint.h" /* fe means field element. Here the field is \Z/(2^255-19). An element t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on context. */ typedef int32_t fe[10]; void fe_0(fe h); void fe_1(fe h); void fe_frombytes(fe h, const unsigned char *s); void fe_tobytes(unsigned char *s, const fe h); void fe_copy(fe h, const fe f); int fe_isnegative(const fe f); int fe_isnonzero(const fe f); void fe_cmov(fe f, const fe g, unsigned int b); void fe_cswap(fe f, fe g, unsigned int b); void fe_neg(fe h, const fe f); void fe_add(fe h, const fe f, const fe g); void fe_invert(fe out, const fe z); void fe_sq(fe h, const fe f); void fe_sq2(fe h, const fe f); void fe_mul(fe h, const fe f, const fe g); void fe_mul121666(fe h, fe f); void fe_pow22523(fe out, const fe z); void fe_sub(fe h, const fe f, const fe g); #endif olm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/src/fixedint.h000066400000000000000000000047261311755073500222510ustar00rootroot00000000000000/* Portable header to provide the 32 and 64 bits type. Not a compatible replacement for , do not blindly use it as such. */ #if ((defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined(__WATCOMC__) && (defined(_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_) || defined(__UINT_FAST64_TYPE__)) )) && !defined(FIXEDINT_H_INCLUDED) #include #define FIXEDINT_H_INCLUDED #if defined(__WATCOMC__) && __WATCOMC__ >= 1250 && !defined(UINT64_C) #include #define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX)) #endif #endif #ifndef FIXEDINT_H_INCLUDED #define FIXEDINT_H_INCLUDED #include /* (u)int32_t */ #ifndef uint32_t #if (ULONG_MAX == 0xffffffffUL) typedef unsigned long uint32_t; #elif (UINT_MAX == 0xffffffffUL) typedef unsigned int uint32_t; #elif (USHRT_MAX == 0xffffffffUL) typedef unsigned short uint32_t; #endif #endif #ifndef int32_t #if (LONG_MAX == 0x7fffffffL) typedef signed long int32_t; #elif (INT_MAX == 0x7fffffffL) typedef signed int int32_t; #elif (SHRT_MAX == 0x7fffffffL) typedef signed short int32_t; #endif #endif /* (u)int64_t */ #if (defined(__STDC__) && defined(__STDC_VERSION__) && __STDC__ && __STDC_VERSION__ >= 199901L) typedef long long int64_t; typedef unsigned long long uint64_t; #define UINT64_C(v) v ##ULL #define INT64_C(v) v ##LL #elif defined(__GNUC__) __extension__ typedef long long int64_t; __extension__ typedef unsigned long long uint64_t; #define UINT64_C(v) v ##ULL #define INT64_C(v) v ##LL #elif defined(__MWERKS__) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) || defined(__APPLE_CC__) || defined(_LONG_LONG) || defined(_CRAYC) typedef long long int64_t; typedef unsigned long long uint64_t; #define UINT64_C(v) v ##ULL #define INT64_C(v) v ##LL #elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined(__BORLANDC__) && __BORLANDC__ > 0x460) || defined(__alpha) || defined(__DECC) typedef __int64 int64_t; typedef unsigned __int64 uint64_t; #define UINT64_C(v) v ##UI64 #define INT64_C(v) v ##I64 #endif #endif olm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/src/ge.c000066400000000000000000000241771311755073500210270ustar00rootroot00000000000000#include "ge.h" #include "precomp_data.h" /* r = p + q */ void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { fe t0; fe_add(r->X, p->Y, p->X); fe_sub(r->Y, p->Y, p->X); fe_mul(r->Z, r->X, q->YplusX); fe_mul(r->Y, r->Y, q->YminusX); fe_mul(r->T, q->T2d, p->T); fe_mul(r->X, p->Z, q->Z); fe_add(t0, r->X, r->X); fe_sub(r->X, r->Z, r->Y); fe_add(r->Y, r->Z, r->Y); fe_add(r->Z, t0, r->T); fe_sub(r->T, t0, r->T); } static void slide(signed char *r, const unsigned char *a) { int i; int b; int k; for (i = 0; i < 256; ++i) { r[i] = 1 & (a[i >> 3] >> (i & 7)); } for (i = 0; i < 256; ++i) if (r[i]) { for (b = 1; b <= 6 && i + b < 256; ++b) { if (r[i + b]) { if (r[i] + (r[i + b] << b) <= 15) { r[i] += r[i + b] << b; r[i + b] = 0; } else if (r[i] - (r[i + b] << b) >= -15) { r[i] -= r[i + b] << b; for (k = i + b; k < 256; ++k) { if (!r[k]) { r[k] = 1; break; } r[k] = 0; } } else { break; } } } } } /* r = a * A + b * B where a = a[0]+256*a[1]+...+256^31 a[31]. and b = b[0]+256*b[1]+...+256^31 b[31]. B is the Ed25519 base point (x,4/5) with x positive. */ void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b) { signed char aslide[256]; signed char bslide[256]; ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */ ge_p1p1 t; ge_p3 u; ge_p3 A2; int i; slide(aslide, a); slide(bslide, b); ge_p3_to_cached(&Ai[0], A); ge_p3_dbl(&t, A); ge_p1p1_to_p3(&A2, &t); ge_add(&t, &A2, &Ai[0]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&Ai[1], &u); ge_add(&t, &A2, &Ai[1]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&Ai[2], &u); ge_add(&t, &A2, &Ai[2]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&Ai[3], &u); ge_add(&t, &A2, &Ai[3]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&Ai[4], &u); ge_add(&t, &A2, &Ai[4]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&Ai[5], &u); ge_add(&t, &A2, &Ai[5]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&Ai[6], &u); ge_add(&t, &A2, &Ai[6]); ge_p1p1_to_p3(&u, &t); ge_p3_to_cached(&Ai[7], &u); ge_p2_0(r); for (i = 255; i >= 0; --i) { if (aslide[i] || bslide[i]) { break; } } for (; i >= 0; --i) { ge_p2_dbl(&t, r); if (aslide[i] > 0) { ge_p1p1_to_p3(&u, &t); ge_add(&t, &u, &Ai[aslide[i] / 2]); } else if (aslide[i] < 0) { ge_p1p1_to_p3(&u, &t); ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]); } if (bslide[i] > 0) { ge_p1p1_to_p3(&u, &t); ge_madd(&t, &u, &Bi[bslide[i] / 2]); } else if (bslide[i] < 0) { ge_p1p1_to_p3(&u, &t); ge_msub(&t, &u, &Bi[(-bslide[i]) / 2]); } ge_p1p1_to_p2(r, &t); } } static const fe d = { -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116 }; static const fe sqrtm1 = { -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482 }; int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s) { fe u; fe v; fe v3; fe vxx; fe check; fe_frombytes(h->Y, s); fe_1(h->Z); fe_sq(u, h->Y); fe_mul(v, u, d); fe_sub(u, u, h->Z); /* u = y^2-1 */ fe_add(v, v, h->Z); /* v = dy^2+1 */ fe_sq(v3, v); fe_mul(v3, v3, v); /* v3 = v^3 */ fe_sq(h->X, v3); fe_mul(h->X, h->X, v); fe_mul(h->X, h->X, u); /* x = uv^7 */ fe_pow22523(h->X, h->X); /* x = (uv^7)^((q-5)/8) */ fe_mul(h->X, h->X, v3); fe_mul(h->X, h->X, u); /* x = uv^3(uv^7)^((q-5)/8) */ fe_sq(vxx, h->X); fe_mul(vxx, vxx, v); fe_sub(check, vxx, u); /* vx^2-u */ if (fe_isnonzero(check)) { fe_add(check, vxx, u); /* vx^2+u */ if (fe_isnonzero(check)) { return -1; } fe_mul(h->X, h->X, sqrtm1); } if (fe_isnegative(h->X) == (s[31] >> 7)) { fe_neg(h->X, h->X); } fe_mul(h->T, h->X, h->Y); return 0; } /* r = p + q */ void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) { fe t0; fe_add(r->X, p->Y, p->X); fe_sub(r->Y, p->Y, p->X); fe_mul(r->Z, r->X, q->yplusx); fe_mul(r->Y, r->Y, q->yminusx); fe_mul(r->T, q->xy2d, p->T); fe_add(t0, p->Z, p->Z); fe_sub(r->X, r->Z, r->Y); fe_add(r->Y, r->Z, r->Y); fe_add(r->Z, t0, r->T); fe_sub(r->T, t0, r->T); } /* r = p - q */ void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) { fe t0; fe_add(r->X, p->Y, p->X); fe_sub(r->Y, p->Y, p->X); fe_mul(r->Z, r->X, q->yminusx); fe_mul(r->Y, r->Y, q->yplusx); fe_mul(r->T, q->xy2d, p->T); fe_add(t0, p->Z, p->Z); fe_sub(r->X, r->Z, r->Y); fe_add(r->Y, r->Z, r->Y); fe_sub(r->Z, t0, r->T); fe_add(r->T, t0, r->T); } /* r = p */ void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p) { fe_mul(r->X, p->X, p->T); fe_mul(r->Y, p->Y, p->Z); fe_mul(r->Z, p->Z, p->T); } /* r = p */ void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p) { fe_mul(r->X, p->X, p->T); fe_mul(r->Y, p->Y, p->Z); fe_mul(r->Z, p->Z, p->T); fe_mul(r->T, p->X, p->Y); } void ge_p2_0(ge_p2 *h) { fe_0(h->X); fe_1(h->Y); fe_1(h->Z); } /* r = 2 * p */ void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p) { fe t0; fe_sq(r->X, p->X); fe_sq(r->Z, p->Y); fe_sq2(r->T, p->Z); fe_add(r->Y, p->X, p->Y); fe_sq(t0, r->Y); fe_add(r->Y, r->Z, r->X); fe_sub(r->Z, r->Z, r->X); fe_sub(r->X, t0, r->Y); fe_sub(r->T, r->T, r->Z); } void ge_p3_0(ge_p3 *h) { fe_0(h->X); fe_1(h->Y); fe_1(h->Z); fe_0(h->T); } /* r = 2 * p */ void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p) { ge_p2 q; ge_p3_to_p2(&q, p); ge_p2_dbl(r, &q); } /* r = p */ static const fe d2 = { -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199 }; void ge_p3_to_cached(ge_cached *r, const ge_p3 *p) { fe_add(r->YplusX, p->Y, p->X); fe_sub(r->YminusX, p->Y, p->X); fe_copy(r->Z, p->Z); fe_mul(r->T2d, p->T, d2); } /* r = p */ void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p) { fe_copy(r->X, p->X); fe_copy(r->Y, p->Y); fe_copy(r->Z, p->Z); } void ge_p3_tobytes(unsigned char *s, const ge_p3 *h) { fe recip; fe x; fe y; fe_invert(recip, h->Z); fe_mul(x, h->X, recip); fe_mul(y, h->Y, recip); fe_tobytes(s, y); s[31] ^= fe_isnegative(x) << 7; } static unsigned char equal(signed char b, signed char c) { unsigned char ub = b; unsigned char uc = c; unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */ uint64_t y = x; /* 0: yes; 1..255: no */ y -= 1; /* large: yes; 0..254: no */ y >>= 63; /* 1: yes; 0: no */ return (unsigned char) y; } static unsigned char negative(signed char b) { uint64_t x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ x >>= 63; /* 1: yes; 0: no */ return (unsigned char) x; } static void cmov(ge_precomp *t, ge_precomp *u, unsigned char b) { fe_cmov(t->yplusx, u->yplusx, b); fe_cmov(t->yminusx, u->yminusx, b); fe_cmov(t->xy2d, u->xy2d, b); } static void select(ge_precomp *t, int pos, signed char b) { ge_precomp minust; unsigned char bnegative = negative(b); unsigned char babs = b - (((-bnegative) & b) << 1); fe_1(t->yplusx); fe_1(t->yminusx); fe_0(t->xy2d); cmov(t, &base[pos][0], equal(babs, 1)); cmov(t, &base[pos][1], equal(babs, 2)); cmov(t, &base[pos][2], equal(babs, 3)); cmov(t, &base[pos][3], equal(babs, 4)); cmov(t, &base[pos][4], equal(babs, 5)); cmov(t, &base[pos][5], equal(babs, 6)); cmov(t, &base[pos][6], equal(babs, 7)); cmov(t, &base[pos][7], equal(babs, 8)); fe_copy(minust.yplusx, t->yminusx); fe_copy(minust.yminusx, t->yplusx); fe_neg(minust.xy2d, t->xy2d); cmov(t, &minust, bnegative); } /* h = a * B where a = a[0]+256*a[1]+...+256^31 a[31] B is the Ed25519 base point (x,4/5) with x positive. Preconditions: a[31] <= 127 */ void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) { signed char e[64]; signed char carry; ge_p1p1 r; ge_p2 s; ge_precomp t; int i; for (i = 0; i < 32; ++i) { e[2 * i + 0] = (a[i] >> 0) & 15; e[2 * i + 1] = (a[i] >> 4) & 15; } /* each e[i] is between 0 and 15 */ /* e[63] is between 0 and 7 */ carry = 0; for (i = 0; i < 63; ++i) { e[i] += carry; carry = e[i] + 8; carry >>= 4; e[i] -= carry << 4; } e[63] += carry; /* each e[i] is between -8 and 8 */ ge_p3_0(h); for (i = 1; i < 64; i += 2) { select(&t, i / 2, e[i]); ge_madd(&r, h, &t); ge_p1p1_to_p3(h, &r); } ge_p3_dbl(&r, h); ge_p1p1_to_p2(&s, &r); ge_p2_dbl(&r, &s); ge_p1p1_to_p2(&s, &r); ge_p2_dbl(&r, &s); ge_p1p1_to_p2(&s, &r); ge_p2_dbl(&r, &s); ge_p1p1_to_p3(h, &r); for (i = 0; i < 64; i += 2) { select(&t, i / 2, e[i]); ge_madd(&r, h, &t); ge_p1p1_to_p3(h, &r); } } /* r = p - q */ void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { fe t0; fe_add(r->X, p->Y, p->X); fe_sub(r->Y, p->Y, p->X); fe_mul(r->Z, r->X, q->YminusX); fe_mul(r->Y, r->Y, q->YplusX); fe_mul(r->T, q->T2d, p->T); fe_mul(r->X, p->Z, q->Z); fe_add(t0, r->X, r->X); fe_sub(r->X, r->Z, r->Y); fe_add(r->Y, r->Z, r->Y); fe_sub(r->Z, t0, r->T); fe_add(r->T, t0, r->T); } void ge_tobytes(unsigned char *s, const ge_p2 *h) { fe recip; fe x; fe y; fe_invert(recip, h->Z); fe_mul(x, h->X, recip); fe_mul(y, h->Y, recip); fe_tobytes(s, y); s[31] ^= fe_isnegative(x) << 7; } olm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/src/ge.h000066400000000000000000000032231311755073500210210ustar00rootroot00000000000000#ifndef GE_H #define GE_H #include "fe.h" /* ge means group element. Here the group is the set of pairs (x,y) of field elements (see fe.h) satisfying -x^2 + y^2 = 1 + d x^2y^2 where d = -121665/121666. Representations: ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T ge_precomp (Duif): (y+x,y-x,2dxy) */ typedef struct { fe X; fe Y; fe Z; } ge_p2; typedef struct { fe X; fe Y; fe Z; fe T; } ge_p3; typedef struct { fe X; fe Y; fe Z; fe T; } ge_p1p1; typedef struct { fe yplusx; fe yminusx; fe xy2d; } ge_precomp; typedef struct { fe YplusX; fe YminusX; fe Z; fe T2d; } ge_cached; void ge_p3_tobytes(unsigned char *s, const ge_p3 *h); void ge_tobytes(unsigned char *s, const ge_p2 *h); int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s); void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q); void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q); void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b); void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q); void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q); void ge_scalarmult_base(ge_p3 *h, const unsigned char *a); void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p); void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p); void ge_p2_0(ge_p2 *h); void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p); void ge_p3_0(ge_p3 *h); void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p); void ge_p3_to_cached(ge_cached *r, const ge_p3 *p); void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p); #endif olm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/src/key_exchange.c000066400000000000000000000034311311755073500230540ustar00rootroot00000000000000#include "ed25519.h" #include "fe.h" void ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key) { unsigned char e[32]; unsigned int i; fe x1; fe x2; fe z2; fe x3; fe z3; fe tmp0; fe tmp1; int pos; unsigned int swap; unsigned int b; /* copy the private key and make sure it's valid */ for (i = 0; i < 32; ++i) { e[i] = private_key[i]; } e[0] &= 248; e[31] &= 63; e[31] |= 64; /* unpack the public key and convert edwards to montgomery */ /* due to CodesInChaos: montgomeryX = (edwardsY + 1)*inverse(1 - edwardsY) mod p */ fe_frombytes(x1, public_key); fe_1(tmp1); fe_add(tmp0, x1, tmp1); fe_sub(tmp1, tmp1, x1); fe_invert(tmp1, tmp1); fe_mul(x1, tmp0, tmp1); fe_1(x2); fe_0(z2); fe_copy(x3, x1); fe_1(z3); swap = 0; for (pos = 254; pos >= 0; --pos) { b = e[pos / 8] >> (pos & 7); b &= 1; swap ^= b; fe_cswap(x2, x3, swap); fe_cswap(z2, z3, swap); swap = b; /* from montgomery.h */ fe_sub(tmp0, x3, z3); fe_sub(tmp1, x2, z2); fe_add(x2, x2, z2); fe_add(z2, x3, z3); fe_mul(z3, tmp0, x2); fe_mul(z2, z2, tmp1); fe_sq(tmp0, tmp1); fe_sq(tmp1, x2); fe_add(x3, z3, z2); fe_sub(z2, z3, z2); fe_mul(x2, tmp1, tmp0); fe_sub(tmp1, tmp1, tmp0); fe_sq(z2, z2); fe_mul121666(z3, tmp1); fe_sq(x3, x3); fe_add(tmp0, tmp0, z3); fe_mul(z3, x1, z2); fe_mul(z2, tmp1, tmp0); } fe_cswap(x2, x3, swap); fe_cswap(z2, z3, swap); fe_invert(z2, z2); fe_mul(x2, x2, z2); fe_tobytes(shared_secret, x2); } olm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/src/keypair.c000066400000000000000000000005741311755073500220730ustar00rootroot00000000000000#include "ed25519.h" #include "sha512.h" #include "ge.h" void ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed) { ge_p3 A; sha512(seed, 32, private_key); private_key[0] &= 248; private_key[31] &= 63; private_key[31] |= 64; ge_scalarmult_base(&A, private_key); ge_p3_tobytes(public_key, &A); } olm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/src/precomp_data.h000066400000000000000000002767731311755073500231120ustar00rootroot00000000000000static ge_precomp Bi[8] = { { { 25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605 }, { -12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378 }, { -8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546 }, }, { { 15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024 }, { 16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574 }, { 30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357 }, }, { { 10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380 }, { 4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306 }, { 19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942 }, }, { { 5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766 }, { -30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701 }, { 28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300 }, }, { { -22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877 }, { -6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951 }, { 4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784 }, }, { { -25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436 }, { 25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918 }, { 23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877 }, }, { { -33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800 }, { -25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305 }, { -13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300 }, }, { { -3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876 }, { -24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619 }, { -3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683 }, }, }; /* base[i][j] = (j+1)*256^i*B */ static ge_precomp base[32][8] = { { { { 25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605 }, { -12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378 }, { -8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546 }, }, { { -12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303 }, { -21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081 }, { 26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697 }, }, { { 15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024 }, { 16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574 }, { 30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357 }, }, { { -17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540 }, { 23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397 }, { 7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325 }, }, { { 10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380 }, { 4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306 }, { 19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942 }, }, { { -15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777 }, { -8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737 }, { -18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652 }, }, { { 5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766 }, { -30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701 }, { 28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300 }, }, { { 14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726 }, { -7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955 }, { 27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425 }, }, }, { { { -13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171 }, { 27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510 }, { 17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660 }, }, { { -10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639 }, { 29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963 }, { 5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950 }, }, { { -27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568 }, { 12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335 }, { 25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628 }, }, { { -26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007 }, { -2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772 }, { -22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653 }, }, { { 2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567 }, { 13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686 }, { 21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372 }, }, { { -13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887 }, { -23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954 }, { -29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953 }, }, { { 24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833 }, { -16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532 }, { -22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876 }, }, { { 2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268 }, { 33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214 }, { 1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038 }, }, }, { { { 6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800 }, { 4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645 }, { -4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664 }, }, { { 1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933 }, { -25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182 }, { -17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222 }, }, { { -18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991 }, { 20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880 }, { 9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092 }, }, { { -16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295 }, { 19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788 }, { 8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553 }, }, { { -15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026 }, { 11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347 }, { -18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033 }, }, { { -23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395 }, { -27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278 }, { 1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890 }, }, { { 32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995 }, { -30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596 }, { -11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891 }, }, { { 31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060 }, { 11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608 }, { -20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606 }, }, }, { { { 7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389 }, { -19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016 }, { -11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341 }, }, { { -22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505 }, { 14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553 }, { -28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655 }, }, { { 15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220 }, { 12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631 }, { -4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099 }, }, { { 26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556 }, { 14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749 }, { 236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930 }, }, { { 1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391 }, { 5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253 }, { 20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066 }, }, { { 24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958 }, { -11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082 }, { -28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383 }, }, { { -30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521 }, { -11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807 }, { 23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948 }, }, { { 9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134 }, { -32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455 }, { 27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629 }, }, }, { { { -8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069 }, { -32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746 }, { 24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919 }, }, { { 11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837 }, { 8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906 }, { -28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771 }, }, { { -25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817 }, { 10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098 }, { 10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409 }, }, { { -12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504 }, { -26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727 }, { 28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420 }, }, { { -32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003 }, { -1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605 }, { -30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384 }, }, { { -26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701 }, { -23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683 }, { 29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708 }, }, { { -3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563 }, { -19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260 }, { -5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387 }, }, { { -19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672 }, { 23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686 }, { -24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665 }, }, }, { { { 11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182 }, { -31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277 }, { 14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628 }, }, { { -4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474 }, { -26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539 }, { -25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822 }, }, { { -10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970 }, { 19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756 }, { -24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508 }, }, { { -26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683 }, { -10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655 }, { -20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158 }, }, { { -4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125 }, { -15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839 }, { -20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664 }, }, { { 27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294 }, { -18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899 }, { -11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070 }, }, { { 3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294 }, { -15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949 }, { -21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083 }, }, { { 31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420 }, { -5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940 }, { 29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396 }, }, }, { { { -12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567 }, { 20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127 }, { -16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294 }, }, { { -12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887 }, { 22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964 }, { 16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195 }, }, { { 9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244 }, { 24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999 }, { -1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762 }, }, { { -18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274 }, { -33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236 }, { -16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605 }, }, { { -13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761 }, { -22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884 }, { -6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482 }, }, { { -24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638 }, { -11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490 }, { -32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170 }, }, { { 5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736 }, { 10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124 }, { -17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392 }, }, { { 8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029 }, { 6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048 }, { 28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958 }, }, }, { { { 24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593 }, { 26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071 }, { -11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692 }, }, { { 11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687 }, { -160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441 }, { -20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001 }, }, { { -938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460 }, { -19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007 }, { -21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762 }, }, { { 15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005 }, { -9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674 }, { 4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035 }, }, { { 7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590 }, { -2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957 }, { -30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812 }, }, { { 33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740 }, { -18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122 }, { -27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158 }, }, { { 8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885 }, { 26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140 }, { 19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857 }, }, { { 801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155 }, { 19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260 }, { 19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483 }, }, }, { { { -3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677 }, { 32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815 }, { 22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751 }, }, { { -16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203 }, { -11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208 }, { 1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230 }, }, { { 16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850 }, { -21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389 }, { -9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968 }, }, { { -11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689 }, { 14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880 }, { 5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304 }, }, { { 30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632 }, { -3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412 }, { 20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566 }, }, { { -20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038 }, { -26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232 }, { -1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943 }, }, { { 17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856 }, { 23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738 }, { 15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971 }, }, { { -27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718 }, { -13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697 }, { -11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883 }, }, }, { { { 5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912 }, { -26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358 }, { 3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849 }, }, { { 29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307 }, { -14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977 }, { -6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335 }, }, { { -29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644 }, { -22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616 }, { -27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735 }, }, { { -21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099 }, { 29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341 }, { -936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336 }, }, { { -23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646 }, { 31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425 }, { -17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388 }, }, { { -31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743 }, { -16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822 }, { -8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462 }, }, { { 18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985 }, { 9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702 }, { -22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797 }, }, { { 21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293 }, { 27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100 }, { 19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688 }, }, }, { { { 12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186 }, { 2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610 }, { -2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707 }, }, { { 7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220 }, { 915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025 }, { 32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044 }, }, { { 32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992 }, { -4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027 }, { 21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197 }, }, { { 8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901 }, { 31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952 }, { 19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878 }, }, { { -28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390 }, { 32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730 }, { 2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730 }, }, { { -19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180 }, { -30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272 }, { -15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715 }, }, { { -22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970 }, { -31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772 }, { -17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865 }, }, { { 15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750 }, { 20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373 }, { 32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348 }, }, }, { { { 9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144 }, { -22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195 }, { 5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086 }, }, { { -13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684 }, { -8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518 }, { -2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233 }, }, { { -5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793 }, { -2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794 }, { 580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435 }, }, { { 23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921 }, { 13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518 }, { 2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563 }, }, { { 14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278 }, { -27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024 }, { 4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030 }, }, { { 10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783 }, { 27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717 }, { 6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844 }, }, { { 14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333 }, { 16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048 }, { 22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760 }, }, { { -4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760 }, { -15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757 }, { -2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112 }, }, }, { { { -19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468 }, { 3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184 }, { 10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289 }, }, { { 15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066 }, { 24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882 }, { 13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226 }, }, { { 16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101 }, { 29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279 }, { -6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811 }, }, { { 27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709 }, { 20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714 }, { -2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121 }, }, { { 9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464 }, { 12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847 }, { 13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400 }, }, { { 4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414 }, { -15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158 }, { 17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045 }, }, { { -461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415 }, { -5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459 }, { -31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079 }, }, { { 21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412 }, { -20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743 }, { -14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836 }, }, }, { { { 12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022 }, { 18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429 }, { -6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065 }, }, { { 30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861 }, { 10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000 }, { -33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101 }, }, { { 32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815 }, { 29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642 }, { 10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966 }, }, { { 25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574 }, { -21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742 }, { -18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689 }, }, { { 12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020 }, { -10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772 }, { 3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982 }, }, { { -14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953 }, { -16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218 }, { -17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265 }, }, { { 29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073 }, { -3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325 }, { -11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798 }, }, { { -4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870 }, { -7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863 }, { -13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927 }, }, }, { { { -2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267 }, { -9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663 }, { 22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862 }, }, { { -25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673 }, { 15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943 }, { 15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020 }, }, { { -4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238 }, { 11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064 }, { 14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795 }, }, { { 15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052 }, { -10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904 }, { 29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531 }, }, { { -13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979 }, { -5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841 }, { 10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431 }, }, { { 10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324 }, { -31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940 }, { 10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320 }, }, { { -15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184 }, { 14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114 }, { 30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878 }, }, { { 12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784 }, { -2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091 }, { -16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585 }, }, }, { { { -8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208 }, { 10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864 }, { 17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661 }, }, { { 7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233 }, { 26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212 }, { -12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525 }, }, { { -24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068 }, { 9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397 }, { -8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988 }, }, { { 5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889 }, { 32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038 }, { 14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697 }, }, { { 20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875 }, { -25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905 }, { -25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656 }, }, { { 11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818 }, { 27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714 }, { 10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203 }, }, { { 20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931 }, { -30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024 }, { -23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084 }, }, { { -1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204 }, { 20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817 }, { 27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667 }, }, }, { { { 11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504 }, { -12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768 }, { -19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255 }, }, { { 6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790 }, { 1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438 }, { -22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333 }, }, { { 17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971 }, { 31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905 }, { 29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409 }, }, { { 12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409 }, { 6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499 }, { -8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363 }, }, { { 28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664 }, { -11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324 }, { -21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940 }, }, { { 13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990 }, { -17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914 }, { -25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290 }, }, { { 24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257 }, { -6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433 }, { -16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236 }, }, { { -12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045 }, { 11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093 }, { -1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347 }, }, }, { { { -28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191 }, { -15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507 }, { -12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906 }, }, { { 3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018 }, { -16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109 }, { -23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926 }, }, { { -24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528 }, { 8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625 }, { -32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286 }, }, { { 2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033 }, { 27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866 }, { 21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896 }, }, { { 30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075 }, { 26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347 }, { -22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437 }, }, { { -5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165 }, { -18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588 }, { -32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193 }, }, { { -19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017 }, { -28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883 }, { 21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961 }, }, { { 8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043 }, { 29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663 }, { -20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362 }, }, }, { { { -33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860 }, { 2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466 }, { -24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063 }, }, { { -26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997 }, { -1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295 }, { -13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369 }, }, { { 9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385 }, { 18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109 }, { 2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906 }, }, { { 4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424 }, { -19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185 }, { 7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962 }, }, { { -7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325 }, { 10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593 }, { 696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404 }, }, { { -11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644 }, { 17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801 }, { 26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804 }, }, { { -31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884 }, { -586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577 }, { -9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849 }, }, { { 32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473 }, { -8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644 }, { -2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319 }, }, }, { { { -11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599 }, { -9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768 }, { -27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084 }, }, { { -27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328 }, { -15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369 }, { 20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920 }, }, { { 12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815 }, { -32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025 }, { -21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397 }, }, { { -20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448 }, { 6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981 }, { 30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165 }, }, { { 32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501 }, { 17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073 }, { -1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861 }, }, { { 14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845 }, { -1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211 }, { 18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870 }, }, { { 10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096 }, { 33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803 }, { -32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168 }, }, { { 30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965 }, { -14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505 }, { 18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598 }, }, }, { { { 5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782 }, { 5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900 }, { -31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479 }, }, { { -12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208 }, { 8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232 }, { 17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719 }, }, { { 16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271 }, { -4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326 }, { -8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132 }, }, { { 14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300 }, { 8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570 }, { 15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670 }, }, { { -2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994 }, { -12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913 }, { 31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317 }, }, { { -25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730 }, { 842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096 }, { -4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078 }, }, { { -15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411 }, { -19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905 }, { -9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654 }, }, { { -28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870 }, { -23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498 }, { 12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579 }, }, }, { { { 14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677 }, { 10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647 }, { -2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743 }, }, { { -25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468 }, { 21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375 }, { -25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155 }, }, { { 6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725 }, { -12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612 }, { -10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943 }, }, { { -30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944 }, { 30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928 }, { 9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406 }, }, { { 22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139 }, { -8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963 }, { -31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693 }, }, { { 1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734 }, { -448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680 }, { -24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410 }, }, { { -9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931 }, { -16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654 }, { 22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710 }, }, { { 29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180 }, { -26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684 }, { -10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895 }, }, }, { { { 22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501 }, { -11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413 }, { 6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880 }, }, { { -8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874 }, { 22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962 }, { -7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899 }, }, { { 21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152 }, { 9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063 }, { 7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080 }, }, { { -9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146 }, { -17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183 }, { -19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133 }, }, { { -32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421 }, { -3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622 }, { -4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197 }, }, { { 2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663 }, { 31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753 }, { 4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755 }, }, { { -9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862 }, { -26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118 }, { 26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171 }, }, { { 15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380 }, { 16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824 }, { 28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270 }, }, }, { { { -817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438 }, { -31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584 }, { -594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562 }, }, { { 30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471 }, { 18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610 }, { 19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269 }, }, { { -30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650 }, { 14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369 }, { 19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461 }, }, { { 30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462 }, { -5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793 }, { -2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218 }, }, { { -24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226 }, { 18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019 }, { -15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037 }, }, { { 31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171 }, { -17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132 }, { -28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841 }, }, { { 21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181 }, { -33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210 }, { -1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040 }, }, { { 3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935 }, { 24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105 }, { -28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814 }, }, }, { { { 793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852 }, { 5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581 }, { -4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646 }, }, { { 10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844 }, { 10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025 }, { 27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453 }, }, { { -23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068 }, { 4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192 }, { -17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921 }, }, { { -9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259 }, { -12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426 }, { -5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072 }, }, { { -17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305 }, { 13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832 }, { 28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943 }, }, { { -16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011 }, { 24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447 }, { 17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494 }, }, { { -28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245 }, { -20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859 }, { 28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915 }, }, { { 16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707 }, { 10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848 }, { -11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224 }, }, }, { { { -25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391 }, { 15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215 }, { -23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101 }, }, { { 23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713 }, { 21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849 }, { -7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930 }, }, { { -29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940 }, { -21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031 }, { -17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404 }, }, { { -25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243 }, { -23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116 }, { -24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525 }, }, { { -23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509 }, { -10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883 }, { 15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865 }, }, { { -3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660 }, { 4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273 }, { -28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138 }, }, { { -25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560 }, { -10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135 }, { 2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941 }, }, { { -4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739 }, { 18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756 }, { -30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819 }, }, }, { { { -6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347 }, { -27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028 }, { 21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075 }, }, { { 16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799 }, { -2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609 }, { -25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817 }, }, { { -23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989 }, { -30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523 }, { 4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278 }, }, { { 31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045 }, { 19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377 }, { 24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480 }, }, { { 17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016 }, { 510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426 }, { 18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525 }, }, { { 13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396 }, { 9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080 }, { 12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892 }, }, { { 15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275 }, { 11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074 }, { 20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140 }, }, { { -16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717 }, { -1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101 }, { 24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127 }, }, }, { { { -12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632 }, { -26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415 }, { -31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160 }, }, { { 31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876 }, { 22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625 }, { -15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478 }, }, { { 27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164 }, { 26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595 }, { -7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248 }, }, { { -16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858 }, { 15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193 }, { 8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184 }, }, { { -18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942 }, { -1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635 }, { 21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948 }, }, { { 11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935 }, { -25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415 }, { -15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416 }, }, { { -7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018 }, { 4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778 }, { 366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659 }, }, { { -24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385 }, { 18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503 }, { 476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329 }, }, }, { { { 20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056 }, { -13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838 }, { 24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948 }, }, { { -3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691 }, { -15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118 }, { -23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517 }, }, { { -20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269 }, { -6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904 }, { -23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589 }, }, { { -28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193 }, { -7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910 }, { -30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930 }, }, { { -7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667 }, { 25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481 }, { -9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876 }, }, { { 22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640 }, { -8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278 }, { -21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112 }, }, { { 26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272 }, { 17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012 }, { -10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221 }, }, { { 30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046 }, { 13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345 }, { -19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310 }, }, }, { { { 19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937 }, { 31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636 }, { -9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008 }, }, { { -2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429 }, { -15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576 }, { 31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066 }, }, { { -9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490 }, { -12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104 }, { 33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053 }, }, { { 31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275 }, { -20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511 }, { 22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095 }, }, { { -28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439 }, { 23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939 }, { -23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424 }, }, { { 2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310 }, { 3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608 }, { -32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079 }, }, { { -23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101 }, { 21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418 }, { 18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576 }, }, { { 30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356 }, { 9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996 }, { -26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099 }, }, }, { { { -26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728 }, { -13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658 }, { -10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242 }, }, { { -21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001 }, { -4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766 }, { 18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373 }, }, { { 26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458 }, { -17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628 }, { -13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657 }, }, { { -23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062 }, { 25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616 }, { 31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014 }, }, { { 24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383 }, { -25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814 }, { -20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718 }, }, { { 30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417 }, { 2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222 }, { 33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444 }, }, { { -20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597 }, { 23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970 }, { 1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799 }, }, { { -5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647 }, { 13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511 }, { -29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032 }, }, }, { { { 9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834 }, { -23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461 }, { 29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062 }, }, { { -25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516 }, { -20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547 }, { -24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240 }, }, { { -17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038 }, { -33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741 }, { 16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103 }, }, { { -19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747 }, { -1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323 }, { 31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016 }, }, { { -14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373 }, { 15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228 }, { -2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141 }, }, { { 16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399 }, { 11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831 }, { -185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376 }, }, { { -32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313 }, { -18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958 }, { -6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577 }, }, { { -22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743 }, { 29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684 }, { -20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476 }, }, }, };olm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/src/sc.c000066400000000000000000000546221311755073500210370ustar00rootroot00000000000000#include "fixedint.h" #include "sc.h" #ifndef ED25519_LOAD_BYTES #define ED25519_LOAD_BYTES static uint64_t load_3(const unsigned char *in) { uint64_t result; result = (uint64_t) in[0]; result |= ((uint64_t) in[1]) << 8; result |= ((uint64_t) in[2]) << 16; return result; } static uint64_t load_4(const unsigned char *in) { uint64_t result; result = (uint64_t) in[0]; result |= ((uint64_t) in[1]) << 8; result |= ((uint64_t) in[2]) << 16; result |= ((uint64_t) in[3]) << 24; return result; } #endif /* Input: s[0]+256*s[1]+...+256^63*s[63] = s Output: s[0]+256*s[1]+...+256^31*s[31] = s mod l where l = 2^252 + 27742317777372353535851937790883648493. Overwrites s in place. */ void sc_reduce(unsigned char *s) { int64_t s0 = 2097151 & load_3(s); int64_t s1 = 2097151 & (load_4(s + 2) >> 5); int64_t s2 = 2097151 & (load_3(s + 5) >> 2); int64_t s3 = 2097151 & (load_4(s + 7) >> 7); int64_t s4 = 2097151 & (load_4(s + 10) >> 4); int64_t s5 = 2097151 & (load_3(s + 13) >> 1); int64_t s6 = 2097151 & (load_4(s + 15) >> 6); int64_t s7 = 2097151 & (load_3(s + 18) >> 3); int64_t s8 = 2097151 & load_3(s + 21); int64_t s9 = 2097151 & (load_4(s + 23) >> 5); int64_t s10 = 2097151 & (load_3(s + 26) >> 2); int64_t s11 = 2097151 & (load_4(s + 28) >> 7); int64_t s12 = 2097151 & (load_4(s + 31) >> 4); int64_t s13 = 2097151 & (load_3(s + 34) >> 1); int64_t s14 = 2097151 & (load_4(s + 36) >> 6); int64_t s15 = 2097151 & (load_3(s + 39) >> 3); int64_t s16 = 2097151 & load_3(s + 42); int64_t s17 = 2097151 & (load_4(s + 44) >> 5); int64_t s18 = 2097151 & (load_3(s + 47) >> 2); int64_t s19 = 2097151 & (load_4(s + 49) >> 7); int64_t s20 = 2097151 & (load_4(s + 52) >> 4); int64_t s21 = 2097151 & (load_3(s + 55) >> 1); int64_t s22 = 2097151 & (load_4(s + 57) >> 6); int64_t s23 = (load_4(s + 60) >> 3); int64_t carry0; int64_t carry1; int64_t carry2; int64_t carry3; int64_t carry4; int64_t carry5; int64_t carry6; int64_t carry7; int64_t carry8; int64_t carry9; int64_t carry10; int64_t carry11; int64_t carry12; int64_t carry13; int64_t carry14; int64_t carry15; int64_t carry16; s11 += s23 * 666643; s12 += s23 * 470296; s13 += s23 * 654183; s14 -= s23 * 997805; s15 += s23 * 136657; s16 -= s23 * 683901; s23 = 0; s10 += s22 * 666643; s11 += s22 * 470296; s12 += s22 * 654183; s13 -= s22 * 997805; s14 += s22 * 136657; s15 -= s22 * 683901; s22 = 0; s9 += s21 * 666643; s10 += s21 * 470296; s11 += s21 * 654183; s12 -= s21 * 997805; s13 += s21 * 136657; s14 -= s21 * 683901; s21 = 0; s8 += s20 * 666643; s9 += s20 * 470296; s10 += s20 * 654183; s11 -= s20 * 997805; s12 += s20 * 136657; s13 -= s20 * 683901; s20 = 0; s7 += s19 * 666643; s8 += s19 * 470296; s9 += s19 * 654183; s10 -= s19 * 997805; s11 += s19 * 136657; s12 -= s19 * 683901; s19 = 0; s6 += s18 * 666643; s7 += s18 * 470296; s8 += s18 * 654183; s9 -= s18 * 997805; s10 += s18 * 136657; s11 -= s18 * 683901; s18 = 0; carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; s6 -= carry6 << 21; carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; s8 -= carry8 << 21; carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; s10 -= carry10 << 21; carry12 = (s12 + (1 << 20)) >> 21; s13 += carry12; s12 -= carry12 << 21; carry14 = (s14 + (1 << 20)) >> 21; s15 += carry14; s14 -= carry14 << 21; carry16 = (s16 + (1 << 20)) >> 21; s17 += carry16; s16 -= carry16 << 21; carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; s7 -= carry7 << 21; carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; s9 -= carry9 << 21; carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; s11 -= carry11 << 21; carry13 = (s13 + (1 << 20)) >> 21; s14 += carry13; s13 -= carry13 << 21; carry15 = (s15 + (1 << 20)) >> 21; s16 += carry15; s15 -= carry15 << 21; s5 += s17 * 666643; s6 += s17 * 470296; s7 += s17 * 654183; s8 -= s17 * 997805; s9 += s17 * 136657; s10 -= s17 * 683901; s17 = 0; s4 += s16 * 666643; s5 += s16 * 470296; s6 += s16 * 654183; s7 -= s16 * 997805; s8 += s16 * 136657; s9 -= s16 * 683901; s16 = 0; s3 += s15 * 666643; s4 += s15 * 470296; s5 += s15 * 654183; s6 -= s15 * 997805; s7 += s15 * 136657; s8 -= s15 * 683901; s15 = 0; s2 += s14 * 666643; s3 += s14 * 470296; s4 += s14 * 654183; s5 -= s14 * 997805; s6 += s14 * 136657; s7 -= s14 * 683901; s14 = 0; s1 += s13 * 666643; s2 += s13 * 470296; s3 += s13 * 654183; s4 -= s13 * 997805; s5 += s13 * 136657; s6 -= s13 * 683901; s13 = 0; s0 += s12 * 666643; s1 += s12 * 470296; s2 += s12 * 654183; s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; s12 = 0; carry0 = (s0 + (1 << 20)) >> 21; s1 += carry0; s0 -= carry0 << 21; carry2 = (s2 + (1 << 20)) >> 21; s3 += carry2; s2 -= carry2 << 21; carry4 = (s4 + (1 << 20)) >> 21; s5 += carry4; s4 -= carry4 << 21; carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; s6 -= carry6 << 21; carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; s8 -= carry8 << 21; carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; s10 -= carry10 << 21; carry1 = (s1 + (1 << 20)) >> 21; s2 += carry1; s1 -= carry1 << 21; carry3 = (s3 + (1 << 20)) >> 21; s4 += carry3; s3 -= carry3 << 21; carry5 = (s5 + (1 << 20)) >> 21; s6 += carry5; s5 -= carry5 << 21; carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; s7 -= carry7 << 21; carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; s9 -= carry9 << 21; carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; s11 -= carry11 << 21; s0 += s12 * 666643; s1 += s12 * 470296; s2 += s12 * 654183; s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; s12 = 0; carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21; s0 += s12 * 666643; s1 += s12 * 470296; s2 += s12 * 654183; s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; s12 = 0; carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; s[0] = (unsigned char) (s0 >> 0); s[1] = (unsigned char) (s0 >> 8); s[2] = (unsigned char) ((s0 >> 16) | (s1 << 5)); s[3] = (unsigned char) (s1 >> 3); s[4] = (unsigned char) (s1 >> 11); s[5] = (unsigned char) ((s1 >> 19) | (s2 << 2)); s[6] = (unsigned char) (s2 >> 6); s[7] = (unsigned char) ((s2 >> 14) | (s3 << 7)); s[8] = (unsigned char) (s3 >> 1); s[9] = (unsigned char) (s3 >> 9); s[10] = (unsigned char) ((s3 >> 17) | (s4 << 4)); s[11] = (unsigned char) (s4 >> 4); s[12] = (unsigned char) (s4 >> 12); s[13] = (unsigned char) ((s4 >> 20) | (s5 << 1)); s[14] = (unsigned char) (s5 >> 7); s[15] = (unsigned char) ((s5 >> 15) | (s6 << 6)); s[16] = (unsigned char) (s6 >> 2); s[17] = (unsigned char) (s6 >> 10); s[18] = (unsigned char) ((s6 >> 18) | (s7 << 3)); s[19] = (unsigned char) (s7 >> 5); s[20] = (unsigned char) (s7 >> 13); s[21] = (unsigned char) (s8 >> 0); s[22] = (unsigned char) (s8 >> 8); s[23] = (unsigned char) ((s8 >> 16) | (s9 << 5)); s[24] = (unsigned char) (s9 >> 3); s[25] = (unsigned char) (s9 >> 11); s[26] = (unsigned char) ((s9 >> 19) | (s10 << 2)); s[27] = (unsigned char) (s10 >> 6); s[28] = (unsigned char) ((s10 >> 14) | (s11 << 7)); s[29] = (unsigned char) (s11 >> 1); s[30] = (unsigned char) (s11 >> 9); s[31] = (unsigned char) (s11 >> 17); } /* Input: a[0]+256*a[1]+...+256^31*a[31] = a b[0]+256*b[1]+...+256^31*b[31] = b c[0]+256*c[1]+...+256^31*c[31] = c Output: s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l where l = 2^252 + 27742317777372353535851937790883648493. */ void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c) { int64_t a0 = 2097151 & load_3(a); int64_t a1 = 2097151 & (load_4(a + 2) >> 5); int64_t a2 = 2097151 & (load_3(a + 5) >> 2); int64_t a3 = 2097151 & (load_4(a + 7) >> 7); int64_t a4 = 2097151 & (load_4(a + 10) >> 4); int64_t a5 = 2097151 & (load_3(a + 13) >> 1); int64_t a6 = 2097151 & (load_4(a + 15) >> 6); int64_t a7 = 2097151 & (load_3(a + 18) >> 3); int64_t a8 = 2097151 & load_3(a + 21); int64_t a9 = 2097151 & (load_4(a + 23) >> 5); int64_t a10 = 2097151 & (load_3(a + 26) >> 2); int64_t a11 = (load_4(a + 28) >> 7); int64_t b0 = 2097151 & load_3(b); int64_t b1 = 2097151 & (load_4(b + 2) >> 5); int64_t b2 = 2097151 & (load_3(b + 5) >> 2); int64_t b3 = 2097151 & (load_4(b + 7) >> 7); int64_t b4 = 2097151 & (load_4(b + 10) >> 4); int64_t b5 = 2097151 & (load_3(b + 13) >> 1); int64_t b6 = 2097151 & (load_4(b + 15) >> 6); int64_t b7 = 2097151 & (load_3(b + 18) >> 3); int64_t b8 = 2097151 & load_3(b + 21); int64_t b9 = 2097151 & (load_4(b + 23) >> 5); int64_t b10 = 2097151 & (load_3(b + 26) >> 2); int64_t b11 = (load_4(b + 28) >> 7); int64_t c0 = 2097151 & load_3(c); int64_t c1 = 2097151 & (load_4(c + 2) >> 5); int64_t c2 = 2097151 & (load_3(c + 5) >> 2); int64_t c3 = 2097151 & (load_4(c + 7) >> 7); int64_t c4 = 2097151 & (load_4(c + 10) >> 4); int64_t c5 = 2097151 & (load_3(c + 13) >> 1); int64_t c6 = 2097151 & (load_4(c + 15) >> 6); int64_t c7 = 2097151 & (load_3(c + 18) >> 3); int64_t c8 = 2097151 & load_3(c + 21); int64_t c9 = 2097151 & (load_4(c + 23) >> 5); int64_t c10 = 2097151 & (load_3(c + 26) >> 2); int64_t c11 = (load_4(c + 28) >> 7); int64_t s0; int64_t s1; int64_t s2; int64_t s3; int64_t s4; int64_t s5; int64_t s6; int64_t s7; int64_t s8; int64_t s9; int64_t s10; int64_t s11; int64_t s12; int64_t s13; int64_t s14; int64_t s15; int64_t s16; int64_t s17; int64_t s18; int64_t s19; int64_t s20; int64_t s21; int64_t s22; int64_t s23; int64_t carry0; int64_t carry1; int64_t carry2; int64_t carry3; int64_t carry4; int64_t carry5; int64_t carry6; int64_t carry7; int64_t carry8; int64_t carry9; int64_t carry10; int64_t carry11; int64_t carry12; int64_t carry13; int64_t carry14; int64_t carry15; int64_t carry16; int64_t carry17; int64_t carry18; int64_t carry19; int64_t carry20; int64_t carry21; int64_t carry22; s0 = c0 + a0 * b0; s1 = c1 + a0 * b1 + a1 * b0; s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0; s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0; s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0; s6 = c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0; s7 = c7 + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 + a6 * b1 + a7 * b0; s8 = c8 + a0 * b8 + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 + a6 * b2 + a7 * b1 + a8 * b0; s9 = c9 + a0 * b9 + a1 * b8 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 + a6 * b3 + a7 * b2 + a8 * b1 + a9 * b0; s10 = c10 + a0 * b10 + a1 * b9 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 + a6 * b4 + a7 * b3 + a8 * b2 + a9 * b1 + a10 * b0; s11 = c11 + a0 * b11 + a1 * b10 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 + a6 * b5 + a7 * b4 + a8 * b3 + a9 * b2 + a10 * b1 + a11 * b0; s12 = a1 * b11 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 + a7 * b5 + a8 * b4 + a9 * b3 + a10 * b2 + a11 * b1; s13 = a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 + a8 * b5 + a9 * b4 + a10 * b3 + a11 * b2; s14 = a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 + a9 * b5 + a10 * b4 + a11 * b3; s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 + a10 * b5 + a11 * b4; s16 = a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5; s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6; s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7; s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8; s20 = a9 * b11 + a10 * b10 + a11 * b9; s21 = a10 * b11 + a11 * b10; s22 = a11 * b11; s23 = 0; carry0 = (s0 + (1 << 20)) >> 21; s1 += carry0; s0 -= carry0 << 21; carry2 = (s2 + (1 << 20)) >> 21; s3 += carry2; s2 -= carry2 << 21; carry4 = (s4 + (1 << 20)) >> 21; s5 += carry4; s4 -= carry4 << 21; carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; s6 -= carry6 << 21; carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; s8 -= carry8 << 21; carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; s10 -= carry10 << 21; carry12 = (s12 + (1 << 20)) >> 21; s13 += carry12; s12 -= carry12 << 21; carry14 = (s14 + (1 << 20)) >> 21; s15 += carry14; s14 -= carry14 << 21; carry16 = (s16 + (1 << 20)) >> 21; s17 += carry16; s16 -= carry16 << 21; carry18 = (s18 + (1 << 20)) >> 21; s19 += carry18; s18 -= carry18 << 21; carry20 = (s20 + (1 << 20)) >> 21; s21 += carry20; s20 -= carry20 << 21; carry22 = (s22 + (1 << 20)) >> 21; s23 += carry22; s22 -= carry22 << 21; carry1 = (s1 + (1 << 20)) >> 21; s2 += carry1; s1 -= carry1 << 21; carry3 = (s3 + (1 << 20)) >> 21; s4 += carry3; s3 -= carry3 << 21; carry5 = (s5 + (1 << 20)) >> 21; s6 += carry5; s5 -= carry5 << 21; carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; s7 -= carry7 << 21; carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; s9 -= carry9 << 21; carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; s11 -= carry11 << 21; carry13 = (s13 + (1 << 20)) >> 21; s14 += carry13; s13 -= carry13 << 21; carry15 = (s15 + (1 << 20)) >> 21; s16 += carry15; s15 -= carry15 << 21; carry17 = (s17 + (1 << 20)) >> 21; s18 += carry17; s17 -= carry17 << 21; carry19 = (s19 + (1 << 20)) >> 21; s20 += carry19; s19 -= carry19 << 21; carry21 = (s21 + (1 << 20)) >> 21; s22 += carry21; s21 -= carry21 << 21; s11 += s23 * 666643; s12 += s23 * 470296; s13 += s23 * 654183; s14 -= s23 * 997805; s15 += s23 * 136657; s16 -= s23 * 683901; s23 = 0; s10 += s22 * 666643; s11 += s22 * 470296; s12 += s22 * 654183; s13 -= s22 * 997805; s14 += s22 * 136657; s15 -= s22 * 683901; s22 = 0; s9 += s21 * 666643; s10 += s21 * 470296; s11 += s21 * 654183; s12 -= s21 * 997805; s13 += s21 * 136657; s14 -= s21 * 683901; s21 = 0; s8 += s20 * 666643; s9 += s20 * 470296; s10 += s20 * 654183; s11 -= s20 * 997805; s12 += s20 * 136657; s13 -= s20 * 683901; s20 = 0; s7 += s19 * 666643; s8 += s19 * 470296; s9 += s19 * 654183; s10 -= s19 * 997805; s11 += s19 * 136657; s12 -= s19 * 683901; s19 = 0; s6 += s18 * 666643; s7 += s18 * 470296; s8 += s18 * 654183; s9 -= s18 * 997805; s10 += s18 * 136657; s11 -= s18 * 683901; s18 = 0; carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; s6 -= carry6 << 21; carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; s8 -= carry8 << 21; carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; s10 -= carry10 << 21; carry12 = (s12 + (1 << 20)) >> 21; s13 += carry12; s12 -= carry12 << 21; carry14 = (s14 + (1 << 20)) >> 21; s15 += carry14; s14 -= carry14 << 21; carry16 = (s16 + (1 << 20)) >> 21; s17 += carry16; s16 -= carry16 << 21; carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; s7 -= carry7 << 21; carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; s9 -= carry9 << 21; carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; s11 -= carry11 << 21; carry13 = (s13 + (1 << 20)) >> 21; s14 += carry13; s13 -= carry13 << 21; carry15 = (s15 + (1 << 20)) >> 21; s16 += carry15; s15 -= carry15 << 21; s5 += s17 * 666643; s6 += s17 * 470296; s7 += s17 * 654183; s8 -= s17 * 997805; s9 += s17 * 136657; s10 -= s17 * 683901; s17 = 0; s4 += s16 * 666643; s5 += s16 * 470296; s6 += s16 * 654183; s7 -= s16 * 997805; s8 += s16 * 136657; s9 -= s16 * 683901; s16 = 0; s3 += s15 * 666643; s4 += s15 * 470296; s5 += s15 * 654183; s6 -= s15 * 997805; s7 += s15 * 136657; s8 -= s15 * 683901; s15 = 0; s2 += s14 * 666643; s3 += s14 * 470296; s4 += s14 * 654183; s5 -= s14 * 997805; s6 += s14 * 136657; s7 -= s14 * 683901; s14 = 0; s1 += s13 * 666643; s2 += s13 * 470296; s3 += s13 * 654183; s4 -= s13 * 997805; s5 += s13 * 136657; s6 -= s13 * 683901; s13 = 0; s0 += s12 * 666643; s1 += s12 * 470296; s2 += s12 * 654183; s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; s12 = 0; carry0 = (s0 + (1 << 20)) >> 21; s1 += carry0; s0 -= carry0 << 21; carry2 = (s2 + (1 << 20)) >> 21; s3 += carry2; s2 -= carry2 << 21; carry4 = (s4 + (1 << 20)) >> 21; s5 += carry4; s4 -= carry4 << 21; carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; s6 -= carry6 << 21; carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; s8 -= carry8 << 21; carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; s10 -= carry10 << 21; carry1 = (s1 + (1 << 20)) >> 21; s2 += carry1; s1 -= carry1 << 21; carry3 = (s3 + (1 << 20)) >> 21; s4 += carry3; s3 -= carry3 << 21; carry5 = (s5 + (1 << 20)) >> 21; s6 += carry5; s5 -= carry5 << 21; carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; s7 -= carry7 << 21; carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; s9 -= carry9 << 21; carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; s11 -= carry11 << 21; s0 += s12 * 666643; s1 += s12 * 470296; s2 += s12 * 654183; s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; s12 = 0; carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21; s0 += s12 * 666643; s1 += s12 * 470296; s2 += s12 * 654183; s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; s12 = 0; carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; s[0] = (unsigned char) (s0 >> 0); s[1] = (unsigned char) (s0 >> 8); s[2] = (unsigned char) ((s0 >> 16) | (s1 << 5)); s[3] = (unsigned char) (s1 >> 3); s[4] = (unsigned char) (s1 >> 11); s[5] = (unsigned char) ((s1 >> 19) | (s2 << 2)); s[6] = (unsigned char) (s2 >> 6); s[7] = (unsigned char) ((s2 >> 14) | (s3 << 7)); s[8] = (unsigned char) (s3 >> 1); s[9] = (unsigned char) (s3 >> 9); s[10] = (unsigned char) ((s3 >> 17) | (s4 << 4)); s[11] = (unsigned char) (s4 >> 4); s[12] = (unsigned char) (s4 >> 12); s[13] = (unsigned char) ((s4 >> 20) | (s5 << 1)); s[14] = (unsigned char) (s5 >> 7); s[15] = (unsigned char) ((s5 >> 15) | (s6 << 6)); s[16] = (unsigned char) (s6 >> 2); s[17] = (unsigned char) (s6 >> 10); s[18] = (unsigned char) ((s6 >> 18) | (s7 << 3)); s[19] = (unsigned char) (s7 >> 5); s[20] = (unsigned char) (s7 >> 13); s[21] = (unsigned char) (s8 >> 0); s[22] = (unsigned char) (s8 >> 8); s[23] = (unsigned char) ((s8 >> 16) | (s9 << 5)); s[24] = (unsigned char) (s9 >> 3); s[25] = (unsigned char) (s9 >> 11); s[26] = (unsigned char) ((s9 >> 19) | (s10 << 2)); s[27] = (unsigned char) (s10 >> 6); s[28] = (unsigned char) ((s10 >> 14) | (s11 << 7)); s[29] = (unsigned char) (s11 >> 1); s[30] = (unsigned char) (s11 >> 9); s[31] = (unsigned char) (s11 >> 17); } olm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/src/sc.h000066400000000000000000000004121311755073500210300ustar00rootroot00000000000000#ifndef SC_H #define SC_H /* The set of scalars is \Z/l where l = 2^252 + 27742317777372353535851937790883648493. */ void sc_reduce(unsigned char *s); void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c); #endifolm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/src/seed.c000066400000000000000000000012031311755073500213350ustar00rootroot00000000000000#include "ed25519.h" #ifndef ED25519_NO_SEED #ifdef _WIN32 #include #include #else #include #endif int ed25519_create_seed(unsigned char *seed) { #ifdef _WIN32 HCRYPTPROV prov; if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { return 1; } if (!CryptGenRandom(prov, 32, seed)) { CryptReleaseContext(prov, 0); return 1; } CryptReleaseContext(prov, 0); #else FILE *f = fopen("/dev/urandom", "rb"); if (f == NULL) { return 1; } fread(seed, 1, 32, f); fclose(f); #endif return 0; } #endifolm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/src/sha512.c000066400000000000000000000255201311755073500214300ustar00rootroot00000000000000/* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ #include "fixedint.h" #include "sha512.h" /* the K array */ static const uint64_t K[80] = { UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc), UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019), UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118), UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe), UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2), UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694), UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3), UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65), UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483), UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5), UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4), UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725), UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70), UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926), UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df), UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b), UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001), UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30), UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910), UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8), UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8), UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb), UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3), UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60), UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec), UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b), UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207), UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178), UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6), UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b), UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c), UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a), UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817) }; /* Various logical functions */ #define ROR64c(x, y) \ ( ((((x)&UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)(y)&UINT64_C(63))) | \ ((x)<<((uint64_t)(64-((y)&UINT64_C(63)))))) & UINT64_C(0xFFFFFFFFFFFFFFFF)) #define STORE64H(x, y) \ { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } #define LOAD64H(x, y) \ { x = (((uint64_t)((y)[0] & 255))<<56)|(((uint64_t)((y)[1] & 255))<<48) | \ (((uint64_t)((y)[2] & 255))<<40)|(((uint64_t)((y)[3] & 255))<<32) | \ (((uint64_t)((y)[4] & 255))<<24)|(((uint64_t)((y)[5] & 255))<<16) | \ (((uint64_t)((y)[6] & 255))<<8)|(((uint64_t)((y)[7] & 255))); } #define Ch(x,y,z) (z ^ (x & (y ^ z))) #define Maj(x,y,z) (((x | y) & z) | (x & y)) #define S(x, n) ROR64c(x, n) #define R(x, n) (((x) &UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)n)) #define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) #define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) #define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) #define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) #ifndef MIN #define MIN(x, y) ( ((x)<(y))?(x):(y) ) #endif /* compress 1024-bits */ static int sha512_compress(sha512_context *md, unsigned char *buf) { uint64_t S[8], W[80], t0, t1; int i; /* copy state into S */ for (i = 0; i < 8; i++) { S[i] = md->state[i]; } /* copy the state into 1024-bits into W[0..15] */ for (i = 0; i < 16; i++) { LOAD64H(W[i], buf + (8*i)); } /* fill W[16..79] */ for (i = 16; i < 80; i++) { W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; } /* Compress */ #define RND(a,b,c,d,e,f,g,h,i) \ t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ t1 = Sigma0(a) + Maj(a, b, c);\ d += t0; \ h = t0 + t1; for (i = 0; i < 80; i += 8) { RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0); RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1); RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2); RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3); RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4); RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5); RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6); RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7); } #undef RND /* feedback */ for (i = 0; i < 8; i++) { md->state[i] = md->state[i] + S[i]; } return 0; } /** Initialize the hash state @param md The hash state you wish to initialize @return 0 if successful */ int sha512_init(sha512_context * md) { if (md == NULL) return 1; md->curlen = 0; md->length = 0; md->state[0] = UINT64_C(0x6a09e667f3bcc908); md->state[1] = UINT64_C(0xbb67ae8584caa73b); md->state[2] = UINT64_C(0x3c6ef372fe94f82b); md->state[3] = UINT64_C(0xa54ff53a5f1d36f1); md->state[4] = UINT64_C(0x510e527fade682d1); md->state[5] = UINT64_C(0x9b05688c2b3e6c1f); md->state[6] = UINT64_C(0x1f83d9abfb41bd6b); md->state[7] = UINT64_C(0x5be0cd19137e2179); return 0; } /** Process a block of memory though the hash @param md The hash state @param in The data to hash @param inlen The length of the data (octets) @return 0 if successful */ int sha512_update (sha512_context * md, const unsigned char *in, size_t inlen) { size_t n; size_t i; int err; if (md == NULL) return 1; if (in == NULL) return 1; if (md->curlen > sizeof(md->buf)) { return 1; } while (inlen > 0) { if (md->curlen == 0 && inlen >= 128) { if ((err = sha512_compress (md, (unsigned char *)in)) != 0) { return err; } md->length += 128 * 8; in += 128; inlen -= 128; } else { n = MIN(inlen, (128 - md->curlen)); for (i = 0; i < n; i++) { md->buf[i + md->curlen] = in[i]; } md->curlen += n; in += n; inlen -= n; if (md->curlen == 128) { if ((err = sha512_compress (md, md->buf)) != 0) { return err; } md->length += 8*128; md->curlen = 0; } } } return 0; } /** Terminate the hash to get the digest @param md The hash state @param out [out] The destination of the hash (64 bytes) @return 0 if successful */ int sha512_final(sha512_context * md, unsigned char *out) { int i; if (md == NULL) return 1; if (out == NULL) return 1; if (md->curlen >= sizeof(md->buf)) { return 1; } /* increase the length of the message */ md->length += md->curlen * UINT64_C(8); /* append the '1' bit */ md->buf[md->curlen++] = (unsigned char)0x80; /* if the length is currently above 112 bytes we append zeros * then compress. Then we can fall back to padding zeros and length * encoding like normal. */ if (md->curlen > 112) { while (md->curlen < 128) { md->buf[md->curlen++] = (unsigned char)0; } sha512_compress(md, md->buf); md->curlen = 0; } /* pad upto 120 bytes of zeroes * note: that from 112 to 120 is the 64 MSB of the length. We assume that you won't hash * > 2^64 bits of data... :-) */ while (md->curlen < 120) { md->buf[md->curlen++] = (unsigned char)0; } /* store length */ STORE64H(md->length, md->buf+120); sha512_compress(md, md->buf); /* copy output */ for (i = 0; i < 8; i++) { STORE64H(md->state[i], out+(8*i)); } return 0; } int sha512(const unsigned char *message, size_t message_len, unsigned char *out) { sha512_context ctx; int ret; if ((ret = sha512_init(&ctx))) return ret; if ((ret = sha512_update(&ctx, message, message_len))) return ret; if ((ret = sha512_final(&ctx, out))) return ret; return 0; } olm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/src/sha512.h000066400000000000000000000007501311755073500214330ustar00rootroot00000000000000#ifndef SHA512_H #define SHA512_H #include #include "fixedint.h" /* state */ typedef struct sha512_context_ { uint64_t length, state[8]; size_t curlen; unsigned char buf[128]; } sha512_context; int sha512_init(sha512_context * md); int sha512_final(sha512_context * md, unsigned char *out); int sha512_update(sha512_context * md, const unsigned char *in, size_t inlen); int sha512(const unsigned char *message, size_t message_len, unsigned char *out); #endifolm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/src/sign.c000066400000000000000000000014731311755073500213660ustar00rootroot00000000000000#include "ed25519.h" #include "sha512.h" #include "ge.h" #include "sc.h" void ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key) { sha512_context hash; unsigned char hram[64]; unsigned char r[64]; ge_p3 R; sha512_init(&hash); sha512_update(&hash, private_key + 32, 32); sha512_update(&hash, message, message_len); sha512_final(&hash, r); sc_reduce(r); ge_scalarmult_base(&R, r); ge_p3_tobytes(signature, &R); sha512_init(&hash); sha512_update(&hash, signature, 32); sha512_update(&hash, public_key, 32); sha512_update(&hash, message, message_len); sha512_final(&hash, hram); sc_reduce(hram); sc_muladd(signature + 32, hram, private_key, r); } olm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/src/verify.c000066400000000000000000000025271311755073500217330ustar00rootroot00000000000000#include "ed25519.h" #include "sha512.h" #include "ge.h" #include "sc.h" static int consttime_equal(const unsigned char *x, const unsigned char *y) { unsigned char r = 0; r = x[0] ^ y[0]; #define F(i) r |= x[i] ^ y[i] F(1); F(2); F(3); F(4); F(5); F(6); F(7); F(8); F(9); F(10); F(11); F(12); F(13); F(14); F(15); F(16); F(17); F(18); F(19); F(20); F(21); F(22); F(23); F(24); F(25); F(26); F(27); F(28); F(29); F(30); F(31); #undef F return !r; } int ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key) { unsigned char h[64]; unsigned char checker[32]; sha512_context hash; ge_p3 A; ge_p2 R; if (signature[63] & 224) { return 0; } if (ge_frombytes_negate_vartime(&A, public_key) != 0) { return 0; } sha512_init(&hash); sha512_update(&hash, signature, 32); sha512_update(&hash, public_key, 32); sha512_update(&hash, message, message_len); sha512_final(&hash, h); sc_reduce(h); ge_double_scalarmult_vartime(&R, h, &A, signature + 32); ge_tobytes(checker, &R); if (!consttime_equal(checker, signature)) { return 0; } return 1; } olm-2.2.2+git20170526.0fd768e+dfsg/lib/ed25519/test.c000066400000000000000000000111021311755073500206040ustar00rootroot00000000000000#include #include #include #include /* #define ED25519_DLL */ #include "src/ed25519.h" #include "src/ge.h" #include "src/sc.h" int main() { unsigned char public_key[32], private_key[64], seed[32], scalar[32]; unsigned char other_public_key[32], other_private_key[64]; unsigned char shared_secret[32], other_shared_secret[32]; unsigned char signature[64]; clock_t start; clock_t end; int i; const unsigned char message[] = "Hello, world!"; const int message_len = strlen((char*) message); /* create a random seed, and a keypair out of that seed */ ed25519_create_seed(seed); ed25519_create_keypair(public_key, private_key, seed); /* create signature on the message with the keypair */ ed25519_sign(signature, message, message_len, public_key, private_key); /* verify the signature */ if (ed25519_verify(signature, message, message_len, public_key)) { printf("valid signature\n"); } else { printf("invalid signature\n"); } /* create scalar and add it to the keypair */ ed25519_create_seed(scalar); ed25519_add_scalar(public_key, private_key, scalar); /* create signature with the new keypair */ ed25519_sign(signature, message, message_len, public_key, private_key); /* verify the signature with the new keypair */ if (ed25519_verify(signature, message, message_len, public_key)) { printf("valid signature\n"); } else { printf("invalid signature\n"); } /* make a slight adjustment and verify again */ signature[44] ^= 0x10; if (ed25519_verify(signature, message, message_len, public_key)) { printf("did not detect signature change\n"); } else { printf("correctly detected signature change\n"); } /* generate two keypairs for testing key exchange */ ed25519_create_seed(seed); ed25519_create_keypair(public_key, private_key, seed); ed25519_create_seed(seed); ed25519_create_keypair(other_public_key, other_private_key, seed); /* create two shared secrets - from both perspectives - and check if they're equal */ ed25519_key_exchange(shared_secret, other_public_key, private_key); ed25519_key_exchange(other_shared_secret, public_key, other_private_key); for (i = 0; i < 32; ++i) { if (shared_secret[i] != other_shared_secret[i]) { printf("key exchange was incorrect\n"); break; } } if (i == 32) { printf("key exchange was correct\n"); } /* test performance */ printf("testing seed generation performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_create_seed(seed); } end = clock(); printf("%fus per seed\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); printf("testing key generation performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_create_keypair(public_key, private_key, seed); } end = clock(); printf("%fus per keypair\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); printf("testing sign performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_sign(signature, message, message_len, public_key, private_key); } end = clock(); printf("%fus per signature\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); printf("testing verify performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_verify(signature, message, message_len, public_key); } end = clock(); printf("%fus per signature\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); printf("testing keypair scalar addition performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_add_scalar(public_key, private_key, scalar); } end = clock(); printf("%fus per keypair\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); printf("testing public key scalar addition performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_add_scalar(public_key, NULL, scalar); } end = clock(); printf("%fus per key\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); printf("testing key exchange performance: "); start = clock(); for (i = 0; i < 10000; ++i) { ed25519_key_exchange(shared_secret, other_public_key, private_key); } end = clock(); printf("%fus per shared secret\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); return 0; } olm-2.2.2+git20170526.0fd768e+dfsg/python/000077500000000000000000000000001311755073500172435ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/python/.gdb_history000066400000000000000000000040431311755073500215620ustar00rootroot00000000000000b _olm_enc_input r l p key p key_lenght p key_length b _olm_enc_input r key[12] p key[12] p key[11] key[11]='\0' p key[11]='\0' p key[11] key_length=12 p key_length=12 n c b _olm_enc_input r r r b olm_decrypt r l b 677 c s fin s s fin s s fin s l n l l s s n l n l p reader p *this n p chain p receiver_chains p receiver_chains.length() p receiver_chains.size() p reader p reader.ratchet_key r r b olm_account_one_time_keys r l s n p *this p one_time_keys p one_time_keys.length p one_time_keys.length() p one_time_keys.len() p one_time_keys.size() p one_time_keys.count() p one_time_keys.data p one_time_keys._data p &one_time_keys._data l n q r b olm_create_inbound_session r b olm_create_inbound_session_from r r r b olm_create_inbound_session_from r b olm_create_inbound_session b olm_create_inbound_session r l n l s b olm_create_inbound_session r l l n s f s fin s s fin s l n l l - l l l n p our_one_time_key p *our_one_time_key l n l n p bob_one_time_key p alice_identity_key p alice_base_key p bob_identity_key x alice_identity_key x &alice_identity_key x /32x &alice_identity_key x /32b &alice_identity_key l l l n b olm_decrypt c l l b 'olm::Session::decrypt' c l l n l n p reader p reader 5*128 p 5*128 p 0xb0 - 0x80 p 0xb0 - 0x80 + 640 l n s l n p reader n l n p max_length p reader.ciphertext_length l n l p receiver_chains p &receiver_chains ._data p &receiver_chains ._data[1] n s s l n p new_chain.index p reader.counter n l l n s s n l x key x /16b key l l n p keys _olm_crypto_aes_decrypt_cbc&keys.aes_key, &keys.aes_iv, ciphertext, ciphertext_length, plaintext p _olm_crypto_aes_decrypt_cbc(&keys.aes_key, &keys.aes_iv, ciphertext, ciphertext_length, plaintext) p plaintext r b olm_account_identity_keys l r b olm_unpickle_account r l n p object.last_error l l - l b 268 r c s l l p end-pos x /246b pos x /246x pos x /82x pos+164 x /82x pos+132 pos p pos x /246x pos r r b olm_create_outbound_session r n l p id_key_length p ot_key_length p olm::decode_base64_length(id_key_length) p olm::decode_base64_length(ot_key_length) p CURVE25519_KEY_LENGTH olm-2.2.2+git20170526.0fd768e+dfsg/python/.gitignore000066400000000000000000000000741311755073500212340ustar00rootroot00000000000000*.pyc /*.account /*.session /*.group_session /group_message olm-2.2.2+git20170526.0fd768e+dfsg/python/olm/000077500000000000000000000000001311755073500200325ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/python/olm/__init__.py000066400000000000000000000003161311755073500221430ustar00rootroot00000000000000from .account import Account from .session import Session from .outbound_group_session import OutboundGroupSession from .inbound_group_session import InboundGroupSession from .utility import ed25519_verify olm-2.2.2+git20170526.0fd768e+dfsg/python/olm/__main__.py000077500000000000000000000420351311755073500221330ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function import argparse import json import os import sys import yaml from . import * def read_base64_file(filename): """Read a base64 file, dropping any CR/LF characters""" with open(filename, "rb") as f: return f.read().translate(None, "\r\n") def build_arg_parser(): parser = argparse.ArgumentParser() parser.add_argument("--key", help="Account encryption key", default="") commands = parser.add_subparsers() create_account = commands.add_parser("create_account", help="Create a new account") create_account.add_argument("account_file", help="Local account file") def do_create_account(args): if os.path.exists(args.account_file): sys.stderr.write("Account %r file already exists" % ( args.account_file, )) sys.exit(1) account = Account() account.create() with open(args.account_file, "wb") as f: f.write(account.pickle(args.key)) create_account.set_defaults(func=do_create_account) keys = commands.add_parser("keys", help="List public keys for an account") keys.add_argument("account_file", help="Local account file") keys.add_argument("--json", action="store_true", help="Output as JSON") def do_keys(args): account = Account() account.unpickle(args.key, read_base64_file(args.account_file)) result = { "account_keys": account.identity_keys(), "one_time_keys": account.one_time_keys(), } try: if args.json: json.dump(result, sys.stdout, indent=4) else: yaml.safe_dump(result, sys.stdout, default_flow_style=False) except: pass keys.set_defaults(func=do_keys) def do_id_key(args): account = Account() account.unpickle(args.key, read_base64_file(args.account_file)) print(account.identity_keys()['curve25519']) id_key = commands.add_parser( "identity_key", help="Get the public part of the identity key for an account", ) id_key.add_argument("account_file", help="Local account file") id_key.set_defaults(func=do_id_key) def do_signing_key(args): account = Account() account.unpickle(args.key, read_base64_file(args.account_file)) print(account.identity_keys()['ed25519']) signing_key = commands.add_parser( "signing_key", help="Get the public part of the signing key for an account", ) signing_key.add_argument("account_file", help="Local account file") signing_key.set_defaults(func=do_signing_key) def do_one_time_key(args): account = Account() account.unpickle(args.key, read_base64_file(args.account_file)) keys = account.one_time_keys()['curve25519'].values() key_num = args.key_num if key_num < 1 or key_num > len(keys): print( "Invalid key number %i: %i keys available" % ( key_num, len(keys), ), file=sys.stderr, ) sys.exit(1) print(keys[key_num-1]) one_time_key = commands.add_parser( "one_time_key", help="Get a one-time key for the account", ) one_time_key.add_argument("account_file", help="Local account file") one_time_key.add_argument("--key-num", "-n", type=int, default=1, help="Index of key to retrieve (default: 1)") one_time_key.set_defaults(func=do_one_time_key) sign = commands.add_parser("sign", help="Sign a message") sign.add_argument("account_file", help="Local account file") sign.add_argument("message_file", help="Message to sign") sign.add_argument("signature_file", help="Signature to output") def do_sign(args): account = Account() account.unpickle(args.key, read_base64_file(args.account_file)) with open_in(args.message_file) as f: message = f.read() signature = account.sign(message) with open_out(args.signature_file) as f: f.write(signature) sign.set_defaults(func=do_sign) generate_keys = commands.add_parser("generate_keys", help="Generate one time keys") generate_keys.add_argument("account_file", help="Local account file") generate_keys.add_argument("count", type=int, help="Number of keys to generate") def do_generate_keys(args): account = Account() account.unpickle(args.key, read_base64_file(args.account_file)) account.generate_one_time_keys(args.count) with open(args.account_file, "wb") as f: f.write(account.pickle(args.key)) generate_keys.set_defaults(func=do_generate_keys) outbound = commands.add_parser("outbound", help="Create an outbound session") outbound.add_argument("account_file", help="Local account file") outbound.add_argument("session_file", help="Local session file") outbound.add_argument("identity_key", help="Remote identity key") outbound.add_argument("one_time_key", help="Remote one time key") def do_outbound(args): if os.path.exists(args.session_file): sys.stderr.write("Session %r file already exists" % ( args.session_file, )) sys.exit(1) account = Account() account.unpickle(args.key, read_base64_file(args.account_file)) session = Session() session.create_outbound( account, args.identity_key, args.one_time_key ) with open(args.session_file, "wb") as f: f.write(session.pickle(args.key)) outbound.set_defaults(func=do_outbound) def open_in(path): if path == "-": return sys.stdin else: return open(path, "rb") def open_out(path): if path == "-": return sys.stdout else: return open(path, "wb") inbound = commands.add_parser("inbound", help="Create an inbound session") inbound.add_argument("account_file", help="Local account file") inbound.add_argument("session_file", help="Local session file") inbound.add_argument("message_file", help="Message", default="-") inbound.add_argument("plaintext_file", help="Plaintext", default="-") def do_inbound(args): if os.path.exists(args.session_file): sys.stderr.write("Session %r file already exists" % ( args.session_file, )) sys.exit(1) account = Account() account.unpickle(args.key, read_base64_file(args.account_file)) with open_in(args.message_file) as f: message_type = f.read(8) message = f.read() if message_type != "PRE_KEY ": sys.stderr.write("Expecting a PRE_KEY message") sys.exit(1) session = Session() session.create_inbound(account, message) plaintext = session.decrypt(0, message) with open(args.session_file, "wb") as f: f.write(session.pickle(args.key)) with open_out(args.plaintext_file) as f: f.write(plaintext) inbound.set_defaults(func=do_inbound) session_id = commands.add_parser("session_id", help="Session ID") session_id.add_argument("session_file", help="Local session file") def do_session_id(args): session = Session() session.unpickle(args.key, read_base64_file(args.session_file)) sys.stdout.write(session.session_id() + "\n") session_id.set_defaults(func=do_session_id) encrypt = commands.add_parser("encrypt", help="Encrypt a message") encrypt.add_argument("session_file", help="Local session file") encrypt.add_argument("plaintext_file", help="Plaintext", default="-") encrypt.add_argument("message_file", help="Message", default="-") def do_encrypt(args): session = Session() session.unpickle(args.key, read_base64_file(args.session_file)) with open_in(args.plaintext_file) as f: plaintext = f.read() message_type, message = session.encrypt(plaintext) with open(args.session_file, "wb") as f: f.write(session.pickle(args.key)) with open_out(args.message_file) as f: f.write(["PRE_KEY ", "MESSAGE "][message_type]) f.write(message) encrypt.set_defaults(func=do_encrypt) decrypt = commands.add_parser("decrypt", help="Decrypt a message") decrypt.add_argument("session_file", help="Local session file") decrypt.add_argument("message_file", help="Message", default="-") decrypt.add_argument("plaintext_file", help="Plaintext", default="-") def do_decrypt(args): session = Session() session.unpickle(args.key, read_base64_file(args.session_file)) with open_in(args.message_file) as f: message_type = f.read(8) message = f.read() if message_type not in {"PRE_KEY ", "MESSAGE "}: sys.stderr.write("Expecting a PRE_KEY or MESSAGE message") sys.exit(1) message_type = 1 if message_type == "MESSAGE " else 0 plaintext = session.decrypt(message_type, message) with open(args.session_file, "wb") as f: f.write(session.pickle(args.key)) with open_out(args.plaintext_file) as f: f.write(plaintext) decrypt.set_defaults(func=do_decrypt) outbound_group = commands.add_parser( "outbound_group", help="Create an outbound group session", ) outbound_group.add_argument("session_file", help="Local group session file") outbound_group.set_defaults(func=do_outbound_group) group_credentials = commands.add_parser( "group_credentials", help="Export the current outbound group session credentials", ) group_credentials.add_argument( "session_file", help="Local outbound group session file", ) group_credentials.add_argument( "credentials_file", help="File to write credentials to (default stdout)", type=argparse.FileType('w'), nargs='?', default=sys.stdout, ) group_credentials.set_defaults(func=do_group_credentials) group_encrypt = commands.add_parser( "group_encrypt", help="Encrypt a group message", ) group_encrypt.add_argument("session_file", help="Local outbound group session file") group_encrypt.add_argument("plaintext_file", help="Plaintext file (default stdin)", type=argparse.FileType('rb'), nargs='?', default=sys.stdin) group_encrypt.add_argument("message_file", help="Message file (default stdout)", type=argparse.FileType('w'), nargs='?', default=sys.stdout) group_encrypt.set_defaults(func=do_group_encrypt) inbound_group = commands.add_parser( "inbound_group", help=("Create an inbound group session based on credentials from an " + "outbound group session")) inbound_group.add_argument("session_file", help="Local inbound group session file") inbound_group.add_argument( "credentials_file", help="File to read credentials from (default stdin)", type=argparse.FileType('r'), nargs='?', default=sys.stdin, ) inbound_group.set_defaults(func=do_inbound_group) import_inbound_group = commands.add_parser( "import_inbound_group", help="Create an inbound group session based an exported inbound group" ) import_inbound_group.add_argument("session_file", help="Local inbound group session file") import_inbound_group.add_argument( "export_file", help="File to read credentials from (default stdin)", type=argparse.FileType('r'), nargs='?', default=sys.stdin, ) import_inbound_group.set_defaults(func=do_import_inbound_group) group_decrypt = commands.add_parser("group_decrypt", help="Decrypt a group message") group_decrypt.add_argument("session_file", help="Local inbound group session file") group_decrypt.add_argument("message_file", help="Message file (default stdin)", type=argparse.FileType('r'), nargs='?', default=sys.stdin) group_decrypt.add_argument("plaintext_file", help="Plaintext file (default stdout)", type=argparse.FileType('wb'), nargs='?', default=sys.stdout) group_decrypt.set_defaults(func=do_group_decrypt) export_inbound_group = commands.add_parser( "export_inbound_group", help="Export the keys for an inbound group session", ) export_inbound_group.add_argument( "session_file", help="Local inbound group session file", ) export_inbound_group.add_argument( "export_file", help="File to export to (default stdout)", type=argparse.FileType('w'), nargs='?', default=sys.stdout, ) export_inbound_group.add_argument( "--message_index", help=("Index to export session at. Defaults to the earliest known " + "index"), type=int, ) export_inbound_group.set_defaults(func=do_export_inbound_group) ed25519_verify = commands.add_parser("ed25519_verify", help="Verify an ed25519 signature") ed25519_verify.add_argument( "signing_key", help="Public signing key used to create the signature" ) ed25519_verify.add_argument("signature", help="Signature to be verified") ed25519_verify.add_argument("message_file", help="Message file (default stdin)", type=argparse.FileType('r'), nargs='?', default=sys.stdin) ed25519_verify.set_defaults(func=do_verify_ed25519_signature) return parser def do_outbound_group(args): if os.path.exists(args.session_file): sys.stderr.write("Session %r file already exists" % ( args.session_file, )) sys.exit(1) session = OutboundGroupSession() with open(args.session_file, "wb") as f: f.write(session.pickle(args.key)) def do_group_encrypt(args): session = OutboundGroupSession() session.unpickle(args.key, read_base64_file(args.session_file)) plaintext = args.plaintext_file.read() message = session.encrypt(plaintext) with open(args.session_file, "wb") as f: f.write(session.pickle(args.key)) args.message_file.write(message) def do_group_credentials(args): session = OutboundGroupSession() session.unpickle(args.key, read_base64_file(args.session_file)) result = { 'message_index': session.message_index(), 'session_key': session.session_key(), } json.dump(result, args.credentials_file, indent=4) def do_inbound_group(args): if os.path.exists(args.session_file): sys.stderr.write("Session %r file already exists\n" % ( args.session_file, )) sys.exit(1) credentials = json.load(args.credentials_file) for k in ('session_key', ): if k not in credentials: sys.stderr.write("Credentials file is missing %s\n" % k) sys.exit(1) session = InboundGroupSession() session.init(credentials['session_key']) with open(args.session_file, "wb") as f: f.write(session.pickle(args.key)) def do_import_inbound_group(args): if os.path.exists(args.session_file): sys.stderr.write("Session %r file already exists\n" % ( args.session_file, )) sys.exit(1) data = args.export_file.read().translate(None, "\r\n") session = InboundGroupSession() session.import_session(data) with open(args.session_file, "wb") as f: f.write(session.pickle(args.key)) def do_group_decrypt(args): session = InboundGroupSession() session.unpickle(args.key, read_base64_file(args.session_file)) message = args.message_file.read() plaintext, message_index = session.decrypt(message) with open(args.session_file, "wb") as f: f.write(session.pickle(args.key)) args.plaintext_file.write(plaintext) def do_export_inbound_group(args): session = InboundGroupSession() session.unpickle(args.key, read_base64_file(args.session_file)) index = args.message_index if index is None: # default to first known index index = session.first_known_index() args.export_file.write(session.export_session(index)) def do_verify_ed25519_signature(args): message = args.message_file.read() ed25519_verify(args.signing_key, message, args.signature) if __name__ == '__main__': parser = build_arg_parser() args = parser.parse_args() args.func(args) olm-2.2.2+git20170526.0fd768e+dfsg/python/olm/_base.py000066400000000000000000000004151311755073500214550ustar00rootroot00000000000000import os.path from ctypes import * lib = cdll.LoadLibrary(os.path.join( os.path.dirname(__file__), "..", "..", "build", "libolm.so.2") ) lib.olm_error.argtypes = [] lib.olm_error.restypes = c_size_t ERR = lib.olm_error() class OlmError(Exception): pass olm-2.2.2+git20170526.0fd768e+dfsg/python/olm/account.py000066400000000000000000000101561311755073500220430ustar00rootroot00000000000000import json from os import urandom from ._base import * lib.olm_account_size.argtypes = [] lib.olm_account_size.restype = c_size_t lib.olm_account.argtypes = [c_void_p] lib.olm_account.restype = c_void_p lib.olm_account_last_error.argtypes = [c_void_p] lib.olm_account_last_error.restype = c_char_p def account_errcheck(res, func, args): if res == ERR: raise OlmError("%s: %s" % ( func.__name__, lib.olm_account_last_error(args[0]) )) return res def account_function(func, *types): func.argtypes = (c_void_p,) + types func.restypes = c_size_t func.errcheck = account_errcheck account_function( lib.olm_pickle_account, c_void_p, c_size_t, c_void_p, c_size_t ) account_function( lib.olm_unpickle_account, c_void_p, c_size_t, c_void_p, c_size_t ) account_function(lib.olm_create_account_random_length) account_function(lib.olm_create_account, c_void_p, c_size_t) account_function(lib.olm_account_identity_keys_length) account_function(lib.olm_account_identity_keys, c_void_p, c_size_t) account_function(lib.olm_account_signature_length) account_function(lib.olm_account_sign, c_void_p, c_size_t, c_void_p, c_size_t) account_function(lib.olm_account_one_time_keys_length) account_function(lib.olm_account_one_time_keys, c_void_p, c_size_t) account_function(lib.olm_account_mark_keys_as_published) account_function(lib.olm_account_max_number_of_one_time_keys) account_function(lib.olm_pickle_account_length) account_function( lib.olm_account_generate_one_time_keys_random_length, c_size_t ) account_function( lib.olm_account_generate_one_time_keys, c_size_t, c_void_p, c_size_t ) class Account(object): def __init__(self): self.buf = create_string_buffer(lib.olm_account_size()) self.ptr = lib.olm_account(self.buf) def create(self): random_length = lib.olm_create_account_random_length(self.ptr) random = urandom(random_length) random_buffer = create_string_buffer(random) lib.olm_create_account(self.ptr, random_buffer, random_length) def pickle(self, key): key_buffer = create_string_buffer(key) pickle_length = lib.olm_pickle_account_length(self.ptr) pickle_buffer = create_string_buffer(pickle_length) lib.olm_pickle_account( self.ptr, key_buffer, len(key), pickle_buffer, pickle_length ) return pickle_buffer.raw def unpickle(self, key, pickle): key_buffer = create_string_buffer(key) pickle_buffer = create_string_buffer(pickle) lib.olm_unpickle_account( self.ptr, key_buffer, len(key), pickle_buffer, len(pickle) ) def identity_keys(self): out_length = lib.olm_account_identity_keys_length(self.ptr) out_buffer = create_string_buffer(out_length) lib.olm_account_identity_keys( self.ptr, out_buffer, out_length ) return json.loads(out_buffer.raw) def sign(self, message): out_length = lib.olm_account_signature_length(self.ptr) message_buffer = create_string_buffer(message) out_buffer = create_string_buffer(out_length) lib.olm_account_sign( self.ptr, message_buffer, len(message), out_buffer, out_length ) return out_buffer.raw def one_time_keys(self): out_length = lib.olm_account_one_time_keys_length(self.ptr) out_buffer = create_string_buffer(out_length) lib.olm_account_one_time_keys(self.ptr, out_buffer, out_length) return json.loads(out_buffer.raw) def mark_keys_as_published(self): lib.olm_account_mark_keys_as_published(self.ptr) def max_number_of_one_time_keys(self): return lib.olm_account_max_number_of_one_time_keys(self.ptr) def generate_one_time_keys(self, count): random_length = lib.olm_account_generate_one_time_keys_random_length( self.ptr, count ) random = urandom(random_length) random_buffer = create_string_buffer(random) lib.olm_account_generate_one_time_keys( self.ptr, count, random_buffer, random_length ) def clear(self): pass olm-2.2.2+git20170526.0fd768e+dfsg/python/olm/inbound_group_session.py000066400000000000000000000106741311755073500250310ustar00rootroot00000000000000import json from ._base import * lib.olm_inbound_group_session_size.argtypes = [] lib.olm_inbound_group_session_size.restype = c_size_t lib.olm_inbound_group_session.argtypes = [c_void_p] lib.olm_inbound_group_session.restype = c_void_p lib.olm_inbound_group_session_last_error.argtypes = [c_void_p] lib.olm_inbound_group_session_last_error.restype = c_char_p def inbound_group_session_errcheck(res, func, args): if res == ERR: raise OlmError("%s: %s" % ( func.__name__, lib.olm_inbound_group_session_last_error(args[0]) )) return res def inbound_group_session_function(func, *types): func.argtypes = (c_void_p,) + types func.restypes = c_size_t func.errcheck = inbound_group_session_errcheck inbound_group_session_function( lib.olm_pickle_inbound_group_session, c_void_p, c_size_t, c_void_p, c_size_t, ) inbound_group_session_function( lib.olm_unpickle_inbound_group_session, c_void_p, c_size_t, c_void_p, c_size_t, ) inbound_group_session_function( lib.olm_init_inbound_group_session, c_void_p, c_size_t ) inbound_group_session_function( lib.olm_import_inbound_group_session, c_void_p, c_size_t ) inbound_group_session_function( lib.olm_group_decrypt_max_plaintext_length, c_void_p, c_size_t ) inbound_group_session_function( lib.olm_group_decrypt, c_void_p, c_size_t, # message c_void_p, c_size_t, # plaintext POINTER(c_uint32), # message_index ) inbound_group_session_function( lib.olm_inbound_group_session_id_length, ) inbound_group_session_function( lib.olm_inbound_group_session_id, c_void_p, c_size_t, ) lib.olm_inbound_group_session_first_known_index.argtypes = (c_void_p,) lib.olm_inbound_group_session_first_known_index.restypes = c_uint32 inbound_group_session_function( lib.olm_export_inbound_group_session_length, ) inbound_group_session_function( lib.olm_export_inbound_group_session, c_void_p, c_size_t, c_uint32, ) class InboundGroupSession(object): def __init__(self): self.buf = create_string_buffer(lib.olm_inbound_group_session_size()) self.ptr = lib.olm_inbound_group_session(self.buf) def pickle(self, key): key_buffer = create_string_buffer(key) pickle_length = lib.olm_pickle_inbound_group_session_length(self.ptr) pickle_buffer = create_string_buffer(pickle_length) lib.olm_pickle_inbound_group_session( self.ptr, key_buffer, len(key), pickle_buffer, pickle_length ) return pickle_buffer.raw def unpickle(self, key, pickle): key_buffer = create_string_buffer(key) pickle_buffer = create_string_buffer(pickle) lib.olm_unpickle_inbound_group_session( self.ptr, key_buffer, len(key), pickle_buffer, len(pickle) ) def init(self, session_key): key_buffer = create_string_buffer(session_key) lib.olm_init_inbound_group_session( self.ptr, key_buffer, len(session_key) ) def import_session(self, session_key): key_buffer = create_string_buffer(session_key) lib.olm_import_inbound_group_session( self.ptr, key_buffer, len(session_key) ) def decrypt(self, message): message_buffer = create_string_buffer(message) max_plaintext_length = lib.olm_group_decrypt_max_plaintext_length( self.ptr, message_buffer, len(message) ) plaintext_buffer = create_string_buffer(max_plaintext_length) message_buffer = create_string_buffer(message) message_index = c_uint32() plaintext_length = lib.olm_group_decrypt( self.ptr, message_buffer, len(message), plaintext_buffer, max_plaintext_length, byref(message_index) ) return plaintext_buffer.raw[:plaintext_length], message_index.value def session_id(self): id_length = lib.olm_inbound_group_session_id_length(self.ptr) id_buffer = create_string_buffer(id_length) lib.olm_inbound_group_session_id(self.ptr, id_buffer, id_length) return id_buffer.raw def first_known_index(self): return lib.olm_inbound_group_session_first_known_index(self.ptr) def export_session(self, message_index): length = lib.olm_export_inbound_group_session_length(self.ptr) buffer = create_string_buffer(length) lib.olm_export_inbound_group_session(self.ptr, buffer, length, message_index) return buffer.raw olm-2.2.2+git20170526.0fd768e+dfsg/python/olm/outbound_group_session.py000066400000000000000000000101021311755073500252140ustar00rootroot00000000000000import json from os import urandom from ._base import * lib.olm_outbound_group_session_size.argtypes = [] lib.olm_outbound_group_session_size.restype = c_size_t lib.olm_outbound_group_session.argtypes = [c_void_p] lib.olm_outbound_group_session.restype = c_void_p lib.olm_outbound_group_session_last_error.argtypes = [c_void_p] lib.olm_outbound_group_session_last_error.restype = c_char_p def outbound_group_session_errcheck(res, func, args): if res == ERR: raise OlmError("%s: %s" % ( func.__name__, lib.olm_outbound_group_session_last_error(args[0]) )) return res def outbound_group_session_function(func, *types): func.argtypes = (c_void_p,) + types func.restypes = c_size_t func.errcheck = outbound_group_session_errcheck outbound_group_session_function( lib.olm_pickle_outbound_group_session, c_void_p, c_size_t, c_void_p, c_size_t, ) outbound_group_session_function( lib.olm_unpickle_outbound_group_session, c_void_p, c_size_t, c_void_p, c_size_t, ) outbound_group_session_function( lib.olm_init_outbound_group_session_random_length, ) outbound_group_session_function( lib.olm_init_outbound_group_session, c_void_p, c_size_t, ) lib.olm_outbound_group_session_message_index.argtypes = [c_void_p] lib.olm_outbound_group_session_message_index.restype = c_uint32 outbound_group_session_function( lib.olm_group_encrypt_message_length, c_size_t, ) outbound_group_session_function( lib.olm_group_encrypt, c_void_p, c_size_t, # Plaintext c_void_p, c_size_t, # Message ) outbound_group_session_function( lib.olm_outbound_group_session_id_length, ) outbound_group_session_function( lib.olm_outbound_group_session_id, c_void_p, c_size_t, ) outbound_group_session_function( lib.olm_outbound_group_session_key_length, ) outbound_group_session_function( lib.olm_outbound_group_session_key, c_void_p, c_size_t, ) class OutboundGroupSession(object): def __init__(self): self.buf = create_string_buffer(lib.olm_outbound_group_session_size()) self.ptr = lib.olm_outbound_group_session(self.buf) random_length = lib.olm_init_outbound_group_session_random_length( self.ptr ) random = urandom(random_length) random_buffer = create_string_buffer(random) lib.olm_init_outbound_group_session( self.ptr, random_buffer, random_length ) def pickle(self, key): key_buffer = create_string_buffer(key) pickle_length = lib.olm_pickle_outbound_group_session_length(self.ptr) pickle_buffer = create_string_buffer(pickle_length) lib.olm_pickle_outbound_group_session( self.ptr, key_buffer, len(key), pickle_buffer, pickle_length ) return pickle_buffer.raw def unpickle(self, key, pickle): key_buffer = create_string_buffer(key) pickle_buffer = create_string_buffer(pickle) lib.olm_unpickle_outbound_group_session( self.ptr, key_buffer, len(key), pickle_buffer, len(pickle) ) def encrypt(self, plaintext): message_length = lib.olm_group_encrypt_message_length( self.ptr, len(plaintext) ) message_buffer = create_string_buffer(message_length) plaintext_buffer = create_string_buffer(plaintext) lib.olm_group_encrypt( self.ptr, plaintext_buffer, len(plaintext), message_buffer, message_length, ) return message_buffer.raw def session_id(self): id_length = lib.olm_outbound_group_session_id_length(self.ptr) id_buffer = create_string_buffer(id_length) lib.olm_outbound_group_session_id(self.ptr, id_buffer, id_length) return id_buffer.raw def message_index(self): return lib.olm_outbound_group_session_message_index(self.ptr) def session_key(self): key_length = lib.olm_outbound_group_session_key_length(self.ptr) key_buffer = create_string_buffer(key_length) lib.olm_outbound_group_session_key(self.ptr, key_buffer, key_length) return key_buffer.raw olm-2.2.2+git20170526.0fd768e+dfsg/python/olm/session.py000066400000000000000000000152251311755073500220740ustar00rootroot00000000000000from os import urandom from ._base import * lib.olm_session_size.argtypes = [] lib.olm_session_size.restype = c_size_t lib.olm_session.argtypes = [c_void_p] lib.olm_session.restype = c_void_p lib.olm_session_last_error.argtypes = [c_void_p] lib.olm_session_last_error.restype = c_char_p def session_errcheck(res, func, args): if res == ERR: raise OlmError("%s: %s" % ( func.__name__, lib.olm_session_last_error(args[0]) )) return res def session_function(func, *types): func.argtypes = (c_void_p,) + types func.restypes = c_size_t func.errcheck = session_errcheck session_function(lib.olm_session_last_error) session_function( lib.olm_pickle_session, c_void_p, c_size_t, c_void_p, c_size_t ) session_function( lib.olm_unpickle_session, c_void_p, c_size_t, c_void_p, c_size_t ) session_function(lib.olm_create_outbound_session_random_length) session_function( lib.olm_create_outbound_session, c_void_p, # Account c_void_p, c_size_t, # Identity Key c_void_p, c_size_t, # One Time Key c_void_p, c_size_t, # Random ) session_function( lib.olm_create_inbound_session, c_void_p, # Account c_void_p, c_size_t, # Pre Key Message ) session_function( lib.olm_create_inbound_session_from, c_void_p, # Account c_void_p, c_size_t, # Identity Key c_void_p, c_size_t, # Pre Key Message ) session_function(lib.olm_session_id_length) session_function(lib.olm_session_id, c_void_p, c_size_t) session_function(lib.olm_matches_inbound_session, c_void_p, c_size_t) session_function( lib.olm_matches_inbound_session_from, c_void_p, c_size_t, # Identity Key c_void_p, c_size_t, # Pre Key Message ) session_function(lib.olm_pickle_session_length) session_function(lib.olm_encrypt_message_type) session_function(lib.olm_encrypt_random_length) session_function(lib.olm_encrypt_message_length, c_size_t) session_function( lib.olm_encrypt, c_void_p, c_size_t, # Plaintext c_void_p, c_size_t, # Random c_void_p, c_size_t, # Message ) session_function( lib.olm_decrypt_max_plaintext_length, c_size_t, # Message Type c_void_p, c_size_t, # Message ) session_function( lib.olm_decrypt, c_size_t, # Message Type c_void_p, c_size_t, # Message c_void_p, c_size_t, # Plaintext ) class Session(object): def __init__(self): self.buf = create_string_buffer(lib.olm_session_size()) self.ptr = lib.olm_session(self.buf) def pickle(self, key): key_buffer = create_string_buffer(key) pickle_length = lib.olm_pickle_session_length(self.ptr) pickle_buffer = create_string_buffer(pickle_length) lib.olm_pickle_session( self.ptr, key_buffer, len(key), pickle_buffer, pickle_length ) return pickle_buffer.raw def unpickle(self, key, pickle): key_buffer = create_string_buffer(key) pickle_buffer = create_string_buffer(pickle) lib.olm_unpickle_session( self.ptr, key_buffer, len(key), pickle_buffer, len(pickle) ) def create_outbound(self, account, identity_key, one_time_key): r_length = lib.olm_create_outbound_session_random_length(self.ptr) random = urandom(r_length) random_buffer = create_string_buffer(random) identity_key_buffer = create_string_buffer(identity_key) one_time_key_buffer = create_string_buffer(one_time_key) lib.olm_create_outbound_session( self.ptr, account.ptr, identity_key_buffer, len(identity_key), one_time_key_buffer, len(one_time_key), random_buffer, r_length ) def create_inbound(self, account, one_time_key_message): one_time_key_message_buffer = create_string_buffer( one_time_key_message ) lib.olm_create_inbound_session( self.ptr, account.ptr, one_time_key_message_buffer, len(one_time_key_message) ) def create_inbound_from(self, account, identity_key, one_time_key_message): identity_key_buffer = create_string_buffer(identity_key) one_time_key_message_buffer = create_string_buffer( one_time_key_message ) lib.olm_create_inbound_session_from( self.ptr, account.ptr, identity_key_buffer, len(identity_key), one_time_key_message_buffer, len(one_time_key_message) ) def session_id(self): id_length = lib.olm_session_id_length(self.ptr) id_buffer = create_string_buffer(id_length) lib.olm_session_id(self.ptr, id_buffer, id_length) return id_buffer.raw def matches_inbound(self, one_time_key_message): one_time_key_message_buffer = create_string_buffer( one_time_key_message, ) return bool(lib.olm_matches_inbound_session( self.ptr, one_time_key_message_buffer, len(one_time_key_message) )) def matches_inbound_from(self, identity_key, one_time_key_message): identity_key_buffer = create_string_buffer(identity_key) one_time_key_message_buffer = create_string_buffer( one_time_key_message, ) return bool(lib.olm_matches_inbound_session( self.ptr, identity_key_buffer, len(identity_key), one_time_key_message_buffer, len(one_time_key_message) )) def encrypt(self, plaintext): r_length = lib.olm_encrypt_random_length(self.ptr) random = urandom(r_length) random_buffer = create_string_buffer(random) message_type = lib.olm_encrypt_message_type(self.ptr) message_length = lib.olm_encrypt_message_length( self.ptr, len(plaintext) ) message_buffer = create_string_buffer(message_length) plaintext_buffer = create_string_buffer(plaintext) lib.olm_encrypt( self.ptr, plaintext_buffer, len(plaintext), random_buffer, r_length, message_buffer, message_length, ) return message_type, message_buffer.raw def decrypt(self, message_type, message): message_buffer = create_string_buffer(message) max_plaintext_length = lib.olm_decrypt_max_plaintext_length( self.ptr, message_type, message_buffer, len(message) ) plaintext_buffer = create_string_buffer(max_plaintext_length) message_buffer = create_string_buffer(message) plaintext_length = lib.olm_decrypt( self.ptr, message_type, message_buffer, len(message), plaintext_buffer, max_plaintext_length ) return plaintext_buffer.raw[:plaintext_length] def clear(self): pass olm-2.2.2+git20170526.0fd768e+dfsg/python/olm/utility.py000066400000000000000000000030771311755073500221160ustar00rootroot00000000000000from ._base import lib, c_void_p, c_size_t, c_char_p, \ create_string_buffer, ERR, OlmError lib.olm_utility_size.argtypes = [] lib.olm_utility_size.restype = c_size_t lib.olm_utility.argtypes = [c_void_p] lib.olm_utility.restype = c_void_p lib.olm_utility_last_error.argtypes = [c_void_p] lib.olm_utility_last_error.restype = c_char_p def utility_errcheck(res, func, args): if res == ERR: raise OlmError("%s: %s" % ( func.__name__, lib.olm_utility_last_error(args[0]) )) return res def utility_function(func, *types): func.argtypes = (c_void_p,) + types func.restypes = c_size_t func.errcheck = utility_errcheck utility_function( lib.olm_ed25519_verify, c_void_p, c_size_t, # key, key_length c_void_p, c_size_t, # message, message_length c_void_p, c_size_t, # signature, signature_length ) class Utility(object): def __init__(self): self.buf = create_string_buffer(lib.olm_utility_size()) self.ptr = lib.olm_utility(self.buf) _utility = None def ed25519_verify(key, message, signature): """ Verify an ed25519 signature. Raises an OlmError if verification fails. Args: key(bytes): The ed25519 public key used for signing. message(bytes): The signed message. signature(bytes): The message signature. """ global _utility if not _utility: _utility = Utility() lib.olm_ed25519_verify(_utility.ptr, key, len(key), message, len(message), signature, len(signature)) olm-2.2.2+git20170526.0fd768e+dfsg/python/test_olm.sh000077500000000000000000000026451311755073500214370ustar00rootroot00000000000000#! /bin/bash set -e cd `dirname $0` OLM="python -m olm" ALICE_ACCOUNT=alice.account ALICE_SESSION=alice.session ALICE_GROUP_SESSION=alice.group_session BOB_ACCOUNT=bob.account BOB_SESSION=bob.session BOB_GROUP_SESSION=bob.group_session CHARLIE_GROUP_SESSION=charlie.group_session rm -f $ALICE_ACCOUNT $BOB_ACCOUNT rm -f $ALICE_SESSION $BOB_SESSION rm -f $ALICE_GROUP_SESSION $BOB_GROUP_SESSION $CHARLIE_GROUP_SESSION $OLM create_account $ALICE_ACCOUNT $OLM create_account $BOB_ACCOUNT $OLM generate_keys $BOB_ACCOUNT 1 BOB_IDENTITY_KEY="$($OLM identity_key $BOB_ACCOUNT)" BOB_ONE_TIME_KEY="$($OLM one_time_key $BOB_ACCOUNT)" $OLM outbound $ALICE_ACCOUNT $ALICE_SESSION "$BOB_IDENTITY_KEY" "$BOB_ONE_TIME_KEY" echo "Hello world" | $OLM encrypt $ALICE_SESSION - - | $OLM inbound $BOB_ACCOUNT $BOB_SESSION - - ### group sessions $OLM outbound_group $ALICE_GROUP_SESSION $OLM group_credentials $ALICE_GROUP_SESSION | $OLM inbound_group $BOB_GROUP_SESSION echo "Hello group" | $OLM group_encrypt $ALICE_GROUP_SESSION - group_message $OLM group_decrypt $BOB_GROUP_SESSION group_message $OLM export_inbound_group $BOB_GROUP_SESSION | $OLM import_inbound_group $CHARLIE_GROUP_SESSION $OLM group_decrypt $CHARLIE_GROUP_SESSION group_message ### Sign/verify ALICE_SIGNING_KEY="$($OLM signing_key $ALICE_ACCOUNT)" sig="$(echo "Test message" | $OLM sign $ALICE_ACCOUNT - -)" echo "Test message" | $OLM ed25519_verify $ALICE_SIGNING_KEY $sig - olm-2.2.2+git20170526.0fd768e+dfsg/src/000077500000000000000000000000001311755073500165115ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/src/account.cpp000066400000000000000000000245101311755073500206530ustar00rootroot00000000000000/* Copyright 2015, 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. */ #include "olm/account.hh" #include "olm/base64.hh" #include "olm/pickle.h" #include "olm/pickle.hh" #include "olm/memory.hh" olm::Account::Account( ) : next_one_time_key_id(0), last_error(OlmErrorCode::OLM_SUCCESS) { } olm::OneTimeKey const * olm::Account::lookup_key( _olm_curve25519_public_key const & public_key ) { for (olm::OneTimeKey const & key : one_time_keys) { if (olm::array_equal(key.key.public_key.public_key, public_key.public_key)) { return &key; } } return 0; } std::size_t olm::Account::remove_key( _olm_curve25519_public_key const & public_key ) { OneTimeKey * i; for (i = one_time_keys.begin(); i != one_time_keys.end(); ++i) { if (olm::array_equal(i->key.public_key.public_key, public_key.public_key)) { std::uint32_t id = i->id; one_time_keys.erase(i); return id; } } return std::size_t(-1); } std::size_t olm::Account::new_account_random_length() { return ED25519_RANDOM_LENGTH + CURVE25519_RANDOM_LENGTH; } std::size_t olm::Account::new_account( uint8_t const * random, std::size_t random_length ) { if (random_length < new_account_random_length()) { last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM; return std::size_t(-1); } _olm_crypto_ed25519_generate_key(random, &identity_keys.ed25519_key); random += ED25519_RANDOM_LENGTH; _olm_crypto_curve25519_generate_key(random, &identity_keys.curve25519_key); return 0; } namespace { uint8_t KEY_JSON_ED25519[] = "\"ed25519\":"; uint8_t KEY_JSON_CURVE25519[] = "\"curve25519\":"; template static std::uint8_t * write_string( std::uint8_t * pos, T const & value ) { std::memcpy(pos, value, sizeof(T) - 1); return pos + (sizeof(T) - 1); } } std::size_t olm::Account::get_identity_json_length() { std::size_t length = 0; length += 1; /* { */ length += sizeof(KEY_JSON_CURVE25519) - 1; length += 1; /* " */ length += olm::encode_base64_length( sizeof(identity_keys.curve25519_key.public_key) ); length += 2; /* ", */ length += sizeof(KEY_JSON_ED25519) - 1; length += 1; /* " */ length += olm::encode_base64_length( sizeof(identity_keys.ed25519_key.public_key) ); length += 2; /* "} */ return length; } std::size_t olm::Account::get_identity_json( std::uint8_t * identity_json, std::size_t identity_json_length ) { std::uint8_t * pos = identity_json; size_t expected_length = get_identity_json_length(); if (identity_json_length < expected_length) { last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } *(pos++) = '{'; pos = write_string(pos, KEY_JSON_CURVE25519); *(pos++) = '\"'; pos = olm::encode_base64( identity_keys.curve25519_key.public_key.public_key, sizeof(identity_keys.curve25519_key.public_key.public_key), pos ); *(pos++) = '\"'; *(pos++) = ','; pos = write_string(pos, KEY_JSON_ED25519); *(pos++) = '\"'; pos = olm::encode_base64( identity_keys.ed25519_key.public_key.public_key, sizeof(identity_keys.ed25519_key.public_key.public_key), pos ); *(pos++) = '\"'; *(pos++) = '}'; return pos - identity_json; } std::size_t olm::Account::signature_length( ) { return ED25519_SIGNATURE_LENGTH; } std::size_t olm::Account::sign( std::uint8_t const * message, std::size_t message_length, std::uint8_t * signature, std::size_t signature_length ) { if (signature_length < this->signature_length()) { last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } _olm_crypto_ed25519_sign( &identity_keys.ed25519_key, message, message_length, signature ); return this->signature_length(); } std::size_t olm::Account::get_one_time_keys_json_length( ) { std::size_t length = 0; bool is_empty = true; for (auto const & key : one_time_keys) { if (key.published) { continue; } is_empty = false; length += 2; /* {" */ length += olm::encode_base64_length(olm::pickle_length(key.id)); length += 3; /* ":" */ length += olm::encode_base64_length(sizeof(key.key.public_key)); length += 1; /* " */ } if (is_empty) { length += 1; /* { */ } length += 3; /* }{} */ length += sizeof(KEY_JSON_CURVE25519) - 1; return length; } std::size_t olm::Account::get_one_time_keys_json( std::uint8_t * one_time_json, std::size_t one_time_json_length ) { std::uint8_t * pos = one_time_json; if (one_time_json_length < get_one_time_keys_json_length()) { last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } *(pos++) = '{'; pos = write_string(pos, KEY_JSON_CURVE25519); std::uint8_t sep = '{'; for (auto const & key : one_time_keys) { if (key.published) { continue; } *(pos++) = sep; *(pos++) = '\"'; std::uint8_t key_id[olm::pickle_length(key.id)]; olm::pickle(key_id, key.id); pos = olm::encode_base64(key_id, sizeof(key_id), pos); *(pos++) = '\"'; *(pos++) = ':'; *(pos++) = '\"'; pos = olm::encode_base64( key.key.public_key.public_key, sizeof(key.key.public_key.public_key), pos ); *(pos++) = '\"'; sep = ','; } if (sep != ',') { /* The list was empty */ *(pos++) = sep; } *(pos++) = '}'; *(pos++) = '}'; return pos - one_time_json; } std::size_t olm::Account::mark_keys_as_published( ) { std::size_t count = 0; for (auto & key : one_time_keys) { if (!key.published) { key.published = true; count++; } } return count; } std::size_t olm::Account::max_number_of_one_time_keys( ) { return olm::MAX_ONE_TIME_KEYS; } std::size_t olm::Account::generate_one_time_keys_random_length( std::size_t number_of_keys ) { return CURVE25519_RANDOM_LENGTH * number_of_keys; } std::size_t olm::Account::generate_one_time_keys( std::size_t number_of_keys, std::uint8_t const * random, std::size_t random_length ) { if (random_length < generate_one_time_keys_random_length(number_of_keys)) { last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM; return std::size_t(-1); } for (unsigned i = 0; i < number_of_keys; ++i) { OneTimeKey & key = *one_time_keys.insert(one_time_keys.begin()); key.id = ++next_one_time_key_id; key.published = false; _olm_crypto_curve25519_generate_key(random, &key.key); random += CURVE25519_RANDOM_LENGTH; } return number_of_keys; } namespace olm { static std::size_t pickle_length( olm::IdentityKeys const & value ) { size_t length = 0; length += _olm_pickle_ed25519_key_pair_length(&value.ed25519_key); length += olm::pickle_length(value.curve25519_key); return length; } static std::uint8_t * pickle( std::uint8_t * pos, olm::IdentityKeys const & value ) { pos = _olm_pickle_ed25519_key_pair(pos, &value.ed25519_key); pos = olm::pickle(pos, value.curve25519_key); return pos; } static std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, olm::IdentityKeys & value ) { pos = _olm_unpickle_ed25519_key_pair(pos, end, &value.ed25519_key); pos = olm::unpickle(pos, end, value.curve25519_key); return pos; } static std::size_t pickle_length( olm::OneTimeKey const & value ) { std::size_t length = 0; length += olm::pickle_length(value.id); length += olm::pickle_length(value.published); length += olm::pickle_length(value.key); return length; } static std::uint8_t * pickle( std::uint8_t * pos, olm::OneTimeKey const & value ) { pos = olm::pickle(pos, value.id); pos = olm::pickle(pos, value.published); pos = olm::pickle(pos, value.key); return pos; } static std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, olm::OneTimeKey & value ) { pos = olm::unpickle(pos, end, value.id); pos = olm::unpickle(pos, end, value.published); pos = olm::unpickle(pos, end, value.key); return pos; } } // namespace olm namespace { // pickle version 1 used only 32 bytes for the ed25519 private key. // Any keys thus used should be considered compromised. static const std::uint32_t ACCOUNT_PICKLE_VERSION = 2; } std::size_t olm::pickle_length( olm::Account const & value ) { std::size_t length = 0; length += olm::pickle_length(ACCOUNT_PICKLE_VERSION); length += olm::pickle_length(value.identity_keys); length += olm::pickle_length(value.one_time_keys); length += olm::pickle_length(value.next_one_time_key_id); return length; } std::uint8_t * olm::pickle( std::uint8_t * pos, olm::Account const & value ) { pos = olm::pickle(pos, ACCOUNT_PICKLE_VERSION); pos = olm::pickle(pos, value.identity_keys); pos = olm::pickle(pos, value.one_time_keys); pos = olm::pickle(pos, value.next_one_time_key_id); return pos; } std::uint8_t const * olm::unpickle( std::uint8_t const * pos, std::uint8_t const * end, olm::Account & value ) { uint32_t pickle_version; pos = olm::unpickle(pos, end, pickle_version); switch (pickle_version) { case ACCOUNT_PICKLE_VERSION: break; case 1: value.last_error = OlmErrorCode::OLM_BAD_LEGACY_ACCOUNT_PICKLE; return end; default: value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION; return end; } pos = olm::unpickle(pos, end, value.identity_keys); pos = olm::unpickle(pos, end, value.one_time_keys); pos = olm::unpickle(pos, end, value.next_one_time_key_id); return pos; } olm-2.2.2+git20170526.0fd768e+dfsg/src/base64.cpp000066400000000000000000000121331311755073500203010ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "olm/base64.h" #include "olm/base64.hh" namespace { static const std::uint8_t ENCODE_BASE64[64] = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B, 0x2F, }; static const std::uint8_t E = -1; static const std::uint8_t DECODE_BASE64[128] = { /* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xA 0xB 0xC 0xD 0xE 0xF */ E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, 62, E, E, E, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, E, E, E, E, E, E, E, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, E, E, E, E, E, E, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, E, E, E, E, E, }; } // namespace std::size_t olm::encode_base64_length( std::size_t input_length ) { return 4 * ((input_length + 2) / 3) + (input_length + 2) % 3 - 2; } std::uint8_t * olm::encode_base64( std::uint8_t const * input, std::size_t input_length, std::uint8_t * output ) { std::uint8_t const * end = input + (input_length / 3) * 3; std::uint8_t const * pos = input; while (pos != end) { unsigned value = pos[0]; value <<= 8; value |= pos[1]; value <<= 8; value |= pos[2]; pos += 3; output[3] = ENCODE_BASE64[value & 0x3F]; value >>= 6; output[2] = ENCODE_BASE64[value & 0x3F]; value >>= 6; output[1] = ENCODE_BASE64[value & 0x3F]; value >>= 6; output[0] = ENCODE_BASE64[value]; output += 4; } unsigned remainder = input + input_length - pos; std::uint8_t * result = output; if (remainder) { unsigned value = pos[0]; if (remainder == 2) { value <<= 8; value |= pos[1]; value <<= 2; output[2] = ENCODE_BASE64[value & 0x3F]; value >>= 6; result += 3; } else { value <<= 4; result += 2; } output[1] = ENCODE_BASE64[value & 0x3F]; value >>= 6; output[0] = ENCODE_BASE64[value]; } return result; } std::size_t olm::decode_base64_length( std::size_t input_length ) { if (input_length % 4 == 1) { return std::size_t(-1); } else { return 3 * ((input_length + 2) / 4) + (input_length + 2) % 4 - 2; } } std::uint8_t const * olm::decode_base64( std::uint8_t const * input, std::size_t input_length, std::uint8_t * output ) { std::uint8_t const * end = input + (input_length / 4) * 4; std::uint8_t const * pos = input; while (pos != end) { unsigned value = DECODE_BASE64[pos[0] & 0x7F]; value <<= 6; value |= DECODE_BASE64[pos[1] & 0x7F]; value <<= 6; value |= DECODE_BASE64[pos[2] & 0x7F]; value <<= 6; value |= DECODE_BASE64[pos[3] & 0x7F]; pos += 4; output[2] = value; value >>= 8; output[1] = value; value >>= 8; output[0] = value; output += 3; } unsigned remainder = input + input_length - pos; if (remainder) { unsigned value = DECODE_BASE64[pos[0] & 0x7F]; value <<= 6; value |= DECODE_BASE64[pos[1] & 0x7F]; if (remainder == 3) { value <<= 6; value |= DECODE_BASE64[pos[2] & 0x7F]; value >>= 2; output[1] = value; value >>= 8; } else { value >>= 4; } output[0] = value; } return input + input_length; } // implementations of base64.h size_t _olm_encode_base64_length( size_t input_length ) { return olm::encode_base64_length(input_length); } size_t _olm_encode_base64( uint8_t const * input, size_t input_length, uint8_t * output ) { uint8_t * r = olm::encode_base64(input, input_length, output); return r - output; } size_t _olm_decode_base64_length( size_t input_length ) { return olm::decode_base64_length(input_length); } size_t _olm_decode_base64( uint8_t const * input, size_t input_length, uint8_t * output ) { olm::decode_base64(input, input_length, output); return olm::decode_base64_length(input_length); } olm-2.2.2+git20170526.0fd768e+dfsg/src/cipher.cpp000066400000000000000000000103231311755073500204660ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "olm/cipher.h" #include "olm/crypto.h" #include "olm/memory.hh" #include const std::size_t HMAC_KEY_LENGTH = 32; namespace { struct DerivedKeys { _olm_aes256_key aes_key; std::uint8_t mac_key[HMAC_KEY_LENGTH]; _olm_aes256_iv aes_iv; }; static void derive_keys( std::uint8_t const * kdf_info, std::size_t kdf_info_length, std::uint8_t const * key, std::size_t key_length, DerivedKeys & keys ) { std::uint8_t derived_secrets[ AES256_KEY_LENGTH + HMAC_KEY_LENGTH + AES256_IV_LENGTH ]; _olm_crypto_hkdf_sha256( key, key_length, nullptr, 0, kdf_info, kdf_info_length, derived_secrets, sizeof(derived_secrets) ); std::uint8_t const * pos = derived_secrets; pos = olm::load_array(keys.aes_key.key, pos); pos = olm::load_array(keys.mac_key, pos); pos = olm::load_array(keys.aes_iv.iv, pos); olm::unset(derived_secrets); } static const std::size_t MAC_LENGTH = 8; size_t aes_sha_256_cipher_mac_length(const struct _olm_cipher *cipher) { return MAC_LENGTH; } size_t aes_sha_256_cipher_encrypt_ciphertext_length( const struct _olm_cipher *cipher, size_t plaintext_length ) { return _olm_crypto_aes_encrypt_cbc_length(plaintext_length); } size_t aes_sha_256_cipher_encrypt( const struct _olm_cipher *cipher, uint8_t const * key, size_t key_length, uint8_t const * plaintext, size_t plaintext_length, uint8_t * ciphertext, size_t ciphertext_length, uint8_t * output, size_t output_length ) { auto *c = reinterpret_cast(cipher); if (aes_sha_256_cipher_encrypt_ciphertext_length(cipher, plaintext_length) < ciphertext_length) { return std::size_t(-1); } struct DerivedKeys keys; std::uint8_t mac[SHA256_OUTPUT_LENGTH]; derive_keys(c->kdf_info, c->kdf_info_length, key, key_length, keys); _olm_crypto_aes_encrypt_cbc( &keys.aes_key, &keys.aes_iv, plaintext, plaintext_length, ciphertext ); _olm_crypto_hmac_sha256( keys.mac_key, HMAC_KEY_LENGTH, output, output_length - MAC_LENGTH, mac ); std::memcpy(output + output_length - MAC_LENGTH, mac, MAC_LENGTH); olm::unset(keys); return output_length; } size_t aes_sha_256_cipher_decrypt_max_plaintext_length( const struct _olm_cipher *cipher, size_t ciphertext_length ) { return ciphertext_length; } size_t aes_sha_256_cipher_decrypt( const struct _olm_cipher *cipher, uint8_t const * key, size_t key_length, uint8_t const * input, size_t input_length, uint8_t const * ciphertext, size_t ciphertext_length, uint8_t * plaintext, size_t max_plaintext_length ) { auto *c = reinterpret_cast(cipher); DerivedKeys keys; std::uint8_t mac[SHA256_OUTPUT_LENGTH]; derive_keys(c->kdf_info, c->kdf_info_length, key, key_length, keys); _olm_crypto_hmac_sha256( keys.mac_key, HMAC_KEY_LENGTH, input, input_length - MAC_LENGTH, mac ); std::uint8_t const * input_mac = input + input_length - MAC_LENGTH; if (!olm::is_equal(input_mac, mac, MAC_LENGTH)) { olm::unset(keys); return std::size_t(-1); } std::size_t plaintext_length = _olm_crypto_aes_decrypt_cbc( &keys.aes_key, &keys.aes_iv, ciphertext, ciphertext_length, plaintext ); olm::unset(keys); return plaintext_length; } } // namespace const struct _olm_cipher_ops _olm_cipher_aes_sha_256_ops = { aes_sha_256_cipher_mac_length, aes_sha_256_cipher_encrypt_ciphertext_length, aes_sha_256_cipher_encrypt, aes_sha_256_cipher_decrypt_max_plaintext_length, aes_sha_256_cipher_decrypt, }; olm-2.2.2+git20170526.0fd768e+dfsg/src/crypto.cpp000066400000000000000000000220321311755073500205340ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "olm/crypto.h" #include "olm/memory.hh" #include extern "C" { #include "crypto-algorithms/aes.h" #include "crypto-algorithms/sha256.h" } #include "ed25519/src/ed25519.h" #include "curve25519-donna.h" namespace { static const std::uint8_t CURVE25519_BASEPOINT[32] = {9}; static const std::size_t AES_KEY_SCHEDULE_LENGTH = 60; static const std::size_t AES_KEY_BITS = 8 * AES256_KEY_LENGTH; static const std::size_t AES_BLOCK_LENGTH = 16; static const std::size_t SHA256_BLOCK_LENGTH = 64; static const std::uint8_t HKDF_DEFAULT_SALT[32] = {}; template inline static void xor_block( std::uint8_t * block, std::uint8_t const * input ) { for (std::size_t i = 0; i < block_size; ++i) { block[i] ^= input[i]; } } inline static void hmac_sha256_key( std::uint8_t const * input_key, std::size_t input_key_length, std::uint8_t * hmac_key ) { std::memset(hmac_key, 0, SHA256_BLOCK_LENGTH); if (input_key_length > SHA256_BLOCK_LENGTH) { ::SHA256_CTX context; ::sha256_init(&context); ::sha256_update(&context, input_key, input_key_length); ::sha256_final(&context, hmac_key); } else { std::memcpy(hmac_key, input_key, input_key_length); } } inline static void hmac_sha256_init( ::SHA256_CTX * context, std::uint8_t const * hmac_key ) { std::uint8_t i_pad[SHA256_BLOCK_LENGTH]; std::memcpy(i_pad, hmac_key, SHA256_BLOCK_LENGTH); for (std::size_t i = 0; i < SHA256_BLOCK_LENGTH; ++i) { i_pad[i] ^= 0x36; } ::sha256_init(context); ::sha256_update(context, i_pad, SHA256_BLOCK_LENGTH); olm::unset(i_pad); } inline static void hmac_sha256_final( ::SHA256_CTX * context, std::uint8_t const * hmac_key, std::uint8_t * output ) { std::uint8_t o_pad[SHA256_BLOCK_LENGTH + SHA256_OUTPUT_LENGTH]; std::memcpy(o_pad, hmac_key, SHA256_BLOCK_LENGTH); for (std::size_t i = 0; i < SHA256_BLOCK_LENGTH; ++i) { o_pad[i] ^= 0x5C; } ::sha256_final(context, o_pad + SHA256_BLOCK_LENGTH); ::SHA256_CTX final_context; ::sha256_init(&final_context); ::sha256_update(&final_context, o_pad, sizeof(o_pad)); ::sha256_final(&final_context, output); olm::unset(final_context); olm::unset(o_pad); } } // namespace void _olm_crypto_curve25519_generate_key( uint8_t const * random_32_bytes, struct _olm_curve25519_key_pair *key_pair ) { std::memcpy( key_pair->private_key.private_key, random_32_bytes, CURVE25519_KEY_LENGTH ); ::curve25519_donna( key_pair->public_key.public_key, key_pair->private_key.private_key, CURVE25519_BASEPOINT ); } void _olm_crypto_curve25519_shared_secret( const struct _olm_curve25519_key_pair *our_key, const struct _olm_curve25519_public_key * their_key, std::uint8_t * output ) { ::curve25519_donna(output, our_key->private_key.private_key, their_key->public_key); } void _olm_crypto_ed25519_generate_key( std::uint8_t const * random_32_bytes, struct _olm_ed25519_key_pair *key_pair ) { ::ed25519_create_keypair( key_pair->public_key.public_key, key_pair->private_key.private_key, random_32_bytes ); } void _olm_crypto_ed25519_sign( const struct _olm_ed25519_key_pair *our_key, std::uint8_t const * message, std::size_t message_length, std::uint8_t * output ) { ::ed25519_sign( output, message, message_length, our_key->public_key.public_key, our_key->private_key.private_key ); } int _olm_crypto_ed25519_verify( const struct _olm_ed25519_public_key *their_key, std::uint8_t const * message, std::size_t message_length, std::uint8_t const * signature ) { return 0 != ::ed25519_verify( signature, message, message_length, their_key->public_key ); } std::size_t _olm_crypto_aes_encrypt_cbc_length( std::size_t input_length ) { return input_length + AES_BLOCK_LENGTH - input_length % AES_BLOCK_LENGTH; } void _olm_crypto_aes_encrypt_cbc( _olm_aes256_key const *key, _olm_aes256_iv const *iv, std::uint8_t const * input, std::size_t input_length, std::uint8_t * output ) { std::uint32_t key_schedule[AES_KEY_SCHEDULE_LENGTH]; ::aes_key_setup(key->key, key_schedule, AES_KEY_BITS); std::uint8_t input_block[AES_BLOCK_LENGTH]; std::memcpy(input_block, iv->iv, AES_BLOCK_LENGTH); while (input_length >= AES_BLOCK_LENGTH) { xor_block(input_block, input); ::aes_encrypt(input_block, output, key_schedule, AES_KEY_BITS); std::memcpy(input_block, output, AES_BLOCK_LENGTH); input += AES_BLOCK_LENGTH; output += AES_BLOCK_LENGTH; input_length -= AES_BLOCK_LENGTH; } std::size_t i = 0; for (; i < input_length; ++i) { input_block[i] ^= input[i]; } for (; i < AES_BLOCK_LENGTH; ++i) { input_block[i] ^= AES_BLOCK_LENGTH - input_length; } ::aes_encrypt(input_block, output, key_schedule, AES_KEY_BITS); olm::unset(key_schedule); olm::unset(input_block); } std::size_t _olm_crypto_aes_decrypt_cbc( _olm_aes256_key const *key, _olm_aes256_iv const *iv, std::uint8_t const * input, std::size_t input_length, std::uint8_t * output ) { std::uint32_t key_schedule[AES_KEY_SCHEDULE_LENGTH]; ::aes_key_setup(key->key, key_schedule, AES_KEY_BITS); std::uint8_t block1[AES_BLOCK_LENGTH]; std::uint8_t block2[AES_BLOCK_LENGTH]; std::memcpy(block1, iv->iv, AES_BLOCK_LENGTH); for (std::size_t i = 0; i < input_length; i += AES_BLOCK_LENGTH) { std::memcpy(block2, &input[i], AES_BLOCK_LENGTH); ::aes_decrypt(&input[i], &output[i], key_schedule, AES_KEY_BITS); xor_block(&output[i], block1); std::memcpy(block1, block2, AES_BLOCK_LENGTH); } olm::unset(key_schedule); olm::unset(block1); olm::unset(block2); std::size_t padding = output[input_length - 1]; return (padding > input_length) ? std::size_t(-1) : (input_length - padding); } void _olm_crypto_sha256( std::uint8_t const * input, std::size_t input_length, std::uint8_t * output ) { ::SHA256_CTX context; ::sha256_init(&context); ::sha256_update(&context, input, input_length); ::sha256_final(&context, output); olm::unset(context); } void _olm_crypto_hmac_sha256( std::uint8_t const * key, std::size_t key_length, std::uint8_t const * input, std::size_t input_length, std::uint8_t * output ) { std::uint8_t hmac_key[SHA256_BLOCK_LENGTH]; ::SHA256_CTX context; hmac_sha256_key(key, key_length, hmac_key); hmac_sha256_init(&context, hmac_key); ::sha256_update(&context, input, input_length); hmac_sha256_final(&context, hmac_key, output); olm::unset(hmac_key); olm::unset(context); } void _olm_crypto_hkdf_sha256( std::uint8_t const * input, std::size_t input_length, std::uint8_t const * salt, std::size_t salt_length, std::uint8_t const * info, std::size_t info_length, std::uint8_t * output, std::size_t output_length ) { ::SHA256_CTX context; std::uint8_t hmac_key[SHA256_BLOCK_LENGTH]; std::uint8_t step_result[SHA256_OUTPUT_LENGTH]; std::size_t bytes_remaining = output_length; std::uint8_t iteration = 1; if (!salt) { salt = HKDF_DEFAULT_SALT; salt_length = sizeof(HKDF_DEFAULT_SALT); } /* Extract */ hmac_sha256_key(salt, salt_length, hmac_key); hmac_sha256_init(&context, hmac_key); ::sha256_update(&context, input, input_length); hmac_sha256_final(&context, hmac_key, step_result); hmac_sha256_key(step_result, SHA256_OUTPUT_LENGTH, hmac_key); /* Expand */ hmac_sha256_init(&context, hmac_key); ::sha256_update(&context, info, info_length); ::sha256_update(&context, &iteration, 1); hmac_sha256_final(&context, hmac_key, step_result); while (bytes_remaining > SHA256_OUTPUT_LENGTH) { std::memcpy(output, step_result, SHA256_OUTPUT_LENGTH); output += SHA256_OUTPUT_LENGTH; bytes_remaining -= SHA256_OUTPUT_LENGTH; iteration ++; hmac_sha256_init(&context, hmac_key); ::sha256_update(&context, step_result, SHA256_OUTPUT_LENGTH); ::sha256_update(&context, info, info_length); ::sha256_update(&context, &iteration, 1); hmac_sha256_final(&context, hmac_key, step_result); } std::memcpy(output, step_result, bytes_remaining); olm::unset(context); olm::unset(hmac_key); olm::unset(step_result); } olm-2.2.2+git20170526.0fd768e+dfsg/src/ed25519.c000066400000000000000000000015061311755073500176550ustar00rootroot00000000000000/* Copyright 2015-6 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. */ #define select ed25519_select #include "ed25519/src/fe.c" #include "ed25519/src/sc.c" #include "ed25519/src/ge.c" #include "ed25519/src/keypair.c" #include "ed25519/src/sha512.c" #include "ed25519/src/verify.c" #include "ed25519/src/sign.c" olm-2.2.2+git20170526.0fd768e+dfsg/src/error.c000066400000000000000000000023211311755073500200040ustar00rootroot00000000000000/* 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. */ #include "olm/error.h" static const char * ERRORS[] = { "SUCCESS", "NOT_ENOUGH_RANDOM", "OUTPUT_BUFFER_TOO_SMALL", "BAD_MESSAGE_VERSION", "BAD_MESSAGE_FORMAT", "BAD_MESSAGE_MAC", "BAD_MESSAGE_KEY_ID", "INVALID_BASE64", "BAD_ACCOUNT_KEY", "UNKNOWN_PICKLE_VERSION", "CORRUPTED_PICKLE", "BAD_SESSION_KEY", "UNKNOWN_MESSAGE_INDEX", "BAD_LEGACY_ACCOUNT_PICKLE", "BAD_SIGNATURE", }; const char * _olm_error_to_string(enum OlmErrorCode error) { if (error < (sizeof(ERRORS)/sizeof(ERRORS[0]))) { return ERRORS[error]; } else { return "UNKNOWN_ERROR"; } } olm-2.2.2+git20170526.0fd768e+dfsg/src/inbound_group_session.c000066400000000000000000000365571311755073500233120ustar00rootroot00000000000000/* 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. */ #include "olm/inbound_group_session.h" #include #include "olm/base64.h" #include "olm/cipher.h" #include "olm/crypto.h" #include "olm/error.h" #include "olm/megolm.h" #include "olm/memory.h" #include "olm/message.h" #include "olm/pickle.h" #include "olm/pickle_encoding.h" #define OLM_PROTOCOL_VERSION 3 #define GROUP_SESSION_ID_LENGTH ED25519_PUBLIC_KEY_LENGTH #define PICKLE_VERSION 2 #define SESSION_KEY_VERSION 2 #define SESSION_EXPORT_VERSION 1 struct OlmInboundGroupSession { /** our earliest known ratchet value */ Megolm initial_ratchet; /** The most recent ratchet value */ Megolm latest_ratchet; /** The ed25519 signing key */ struct _olm_ed25519_public_key signing_key; /** * Have we ever seen any evidence that this is a valid session? * (either because the original session share was signed, or because we * have subsequently successfully decrypted a message) * * (We don't do anything with this currently, but we may want to bear it in * mind when we consider handling key-shares for sessions we already know * about.) */ int signing_key_verified; enum OlmErrorCode last_error; }; size_t olm_inbound_group_session_size() { return sizeof(OlmInboundGroupSession); } OlmInboundGroupSession * olm_inbound_group_session( void *memory ) { OlmInboundGroupSession *session = memory; olm_clear_inbound_group_session(session); return session; } const char *olm_inbound_group_session_last_error( const OlmInboundGroupSession *session ) { return _olm_error_to_string(session->last_error); } size_t olm_clear_inbound_group_session( OlmInboundGroupSession *session ) { _olm_unset(session, sizeof(OlmInboundGroupSession)); return sizeof(OlmInboundGroupSession); } #define SESSION_EXPORT_RAW_LENGTH \ (1 + 4 + MEGOLM_RATCHET_LENGTH + ED25519_PUBLIC_KEY_LENGTH) #define SESSION_KEY_RAW_LENGTH \ (1 + 4 + MEGOLM_RATCHET_LENGTH + ED25519_PUBLIC_KEY_LENGTH\ + ED25519_SIGNATURE_LENGTH) static size_t _init_group_session_keys( OlmInboundGroupSession *session, const uint8_t *key_buf, int export_format ) { const uint8_t expected_version = (export_format ? SESSION_EXPORT_VERSION : SESSION_KEY_VERSION); const uint8_t *ptr = key_buf; size_t version = *ptr++; if (version != expected_version) { session->last_error = OLM_BAD_SESSION_KEY; return (size_t)-1; } uint32_t counter = 0; // Decode counter as a big endian 32-bit number. for (unsigned i = 0; i < 4; i++) { counter <<= 8; counter |= *ptr++; } megolm_init(&session->initial_ratchet, ptr, counter); megolm_init(&session->latest_ratchet, ptr, counter); ptr += MEGOLM_RATCHET_LENGTH; memcpy( session->signing_key.public_key, ptr, ED25519_PUBLIC_KEY_LENGTH ); ptr += ED25519_PUBLIC_KEY_LENGTH; if (!export_format) { if (!_olm_crypto_ed25519_verify(&session->signing_key, key_buf, ptr - key_buf, ptr)) { session->last_error = OLM_BAD_SIGNATURE; return (size_t)-1; } /* signed keyshare */ session->signing_key_verified = 1; } return 0; } size_t olm_init_inbound_group_session( OlmInboundGroupSession *session, const uint8_t * session_key, size_t session_key_length ) { uint8_t key_buf[SESSION_KEY_RAW_LENGTH]; size_t raw_length = _olm_decode_base64_length(session_key_length); size_t result; if (raw_length == (size_t)-1) { session->last_error = OLM_INVALID_BASE64; return (size_t)-1; } if (raw_length != SESSION_KEY_RAW_LENGTH) { session->last_error = OLM_BAD_SESSION_KEY; return (size_t)-1; } _olm_decode_base64(session_key, session_key_length, key_buf); result = _init_group_session_keys(session, key_buf, 0); _olm_unset(key_buf, SESSION_KEY_RAW_LENGTH); return result; } size_t olm_import_inbound_group_session( OlmInboundGroupSession *session, const uint8_t * session_key, size_t session_key_length ) { uint8_t key_buf[SESSION_EXPORT_RAW_LENGTH]; size_t raw_length = _olm_decode_base64_length(session_key_length); size_t result; if (raw_length == (size_t)-1) { session->last_error = OLM_INVALID_BASE64; return (size_t)-1; } if (raw_length != SESSION_EXPORT_RAW_LENGTH) { session->last_error = OLM_BAD_SESSION_KEY; return (size_t)-1; } _olm_decode_base64(session_key, session_key_length, key_buf); result = _init_group_session_keys(session, key_buf, 1); _olm_unset(key_buf, SESSION_EXPORT_RAW_LENGTH); return result; } static size_t raw_pickle_length( const OlmInboundGroupSession *session ) { size_t length = 0; length += _olm_pickle_uint32_length(PICKLE_VERSION); length += megolm_pickle_length(&session->initial_ratchet); length += megolm_pickle_length(&session->latest_ratchet); length += _olm_pickle_ed25519_public_key_length(&session->signing_key); length += _olm_pickle_bool_length(session->signing_key_verified); return length; } size_t olm_pickle_inbound_group_session_length( const OlmInboundGroupSession *session ) { return _olm_enc_output_length(raw_pickle_length(session)); } size_t olm_pickle_inbound_group_session( OlmInboundGroupSession *session, void const * key, size_t key_length, void * pickled, size_t pickled_length ) { size_t raw_length = raw_pickle_length(session); uint8_t *pos; if (pickled_length < _olm_enc_output_length(raw_length)) { session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL; return (size_t)-1; } pos = _olm_enc_output_pos(pickled, raw_length); pos = _olm_pickle_uint32(pos, PICKLE_VERSION); pos = megolm_pickle(&session->initial_ratchet, pos); pos = megolm_pickle(&session->latest_ratchet, pos); pos = _olm_pickle_ed25519_public_key(pos, &session->signing_key); pos = _olm_pickle_bool(pos, session->signing_key_verified); return _olm_enc_output(key, key_length, pickled, raw_length); } size_t olm_unpickle_inbound_group_session( OlmInboundGroupSession *session, void const * key, size_t key_length, void * pickled, size_t pickled_length ) { const uint8_t *pos; const uint8_t *end; uint32_t pickle_version; size_t raw_length = _olm_enc_input( key, key_length, pickled, pickled_length, &(session->last_error) ); if (raw_length == (size_t)-1) { return raw_length; } pos = pickled; end = pos + raw_length; pos = _olm_unpickle_uint32(pos, end, &pickle_version); if (pickle_version < 1 || pickle_version > PICKLE_VERSION) { session->last_error = OLM_UNKNOWN_PICKLE_VERSION; return (size_t)-1; } pos = megolm_unpickle(&session->initial_ratchet, pos, end); pos = megolm_unpickle(&session->latest_ratchet, pos, end); pos = _olm_unpickle_ed25519_public_key(pos, end, &session->signing_key); if (pickle_version == 1) { /* pickle v1 had no signing_key_verified field (all keyshares were * verified at import time) */ session->signing_key_verified = 1; } else { pos = _olm_unpickle_bool(pos, end, &(session->signing_key_verified)); } if (end != pos) { /* We had the wrong number of bytes in the input. */ session->last_error = OLM_CORRUPTED_PICKLE; return (size_t)-1; } return pickled_length; } /** * get the max plaintext length in an un-base64-ed message */ static size_t _decrypt_max_plaintext_length( OlmInboundGroupSession *session, uint8_t * message, size_t message_length ) { struct _OlmDecodeGroupMessageResults decoded_results; _olm_decode_group_message( message, message_length, megolm_cipher->ops->mac_length(megolm_cipher), ED25519_SIGNATURE_LENGTH, &decoded_results); if (decoded_results.version != OLM_PROTOCOL_VERSION) { session->last_error = OLM_BAD_MESSAGE_VERSION; return (size_t)-1; } if (!decoded_results.ciphertext) { session->last_error = OLM_BAD_MESSAGE_FORMAT; return (size_t)-1; } return megolm_cipher->ops->decrypt_max_plaintext_length( megolm_cipher, decoded_results.ciphertext_length); } size_t olm_group_decrypt_max_plaintext_length( OlmInboundGroupSession *session, uint8_t * message, size_t message_length ) { size_t raw_length; raw_length = _olm_decode_base64(message, message_length, message); if (raw_length == (size_t)-1) { session->last_error = OLM_INVALID_BASE64; return (size_t)-1; } return _decrypt_max_plaintext_length( session, message, raw_length ); } /** * get a copy of the megolm ratchet, advanced * to the relevant index. Returns 0 on success, -1 on error */ static size_t _get_megolm( OlmInboundGroupSession *session, uint32_t message_index, Megolm *result ) { /* pick a megolm instance to use. If we're at or beyond the latest ratchet * value, use that */ if ((message_index - session->latest_ratchet.counter) < (1U << 31)) { megolm_advance_to(&session->latest_ratchet, message_index); *result = session->latest_ratchet; return 0; } else if ((message_index - session->initial_ratchet.counter) >= (1U << 31)) { /* the counter is before our intial ratchet - we can't decode this. */ session->last_error = OLM_UNKNOWN_MESSAGE_INDEX; return (size_t)-1; } else { /* otherwise, start from the initial megolm. Take a copy so that we * don't overwrite the initial megolm */ *result = session->initial_ratchet; megolm_advance_to(result, message_index); return 0; } } /** * decrypt an un-base64-ed message */ static size_t _decrypt( OlmInboundGroupSession *session, uint8_t * message, size_t message_length, uint8_t * plaintext, size_t max_plaintext_length, uint32_t * message_index ) { struct _OlmDecodeGroupMessageResults decoded_results; size_t max_length, r; Megolm megolm; _olm_decode_group_message( message, message_length, megolm_cipher->ops->mac_length(megolm_cipher), ED25519_SIGNATURE_LENGTH, &decoded_results); if (decoded_results.version != OLM_PROTOCOL_VERSION) { session->last_error = OLM_BAD_MESSAGE_VERSION; return (size_t)-1; } if (!decoded_results.has_message_index || !decoded_results.ciphertext) { session->last_error = OLM_BAD_MESSAGE_FORMAT; return (size_t)-1; } if (message_index != NULL) { *message_index = decoded_results.message_index; } /* verify the signature. We could do this before decoding the message, but * we allow for the possibility of future protocol versions which use a * different signing mechanism; we would rather throw "BAD_MESSAGE_VERSION" * than "BAD_SIGNATURE" in this case. */ message_length -= ED25519_SIGNATURE_LENGTH; r = _olm_crypto_ed25519_verify( &session->signing_key, message, message_length, message + message_length ); if (!r) { session->last_error = OLM_BAD_SIGNATURE; return (size_t)-1; } max_length = megolm_cipher->ops->decrypt_max_plaintext_length( megolm_cipher, decoded_results.ciphertext_length ); if (max_plaintext_length < max_length) { session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL; return (size_t)-1; } r = _get_megolm(session, decoded_results.message_index, &megolm); if (r == (size_t)-1) { return r; } /* now try checking the mac, and decrypting */ r = megolm_cipher->ops->decrypt( megolm_cipher, megolm_get_data(&megolm), MEGOLM_RATCHET_LENGTH, message, message_length, decoded_results.ciphertext, decoded_results.ciphertext_length, plaintext, max_plaintext_length ); _olm_unset(&megolm, sizeof(megolm)); if (r == (size_t)-1) { session->last_error = OLM_BAD_MESSAGE_MAC; return r; } /* once we have successfully decrypted a message, set a flag to say the * session appears valid. */ session->signing_key_verified = 1; return r; } size_t olm_group_decrypt( OlmInboundGroupSession *session, uint8_t * message, size_t message_length, uint8_t * plaintext, size_t max_plaintext_length, uint32_t * message_index ) { size_t raw_message_length; raw_message_length = _olm_decode_base64(message, message_length, message); if (raw_message_length == (size_t)-1) { session->last_error = OLM_INVALID_BASE64; return (size_t)-1; } return _decrypt( session, message, raw_message_length, plaintext, max_plaintext_length, message_index ); } size_t olm_inbound_group_session_id_length( const OlmInboundGroupSession *session ) { return _olm_encode_base64_length(GROUP_SESSION_ID_LENGTH); } size_t olm_inbound_group_session_id( OlmInboundGroupSession *session, uint8_t * id, size_t id_length ) { if (id_length < olm_inbound_group_session_id_length(session)) { session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL; return (size_t)-1; } return _olm_encode_base64( session->signing_key.public_key, GROUP_SESSION_ID_LENGTH, id ); } uint32_t olm_inbound_group_session_first_known_index( const OlmInboundGroupSession *session ) { return session->initial_ratchet.counter; } int olm_inbound_group_session_is_verified( const OlmInboundGroupSession *session ) { return session->signing_key_verified; } size_t olm_export_inbound_group_session_length( const OlmInboundGroupSession *session ) { return _olm_encode_base64_length(SESSION_EXPORT_RAW_LENGTH); } size_t olm_export_inbound_group_session( OlmInboundGroupSession *session, uint8_t * key, size_t key_length, uint32_t message_index ) { uint8_t *raw; uint8_t *ptr; Megolm megolm; size_t r; size_t encoded_length = olm_export_inbound_group_session_length(session); if (key_length < encoded_length) { session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL; return (size_t)-1; } r = _get_megolm(session, message_index, &megolm); if (r == (size_t)-1) { return r; } /* put the raw data at the end of the output buffer. */ raw = ptr = key + encoded_length - SESSION_EXPORT_RAW_LENGTH; *ptr++ = SESSION_EXPORT_VERSION; // Encode message index as a big endian 32-bit number. for (unsigned i = 0; i < 4; i++) { *ptr++ = 0xFF & (message_index >> 24); message_index <<= 8; } memcpy(ptr, megolm_get_data(&megolm), MEGOLM_RATCHET_LENGTH); ptr += MEGOLM_RATCHET_LENGTH; memcpy( ptr, session->signing_key.public_key, ED25519_PUBLIC_KEY_LENGTH ); ptr += ED25519_PUBLIC_KEY_LENGTH; return _olm_encode_base64(raw, SESSION_EXPORT_RAW_LENGTH, key); } olm-2.2.2+git20170526.0fd768e+dfsg/src/megolm.c000066400000000000000000000110141311755073500201320ustar00rootroot00000000000000/* 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. */ #include "olm/megolm.h" #include #include "olm/cipher.h" #include "olm/crypto.h" #include "olm/pickle.h" static const struct _olm_cipher_aes_sha_256 MEGOLM_CIPHER = OLM_CIPHER_INIT_AES_SHA_256("MEGOLM_KEYS"); const struct _olm_cipher *megolm_cipher = OLM_CIPHER_BASE(&MEGOLM_CIPHER); /* the seeds used in the HMAC-SHA-256 functions for each part of the ratchet. */ #define HASH_KEY_SEED_LENGTH 1 static uint8_t HASH_KEY_SEEDS[MEGOLM_RATCHET_PARTS][HASH_KEY_SEED_LENGTH] = { {0x00}, {0x01}, {0x02}, {0x03} }; static void rehash_part( uint8_t data[MEGOLM_RATCHET_PARTS][MEGOLM_RATCHET_PART_LENGTH], int rehash_from_part, int rehash_to_part ) { _olm_crypto_hmac_sha256( data[rehash_from_part], MEGOLM_RATCHET_PART_LENGTH, HASH_KEY_SEEDS[rehash_to_part], HASH_KEY_SEED_LENGTH, data[rehash_to_part] ); } void megolm_init(Megolm *megolm, uint8_t const *random_data, uint32_t counter) { megolm->counter = counter; memcpy(megolm->data, random_data, MEGOLM_RATCHET_LENGTH); } size_t megolm_pickle_length(const Megolm *megolm) { size_t length = 0; length += _olm_pickle_bytes_length(megolm_get_data(megolm), MEGOLM_RATCHET_LENGTH); length += _olm_pickle_uint32_length(megolm->counter); return length; } uint8_t * megolm_pickle(const Megolm *megolm, uint8_t *pos) { pos = _olm_pickle_bytes(pos, megolm_get_data(megolm), MEGOLM_RATCHET_LENGTH); pos = _olm_pickle_uint32(pos, megolm->counter); return pos; } const uint8_t * megolm_unpickle(Megolm *megolm, const uint8_t *pos, const uint8_t *end) { pos = _olm_unpickle_bytes(pos, end, (uint8_t *)(megolm->data), MEGOLM_RATCHET_LENGTH); pos = _olm_unpickle_uint32(pos, end, &megolm->counter); return pos; } /* simplistic implementation for a single step */ void megolm_advance(Megolm *megolm) { uint32_t mask = 0x00FFFFFF; int h = 0; int i; megolm->counter++; /* figure out how much we need to rekey */ while (h < (int)MEGOLM_RATCHET_PARTS) { if (!(megolm->counter & mask)) break; h++; mask >>= 8; } /* now update R(h)...R(3) based on R(h) */ for (i = MEGOLM_RATCHET_PARTS-1; i >= h; i--) { rehash_part(megolm->data, h, i); } } void megolm_advance_to(Megolm *megolm, uint32_t advance_to) { int j; /* starting with R0, see if we need to update each part of the hash */ for (j = 0; j < (int)MEGOLM_RATCHET_PARTS; j++) { int shift = (MEGOLM_RATCHET_PARTS-j-1) * 8; uint32_t mask = (~(uint32_t)0) << shift; int k; /* how many times do we need to rehash this part? * * '& 0xff' ensures we handle integer wraparound correctly */ unsigned int steps = ((advance_to >> shift) - (megolm->counter >> shift)) & 0xff; if (steps == 0) { /* deal with the edge case where megolm->counter is slightly larger * than advance_to. This should only happen for R(0), and implies * that advance_to has wrapped around and we need to advance R(0) * 256 times. */ if (advance_to < megolm->counter) { steps = 0x100; } else { continue; } } /* for all but the last step, we can just bump R(j) without regard * to R(j+1)...R(3). */ while (steps > 1) { rehash_part(megolm->data, j, j); steps --; } /* on the last step we also need to bump R(j+1)...R(3). * * (Theoretically, we could skip bumping R(j+2) if we're going to bump * R(j+1) again, but the code to figure that out is a bit baroque and * doesn't save us much). */ for (k = 3; k >= j; k--) { rehash_part(megolm->data, j, k); } megolm->counter = advance_to & mask; } } olm-2.2.2+git20170526.0fd768e+dfsg/src/memory.cpp000066400000000000000000000023641311755073500205320ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "olm/memory.hh" #include "olm/memory.h" void _olm_unset( void volatile * buffer, size_t buffer_length ) { olm::unset(buffer, buffer_length); } void olm::unset( void volatile * buffer, std::size_t buffer_length ) { char volatile * pos = reinterpret_cast(buffer); char volatile * end = pos + buffer_length; while (pos != end) { *(pos++) = 0; } } bool olm::is_equal( std::uint8_t const * buffer_a, std::uint8_t const * buffer_b, std::size_t length ) { std::uint8_t volatile result = 0; while (length--) { result |= (*(buffer_a++)) ^ (*(buffer_b++)); } return result == 0; } olm-2.2.2+git20170526.0fd768e+dfsg/src/message.cpp000066400000000000000000000247341311755073500206530ustar00rootroot00000000000000/* Copyright 2015-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. */ #include "olm/message.hh" #include "olm/memory.hh" namespace { template static std::size_t varint_length( T value ) { std::size_t result = 1; while (value >= 128U) { ++result; value >>= 7; } return result; } template static std::uint8_t * varint_encode( std::uint8_t * output, T value ) { while (value >= 128U) { *(output++) = (0x7F & value) | 0x80; value >>= 7; } (*output++) = value; return output; } template static T varint_decode( std::uint8_t const * varint_start, std::uint8_t const * varint_end ) { T value = 0; if (varint_end == varint_start) { return 0; } do { value <<= 7; value |= 0x7F & *(--varint_end); } while (varint_end != varint_start); return value; } static std::uint8_t const * varint_skip( std::uint8_t const * input, std::uint8_t const * input_end ) { while (input != input_end) { std::uint8_t tmp = *(input++); if ((tmp & 0x80) == 0) { return input; } } return input; } static std::size_t varstring_length( std::size_t string_length ) { return varint_length(string_length) + string_length; } static std::size_t const VERSION_LENGTH = 1; static std::uint8_t const RATCHET_KEY_TAG = 012; static std::uint8_t const COUNTER_TAG = 020; static std::uint8_t const CIPHERTEXT_TAG = 042; static std::uint8_t * encode( std::uint8_t * pos, std::uint8_t tag, std::uint32_t value ) { *(pos++) = tag; return varint_encode(pos, value); } static std::uint8_t * encode( std::uint8_t * pos, std::uint8_t tag, std::uint8_t * & value, std::size_t value_length ) { *(pos++) = tag; pos = varint_encode(pos, value_length); value = pos; return pos + value_length; } static std::uint8_t const * decode( std::uint8_t const * pos, std::uint8_t const * end, std::uint8_t tag, std::uint32_t & value, bool & has_value ) { if (pos != end && *pos == tag) { ++pos; std::uint8_t const * value_start = pos; pos = varint_skip(pos, end); value = varint_decode(value_start, pos); has_value = true; } return pos; } static std::uint8_t const * decode( std::uint8_t const * pos, std::uint8_t const * end, std::uint8_t tag, std::uint8_t const * & value, std::size_t & value_length ) { if (pos != end && *pos == tag) { ++pos; std::uint8_t const * len_start = pos; pos = varint_skip(pos, end); std::size_t len = varint_decode(len_start, pos); if (len > std::size_t(end - pos)) return end; value = pos; value_length = len; pos += len; } return pos; } static std::uint8_t const * skip_unknown( std::uint8_t const * pos, std::uint8_t const * end ) { if (pos != end) { uint8_t tag = *pos; if ((tag & 0x7) == 0) { pos = varint_skip(pos, end); pos = varint_skip(pos, end); } else if ((tag & 0x7) == 2) { pos = varint_skip(pos, end); std::uint8_t const * len_start = pos; pos = varint_skip(pos, end); std::size_t len = varint_decode(len_start, pos); if (len > std::size_t(end - pos)) return end; pos += len; } else { return end; } } return pos; } } // namespace std::size_t olm::encode_message_length( std::uint32_t counter, std::size_t ratchet_key_length, std::size_t ciphertext_length, std::size_t mac_length ) { std::size_t length = VERSION_LENGTH; length += 1 + varstring_length(ratchet_key_length); length += 1 + varint_length(counter); length += 1 + varstring_length(ciphertext_length); length += mac_length; return length; } void olm::encode_message( olm::MessageWriter & writer, std::uint8_t version, std::uint32_t counter, std::size_t ratchet_key_length, std::size_t ciphertext_length, std::uint8_t * output ) { std::uint8_t * pos = output; *(pos++) = version; pos = encode(pos, RATCHET_KEY_TAG, writer.ratchet_key, ratchet_key_length); pos = encode(pos, COUNTER_TAG, counter); pos = encode(pos, CIPHERTEXT_TAG, writer.ciphertext, ciphertext_length); } void olm::decode_message( olm::MessageReader & reader, std::uint8_t const * input, std::size_t input_length, std::size_t mac_length ) { std::uint8_t const * pos = input; std::uint8_t const * end = input + input_length - mac_length; std::uint8_t const * unknown = nullptr; reader.input = input; reader.input_length = input_length; reader.has_counter = false; reader.ratchet_key = nullptr; reader.ratchet_key_length = 0; reader.ciphertext = nullptr; reader.ciphertext_length = 0; if (input_length < mac_length) return; if (pos == end) return; reader.version = *(pos++); while (pos != end) { unknown = pos; pos = decode( pos, end, RATCHET_KEY_TAG, reader.ratchet_key, reader.ratchet_key_length ); pos = decode( pos, end, COUNTER_TAG, reader.counter, reader.has_counter ); pos = decode( pos, end, CIPHERTEXT_TAG, reader.ciphertext, reader.ciphertext_length ); if (unknown == pos) { pos = skip_unknown(pos, end); } } } namespace { static std::uint8_t const ONE_TIME_KEY_ID_TAG = 012; static std::uint8_t const BASE_KEY_TAG = 022; static std::uint8_t const IDENTITY_KEY_TAG = 032; static std::uint8_t const MESSAGE_TAG = 042; } // namespace std::size_t olm::encode_one_time_key_message_length( std::size_t one_time_key_length, std::size_t identity_key_length, std::size_t base_key_length, std::size_t message_length ) { std::size_t length = VERSION_LENGTH; length += 1 + varstring_length(one_time_key_length); length += 1 + varstring_length(identity_key_length); length += 1 + varstring_length(base_key_length); length += 1 + varstring_length(message_length); return length; } void olm::encode_one_time_key_message( olm::PreKeyMessageWriter & writer, std::uint8_t version, std::size_t identity_key_length, std::size_t base_key_length, std::size_t one_time_key_length, std::size_t message_length, std::uint8_t * output ) { std::uint8_t * pos = output; *(pos++) = version; pos = encode(pos, ONE_TIME_KEY_ID_TAG, writer.one_time_key, one_time_key_length); pos = encode(pos, BASE_KEY_TAG, writer.base_key, base_key_length); pos = encode(pos, IDENTITY_KEY_TAG, writer.identity_key, identity_key_length); pos = encode(pos, MESSAGE_TAG, writer.message, message_length); } void olm::decode_one_time_key_message( PreKeyMessageReader & reader, std::uint8_t const * input, std::size_t input_length ) { std::uint8_t const * pos = input; std::uint8_t const * end = input + input_length; std::uint8_t const * unknown = nullptr; reader.one_time_key = nullptr; reader.one_time_key_length = 0; reader.identity_key = nullptr; reader.identity_key_length = 0; reader.base_key = nullptr; reader.base_key_length = 0; reader.message = nullptr; reader.message_length = 0; if (pos == end) return; reader.version = *(pos++); while (pos != end) { unknown = pos; pos = decode( pos, end, ONE_TIME_KEY_ID_TAG, reader.one_time_key, reader.one_time_key_length ); pos = decode( pos, end, BASE_KEY_TAG, reader.base_key, reader.base_key_length ); pos = decode( pos, end, IDENTITY_KEY_TAG, reader.identity_key, reader.identity_key_length ); pos = decode( pos, end, MESSAGE_TAG, reader.message, reader.message_length ); if (unknown == pos) { pos = skip_unknown(pos, end); } } } static const std::uint8_t GROUP_MESSAGE_INDEX_TAG = 010; static const std::uint8_t GROUP_CIPHERTEXT_TAG = 022; size_t _olm_encode_group_message_length( uint32_t message_index, size_t ciphertext_length, size_t mac_length, size_t signature_length ) { size_t length = VERSION_LENGTH; length += 1 + varint_length(message_index); length += 1 + varstring_length(ciphertext_length); length += mac_length; length += signature_length; return length; } size_t _olm_encode_group_message( uint8_t version, uint32_t message_index, size_t ciphertext_length, uint8_t *output, uint8_t **ciphertext_ptr ) { std::uint8_t * pos = output; *(pos++) = version; pos = encode(pos, GROUP_MESSAGE_INDEX_TAG, message_index); pos = encode(pos, GROUP_CIPHERTEXT_TAG, *ciphertext_ptr, ciphertext_length); return pos-output; } void _olm_decode_group_message( const uint8_t *input, size_t input_length, size_t mac_length, size_t signature_length, struct _OlmDecodeGroupMessageResults *results ) { std::uint8_t const * pos = input; std::size_t trailer_length = mac_length + signature_length; std::uint8_t const * end = input + input_length - trailer_length; std::uint8_t const * unknown = nullptr; bool has_message_index = false; results->message_index = 0; results->ciphertext = nullptr; results->ciphertext_length = 0; if (input_length < trailer_length) return; if (pos == end) return; results->version = *(pos++); while (pos != end) { unknown = pos; pos = decode( pos, end, GROUP_MESSAGE_INDEX_TAG, results->message_index, has_message_index ); pos = decode( pos, end, GROUP_CIPHERTEXT_TAG, results->ciphertext, results->ciphertext_length ); if (unknown == pos) { pos = skip_unknown(pos, end); } } results->has_message_index = (int)has_message_index; } olm-2.2.2+git20170526.0fd768e+dfsg/src/olm.cpp000066400000000000000000000477201311755073500200160ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "olm/olm.h" #include "olm/session.hh" #include "olm/account.hh" #include "olm/cipher.h" #include "olm/pickle_encoding.h" #include "olm/utility.hh" #include "olm/base64.hh" #include "olm/memory.hh" #include #include namespace { static OlmAccount * to_c(olm::Account * account) { return reinterpret_cast(account); } static OlmSession * to_c(olm::Session * session) { return reinterpret_cast(session); } static OlmUtility * to_c(olm::Utility * utility) { return reinterpret_cast(utility); } static olm::Account * from_c(OlmAccount * account) { return reinterpret_cast(account); } static olm::Session * from_c(OlmSession * session) { return reinterpret_cast(session); } static olm::Utility * from_c(OlmUtility * utility) { return reinterpret_cast(utility); } static std::uint8_t * from_c(void * bytes) { return reinterpret_cast(bytes); } static std::uint8_t const * from_c(void const * bytes) { return reinterpret_cast(bytes); } std::size_t b64_output_length( size_t raw_length ) { return olm::encode_base64_length(raw_length); } std::uint8_t * b64_output_pos( std::uint8_t * output, size_t raw_length ) { return output + olm::encode_base64_length(raw_length) - raw_length; } std::size_t b64_output( std::uint8_t * output, size_t raw_length ) { std::size_t base64_length = olm::encode_base64_length(raw_length); std::uint8_t * raw_output = output + base64_length - raw_length; olm::encode_base64(raw_output, raw_length, output); return base64_length; } std::size_t b64_input( std::uint8_t * input, size_t b64_length, OlmErrorCode & last_error ) { std::size_t raw_length = olm::decode_base64_length(b64_length); if (raw_length == std::size_t(-1)) { last_error = OlmErrorCode::OLM_INVALID_BASE64; return std::size_t(-1); } olm::decode_base64(input, b64_length, input); return raw_length; } } // namespace extern "C" { void olm_get_library_version(uint8_t *major, uint8_t *minor, uint8_t *patch) { if (major != NULL) *major = OLMLIB_VERSION_MAJOR; if (minor != NULL) *minor = OLMLIB_VERSION_MINOR; if (patch != NULL) *patch = OLMLIB_VERSION_PATCH; } size_t olm_error() { return std::size_t(-1); } const char * olm_account_last_error( OlmAccount * account ) { auto error = from_c(account)->last_error; return _olm_error_to_string(error); } const char * olm_session_last_error( OlmSession * session ) { auto error = from_c(session)->last_error; return _olm_error_to_string(error); } const char * olm_utility_last_error( OlmUtility * utility ) { auto error = from_c(utility)->last_error; return _olm_error_to_string(error); } size_t olm_account_size() { return sizeof(olm::Account); } size_t olm_session_size() { return sizeof(olm::Session); } size_t olm_utility_size() { return sizeof(olm::Utility); } OlmAccount * olm_account( void * memory ) { olm::unset(memory, sizeof(olm::Account)); return to_c(new(memory) olm::Account()); } OlmSession * olm_session( void * memory ) { olm::unset(memory, sizeof(olm::Session)); return to_c(new(memory) olm::Session()); } OlmUtility * olm_utility( void * memory ) { olm::unset(memory, sizeof(olm::Utility)); return to_c(new(memory) olm::Utility()); } size_t olm_clear_account( OlmAccount * account ) { /* Clear the memory backing the account */ olm::unset(account, sizeof(olm::Account)); /* Initialise a fresh account object in case someone tries to use it */ new(account) olm::Account(); return sizeof(olm::Account); } size_t olm_clear_session( OlmSession * session ) { /* Clear the memory backing the session */ olm::unset(session, sizeof(olm::Session)); /* Initialise a fresh session object in case someone tries to use it */ new(session) olm::Session(); return sizeof(olm::Session); } size_t olm_clear_utility( OlmUtility * utility ) { /* Clear the memory backing the session */ olm::unset(utility, sizeof(olm::Utility)); /* Initialise a fresh session object in case someone tries to use it */ new(utility) olm::Utility(); return sizeof(olm::Utility); } size_t olm_pickle_account_length( OlmAccount * account ) { return _olm_enc_output_length(pickle_length(*from_c(account))); } size_t olm_pickle_session_length( OlmSession * session ) { return _olm_enc_output_length(pickle_length(*from_c(session))); } size_t olm_pickle_account( OlmAccount * account, void const * key, size_t key_length, void * pickled, size_t pickled_length ) { olm::Account & object = *from_c(account); std::size_t raw_length = pickle_length(object); if (pickled_length < _olm_enc_output_length(raw_length)) { object.last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return size_t(-1); } pickle(_olm_enc_output_pos(from_c(pickled), raw_length), object); return _olm_enc_output(from_c(key), key_length, from_c(pickled), raw_length); } size_t olm_pickle_session( OlmSession * session, void const * key, size_t key_length, void * pickled, size_t pickled_length ) { olm::Session & object = *from_c(session); std::size_t raw_length = pickle_length(object); if (pickled_length < _olm_enc_output_length(raw_length)) { object.last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return size_t(-1); } pickle(_olm_enc_output_pos(from_c(pickled), raw_length), object); return _olm_enc_output(from_c(key), key_length, from_c(pickled), raw_length); } size_t olm_unpickle_account( OlmAccount * account, void const * key, size_t key_length, void * pickled, size_t pickled_length ) { olm::Account & object = *from_c(account); std::uint8_t * const pos = from_c(pickled); std::size_t raw_length = _olm_enc_input( from_c(key), key_length, pos, pickled_length, &object.last_error ); if (raw_length == std::size_t(-1)) { return std::size_t(-1); } std::uint8_t * const end = pos + raw_length; /* On success unpickle will return (pos + raw_length). If unpickling * terminates too soon then it will return a pointer before * (pos + raw_length). On error unpickle will return (pos + raw_length + 1). */ if (end != unpickle(pos, end + 1, object)) { if (object.last_error == OlmErrorCode::OLM_SUCCESS) { object.last_error = OlmErrorCode::OLM_CORRUPTED_PICKLE; } return std::size_t(-1); } return pickled_length; } size_t olm_unpickle_session( OlmSession * session, void const * key, size_t key_length, void * pickled, size_t pickled_length ) { olm::Session & object = *from_c(session); std::uint8_t * const pos = from_c(pickled); std::size_t raw_length = _olm_enc_input( from_c(key), key_length, pos, pickled_length, &object.last_error ); if (raw_length == std::size_t(-1)) { return std::size_t(-1); } std::uint8_t * const end = pos + raw_length; /* On success unpickle will return (pos + raw_length). If unpickling * terminates too soon then it will return a pointer before * (pos + raw_length). On error unpickle will return (pos + raw_length + 1). */ if (end != unpickle(pos, end + 1, object)) { if (object.last_error == OlmErrorCode::OLM_SUCCESS) { object.last_error = OlmErrorCode::OLM_CORRUPTED_PICKLE; } return std::size_t(-1); } return pickled_length; } size_t olm_create_account_random_length( OlmAccount * account ) { return from_c(account)->new_account_random_length(); } size_t olm_create_account( OlmAccount * account, void * random, size_t random_length ) { size_t result = from_c(account)->new_account(from_c(random), random_length); olm::unset(random, random_length); return result; } size_t olm_account_identity_keys_length( OlmAccount * account ) { return from_c(account)->get_identity_json_length(); } size_t olm_account_identity_keys( OlmAccount * account, void * identity_keys, size_t identity_key_length ) { return from_c(account)->get_identity_json( from_c(identity_keys), identity_key_length ); } size_t olm_account_signature_length( OlmAccount * account ) { return b64_output_length(from_c(account)->signature_length()); } size_t olm_account_sign( OlmAccount * account, void const * message, size_t message_length, void * signature, size_t signature_length ) { std::size_t raw_length = from_c(account)->signature_length(); if (signature_length < b64_output_length(raw_length)) { from_c(account)->last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } from_c(account)->sign( from_c(message), message_length, b64_output_pos(from_c(signature), raw_length), raw_length ); return b64_output(from_c(signature), raw_length); } size_t olm_account_one_time_keys_length( OlmAccount * account ) { return from_c(account)->get_one_time_keys_json_length(); } size_t olm_account_one_time_keys( OlmAccount * account, void * one_time_keys_json, size_t one_time_key_json_length ) { return from_c(account)->get_one_time_keys_json( from_c(one_time_keys_json), one_time_key_json_length ); } size_t olm_account_mark_keys_as_published( OlmAccount * account ) { return from_c(account)->mark_keys_as_published(); } size_t olm_account_max_number_of_one_time_keys( OlmAccount * account ) { return from_c(account)->max_number_of_one_time_keys(); } size_t olm_account_generate_one_time_keys_random_length( OlmAccount * account, size_t number_of_keys ) { return from_c(account)->generate_one_time_keys_random_length(number_of_keys); } size_t olm_account_generate_one_time_keys( OlmAccount * account, size_t number_of_keys, void * random, size_t random_length ) { size_t result = from_c(account)->generate_one_time_keys( number_of_keys, from_c(random), random_length ); olm::unset(random, random_length); return result; } size_t olm_create_outbound_session_random_length( OlmSession * session ) { return from_c(session)->new_outbound_session_random_length(); } size_t olm_create_outbound_session( OlmSession * session, OlmAccount * account, void const * their_identity_key, size_t their_identity_key_length, void const * their_one_time_key, size_t their_one_time_key_length, void * random, size_t random_length ) { std::uint8_t const * id_key = from_c(their_identity_key); std::uint8_t const * ot_key = from_c(their_one_time_key); std::size_t id_key_length = their_identity_key_length; std::size_t ot_key_length = their_one_time_key_length; if (olm::decode_base64_length(id_key_length) != CURVE25519_KEY_LENGTH || olm::decode_base64_length(ot_key_length) != CURVE25519_KEY_LENGTH ) { from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64; return std::size_t(-1); } _olm_curve25519_public_key identity_key; _olm_curve25519_public_key one_time_key; olm::decode_base64(id_key, id_key_length, identity_key.public_key); olm::decode_base64(ot_key, ot_key_length, one_time_key.public_key); size_t result = from_c(session)->new_outbound_session( *from_c(account), identity_key, one_time_key, from_c(random), random_length ); olm::unset(random, random_length); return result; } size_t olm_create_inbound_session( OlmSession * session, OlmAccount * account, void * one_time_key_message, size_t message_length ) { std::size_t raw_length = b64_input( from_c(one_time_key_message), message_length, from_c(session)->last_error ); if (raw_length == std::size_t(-1)) { return std::size_t(-1); } return from_c(session)->new_inbound_session( *from_c(account), nullptr, from_c(one_time_key_message), raw_length ); } size_t olm_create_inbound_session_from( OlmSession * session, OlmAccount * account, void const * their_identity_key, size_t their_identity_key_length, void * one_time_key_message, size_t message_length ) { std::uint8_t const * id_key = from_c(their_identity_key); std::size_t id_key_length = their_identity_key_length; if (olm::decode_base64_length(id_key_length) != CURVE25519_KEY_LENGTH) { from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64; return std::size_t(-1); } _olm_curve25519_public_key identity_key; olm::decode_base64(id_key, id_key_length, identity_key.public_key); std::size_t raw_length = b64_input( from_c(one_time_key_message), message_length, from_c(session)->last_error ); if (raw_length == std::size_t(-1)) { return std::size_t(-1); } return from_c(session)->new_inbound_session( *from_c(account), &identity_key, from_c(one_time_key_message), raw_length ); } size_t olm_session_id_length( OlmSession * session ) { return b64_output_length(from_c(session)->session_id_length()); } size_t olm_session_id( OlmSession * session, void * id, size_t id_length ) { std::size_t raw_length = from_c(session)->session_id_length(); if (id_length < b64_output_length(raw_length)) { from_c(session)->last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } std::size_t result = from_c(session)->session_id( b64_output_pos(from_c(id), raw_length), raw_length ); if (result == std::size_t(-1)) { return result; } return b64_output(from_c(id), raw_length); } int olm_session_has_received_message( OlmSession * session ) { return from_c(session)->received_message; } size_t olm_matches_inbound_session( OlmSession * session, void * one_time_key_message, size_t message_length ) { std::size_t raw_length = b64_input( from_c(one_time_key_message), message_length, from_c(session)->last_error ); if (raw_length == std::size_t(-1)) { return std::size_t(-1); } bool matches = from_c(session)->matches_inbound_session( nullptr, from_c(one_time_key_message), raw_length ); return matches ? 1 : 0; } size_t olm_matches_inbound_session_from( OlmSession * session, void const * their_identity_key, size_t their_identity_key_length, void * one_time_key_message, size_t message_length ) { std::uint8_t const * id_key = from_c(their_identity_key); std::size_t id_key_length = their_identity_key_length; if (olm::decode_base64_length(id_key_length) != CURVE25519_KEY_LENGTH) { from_c(session)->last_error = OlmErrorCode::OLM_INVALID_BASE64; return std::size_t(-1); } _olm_curve25519_public_key identity_key; olm::decode_base64(id_key, id_key_length, identity_key.public_key); std::size_t raw_length = b64_input( from_c(one_time_key_message), message_length, from_c(session)->last_error ); if (raw_length == std::size_t(-1)) { return std::size_t(-1); } bool matches = from_c(session)->matches_inbound_session( &identity_key, from_c(one_time_key_message), raw_length ); return matches ? 1 : 0; } size_t olm_remove_one_time_keys( OlmAccount * account, OlmSession * session ) { size_t result = from_c(account)->remove_key( from_c(session)->bob_one_time_key ); if (result == std::size_t(-1)) { from_c(account)->last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID; } return result; } size_t olm_encrypt_message_type( OlmSession * session ) { return size_t(from_c(session)->encrypt_message_type()); } size_t olm_encrypt_random_length( OlmSession * session ) { return from_c(session)->encrypt_random_length(); } size_t olm_encrypt_message_length( OlmSession * session, size_t plaintext_length ) { return b64_output_length( from_c(session)->encrypt_message_length(plaintext_length) ); } size_t olm_encrypt( OlmSession * session, void const * plaintext, size_t plaintext_length, void * random, size_t random_length, void * message, size_t message_length ) { std::size_t raw_length = from_c(session)->encrypt_message_length( plaintext_length ); if (message_length < b64_output_length(raw_length)) { from_c(session)->last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } std::size_t result = from_c(session)->encrypt( from_c(plaintext), plaintext_length, from_c(random), random_length, b64_output_pos(from_c(message), raw_length), raw_length ); olm::unset(random, random_length); if (result == std::size_t(-1)) { return result; } return b64_output(from_c(message), raw_length); } size_t olm_decrypt_max_plaintext_length( OlmSession * session, size_t message_type, void * message, size_t message_length ) { std::size_t raw_length = b64_input( from_c(message), message_length, from_c(session)->last_error ); if (raw_length == std::size_t(-1)) { return std::size_t(-1); } return from_c(session)->decrypt_max_plaintext_length( olm::MessageType(message_type), from_c(message), raw_length ); } size_t olm_decrypt( OlmSession * session, size_t message_type, void * message, size_t message_length, void * plaintext, size_t max_plaintext_length ) { std::size_t raw_length = b64_input( from_c(message), message_length, from_c(session)->last_error ); if (raw_length == std::size_t(-1)) { return std::size_t(-1); } return from_c(session)->decrypt( olm::MessageType(message_type), from_c(message), raw_length, from_c(plaintext), max_plaintext_length ); } size_t olm_sha256_length( OlmUtility * utility ) { return b64_output_length(from_c(utility)->sha256_length()); } size_t olm_sha256( OlmUtility * utility, void const * input, size_t input_length, void * output, size_t output_length ) { std::size_t raw_length = from_c(utility)->sha256_length(); if (output_length < b64_output_length(raw_length)) { from_c(utility)->last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } std::size_t result = from_c(utility)->sha256( from_c(input), input_length, b64_output_pos(from_c(output), raw_length), raw_length ); if (result == std::size_t(-1)) { return result; } return b64_output(from_c(output), raw_length); } size_t olm_ed25519_verify( OlmUtility * utility, void const * key, size_t key_length, void const * message, size_t message_length, void * signature, size_t signature_length ) { if (olm::decode_base64_length(key_length) != CURVE25519_KEY_LENGTH) { from_c(utility)->last_error = OlmErrorCode::OLM_INVALID_BASE64; return std::size_t(-1); } _olm_ed25519_public_key verify_key; olm::decode_base64(from_c(key), key_length, verify_key.public_key); std::size_t raw_signature_length = b64_input( from_c(signature), signature_length, from_c(utility)->last_error ); if (raw_signature_length == std::size_t(-1)) { return std::size_t(-1); } return from_c(utility)->ed25519_verify( verify_key, from_c(message), message_length, from_c(signature), raw_signature_length ); } } olm-2.2.2+git20170526.0fd768e+dfsg/src/outbound_group_session.c000066400000000000000000000241311311755073500234740ustar00rootroot00000000000000/* 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. */ #include "olm/outbound_group_session.h" #include #include #include "olm/base64.h" #include "olm/cipher.h" #include "olm/crypto.h" #include "olm/error.h" #include "olm/megolm.h" #include "olm/memory.h" #include "olm/message.h" #include "olm/pickle.h" #include "olm/pickle_encoding.h" #define OLM_PROTOCOL_VERSION 3 #define GROUP_SESSION_ID_LENGTH ED25519_PUBLIC_KEY_LENGTH #define PICKLE_VERSION 1 #define SESSION_KEY_VERSION 2 struct OlmOutboundGroupSession { /** the Megolm ratchet providing the encryption keys */ Megolm ratchet; /** The ed25519 keypair used for signing the messages */ struct _olm_ed25519_key_pair signing_key; enum OlmErrorCode last_error; }; size_t olm_outbound_group_session_size() { return sizeof(OlmOutboundGroupSession); } OlmOutboundGroupSession * olm_outbound_group_session( void *memory ) { OlmOutboundGroupSession *session = memory; olm_clear_outbound_group_session(session); return session; } const char *olm_outbound_group_session_last_error( const OlmOutboundGroupSession *session ) { return _olm_error_to_string(session->last_error); } size_t olm_clear_outbound_group_session( OlmOutboundGroupSession *session ) { _olm_unset(session, sizeof(OlmOutboundGroupSession)); return sizeof(OlmOutboundGroupSession); } static size_t raw_pickle_length( const OlmOutboundGroupSession *session ) { size_t length = 0; length += _olm_pickle_uint32_length(PICKLE_VERSION); length += megolm_pickle_length(&(session->ratchet)); length += _olm_pickle_ed25519_key_pair_length(&(session->signing_key)); return length; } size_t olm_pickle_outbound_group_session_length( const OlmOutboundGroupSession *session ) { return _olm_enc_output_length(raw_pickle_length(session)); } size_t olm_pickle_outbound_group_session( OlmOutboundGroupSession *session, void const * key, size_t key_length, void * pickled, size_t pickled_length ) { size_t raw_length = raw_pickle_length(session); uint8_t *pos; if (pickled_length < _olm_enc_output_length(raw_length)) { session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL; return (size_t)-1; } pos = _olm_enc_output_pos(pickled, raw_length); pos = _olm_pickle_uint32(pos, PICKLE_VERSION); pos = megolm_pickle(&(session->ratchet), pos); pos = _olm_pickle_ed25519_key_pair(pos, &(session->signing_key)); return _olm_enc_output(key, key_length, pickled, raw_length); } size_t olm_unpickle_outbound_group_session( OlmOutboundGroupSession *session, void const * key, size_t key_length, void * pickled, size_t pickled_length ) { const uint8_t *pos; const uint8_t *end; uint32_t pickle_version; size_t raw_length = _olm_enc_input( key, key_length, pickled, pickled_length, &(session->last_error) ); if (raw_length == (size_t)-1) { return raw_length; } pos = pickled; end = pos + raw_length; pos = _olm_unpickle_uint32(pos, end, &pickle_version); if (pickle_version != PICKLE_VERSION) { session->last_error = OLM_UNKNOWN_PICKLE_VERSION; return (size_t)-1; } pos = megolm_unpickle(&(session->ratchet), pos, end); pos = _olm_unpickle_ed25519_key_pair(pos, end, &(session->signing_key)); if (end != pos) { /* We had the wrong number of bytes in the input. */ session->last_error = OLM_CORRUPTED_PICKLE; return (size_t)-1; } return pickled_length; } size_t olm_init_outbound_group_session_random_length( const OlmOutboundGroupSession *session ) { /* we need data to initialize the megolm ratchet, plus some more for the * session id. */ return MEGOLM_RATCHET_LENGTH + ED25519_RANDOM_LENGTH; } size_t olm_init_outbound_group_session( OlmOutboundGroupSession *session, uint8_t *random, size_t random_length ) { const uint8_t *random_ptr = random; if (random_length < olm_init_outbound_group_session_random_length(session)) { /* Insufficient random data for new session */ session->last_error = OLM_NOT_ENOUGH_RANDOM; return (size_t)-1; } megolm_init(&(session->ratchet), random_ptr, 0); random_ptr += MEGOLM_RATCHET_LENGTH; _olm_crypto_ed25519_generate_key(random_ptr, &(session->signing_key)); random_ptr += ED25519_RANDOM_LENGTH; _olm_unset(random, random_length); return 0; } static size_t raw_message_length( OlmOutboundGroupSession *session, size_t plaintext_length) { size_t ciphertext_length, mac_length; ciphertext_length = megolm_cipher->ops->encrypt_ciphertext_length( megolm_cipher, plaintext_length ); mac_length = megolm_cipher->ops->mac_length(megolm_cipher); return _olm_encode_group_message_length( session->ratchet.counter, ciphertext_length, mac_length, ED25519_SIGNATURE_LENGTH ); } size_t olm_group_encrypt_message_length( OlmOutboundGroupSession *session, size_t plaintext_length ) { size_t message_length = raw_message_length(session, plaintext_length); return _olm_encode_base64_length(message_length); } /** write an un-base64-ed message to the buffer */ static size_t _encrypt( OlmOutboundGroupSession *session, uint8_t const * plaintext, size_t plaintext_length, uint8_t * buffer ) { size_t ciphertext_length, mac_length, message_length; size_t result; uint8_t *ciphertext_ptr; ciphertext_length = megolm_cipher->ops->encrypt_ciphertext_length( megolm_cipher, plaintext_length ); mac_length = megolm_cipher->ops->mac_length(megolm_cipher); /* first we build the message structure, then we encrypt * the plaintext into it. */ message_length = _olm_encode_group_message( OLM_PROTOCOL_VERSION, session->ratchet.counter, ciphertext_length, buffer, &ciphertext_ptr); message_length += mac_length; result = megolm_cipher->ops->encrypt( megolm_cipher, megolm_get_data(&(session->ratchet)), MEGOLM_RATCHET_LENGTH, plaintext, plaintext_length, ciphertext_ptr, ciphertext_length, buffer, message_length ); if (result == (size_t)-1) { return result; } megolm_advance(&(session->ratchet)); /* sign the whole thing with the ed25519 key. */ _olm_crypto_ed25519_sign( &(session->signing_key), buffer, message_length, buffer + message_length ); return result; } size_t olm_group_encrypt( OlmOutboundGroupSession *session, uint8_t const * plaintext, size_t plaintext_length, uint8_t * message, size_t max_message_length ) { size_t rawmsglen; size_t result; uint8_t *message_pos; rawmsglen = raw_message_length(session, plaintext_length); if (max_message_length < _olm_encode_base64_length(rawmsglen)) { session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL; return (size_t)-1; } /* we construct the message at the end of the buffer, so that * we have room to base64-encode it once we're done. */ message_pos = message + _olm_encode_base64_length(rawmsglen) - rawmsglen; /* write the message, and encrypt it, at message_pos */ result = _encrypt(session, plaintext, plaintext_length, message_pos); if (result == (size_t)-1) { return result; } /* bas64-encode it */ return _olm_encode_base64( message_pos, rawmsglen, message ); } size_t olm_outbound_group_session_id_length( const OlmOutboundGroupSession *session ) { return _olm_encode_base64_length(GROUP_SESSION_ID_LENGTH); } size_t olm_outbound_group_session_id( OlmOutboundGroupSession *session, uint8_t * id, size_t id_length ) { if (id_length < olm_outbound_group_session_id_length(session)) { session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL; return (size_t)-1; } return _olm_encode_base64( session->signing_key.public_key.public_key, GROUP_SESSION_ID_LENGTH, id ); } uint32_t olm_outbound_group_session_message_index( OlmOutboundGroupSession *session ) { return session->ratchet.counter; } #define SESSION_KEY_RAW_LENGTH \ (1 + 4 + MEGOLM_RATCHET_LENGTH + ED25519_PUBLIC_KEY_LENGTH\ + ED25519_SIGNATURE_LENGTH) size_t olm_outbound_group_session_key_length( const OlmOutboundGroupSession *session ) { return _olm_encode_base64_length(SESSION_KEY_RAW_LENGTH); } size_t olm_outbound_group_session_key( OlmOutboundGroupSession *session, uint8_t * key, size_t key_length ) { uint8_t *raw; uint8_t *ptr; size_t encoded_length = olm_outbound_group_session_key_length(session); if (key_length < encoded_length) { session->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL; return (size_t)-1; } /* put the raw data at the end of the output buffer. */ raw = ptr = key + encoded_length - SESSION_KEY_RAW_LENGTH; *ptr++ = SESSION_KEY_VERSION; uint32_t counter = session->ratchet.counter; // Encode counter as a big endian 32-bit number. for (unsigned i = 0; i < 4; i++) { *ptr++ = 0xFF & (counter >> 24); counter <<= 8; } memcpy(ptr, megolm_get_data(&session->ratchet), MEGOLM_RATCHET_LENGTH); ptr += MEGOLM_RATCHET_LENGTH; memcpy( ptr, session->signing_key.public_key.public_key, ED25519_PUBLIC_KEY_LENGTH ); ptr += ED25519_PUBLIC_KEY_LENGTH; /* sign the whole thing with the ed25519 key. */ _olm_crypto_ed25519_sign( &(session->signing_key), raw, ptr - raw, ptr ); return _olm_encode_base64(raw, SESSION_KEY_RAW_LENGTH, key); } olm-2.2.2+git20170526.0fd768e+dfsg/src/pickle.cpp000066400000000000000000000136061311755073500204720ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "olm/pickle.hh" #include "olm/pickle.h" std::uint8_t * olm::pickle( std::uint8_t * pos, std::uint32_t value ) { pos += 4; for (unsigned i = 4; i--;) { *(--pos) = value; value >>= 8; } return pos + 4; } std::uint8_t const * olm::unpickle( std::uint8_t const * pos, std::uint8_t const * end, std::uint32_t & value ) { value = 0; if (end < pos + 4) return end; for (unsigned i = 4; i--;) { value <<= 8; value |= *(pos++); } return pos; } std::uint8_t * olm::pickle( std::uint8_t * pos, bool value ) { *(pos++) = value ? 1 : 0; return pos; } std::uint8_t const * olm::unpickle( std::uint8_t const * pos, std::uint8_t const * end, bool & value ) { if (pos == end) return end; value = *(pos++); return pos; } std::uint8_t * olm::pickle_bytes( std::uint8_t * pos, std::uint8_t const * bytes, std::size_t bytes_length ) { std::memcpy(pos, bytes, bytes_length); return pos + bytes_length; } std::uint8_t const * olm::unpickle_bytes( std::uint8_t const * pos, std::uint8_t const * end, std::uint8_t * bytes, std::size_t bytes_length ) { if (end < pos + bytes_length) return end; std::memcpy(bytes, pos, bytes_length); return pos + bytes_length; } std::size_t olm::pickle_length( const _olm_curve25519_public_key & value ) { return sizeof(value.public_key); } std::uint8_t * olm::pickle( std::uint8_t * pos, const _olm_curve25519_public_key & value ) { pos = olm::pickle_bytes( pos, value.public_key, sizeof(value.public_key) ); return pos; } std::uint8_t const * olm::unpickle( std::uint8_t const * pos, std::uint8_t const * end, _olm_curve25519_public_key & value ) { pos = olm::unpickle_bytes( pos, end, value.public_key, sizeof(value.public_key) ); return pos; } std::size_t olm::pickle_length( const _olm_curve25519_key_pair & value ) { return sizeof(value.public_key.public_key) + sizeof(value.private_key.private_key); } std::uint8_t * olm::pickle( std::uint8_t * pos, const _olm_curve25519_key_pair & value ) { pos = olm::pickle_bytes( pos, value.public_key.public_key, sizeof(value.public_key.public_key) ); pos = olm::pickle_bytes( pos, value.private_key.private_key, sizeof(value.private_key.private_key) ); return pos; } std::uint8_t const * olm::unpickle( std::uint8_t const * pos, std::uint8_t const * end, _olm_curve25519_key_pair & value ) { pos = olm::unpickle_bytes( pos, end, value.public_key.public_key, sizeof(value.public_key.public_key) ); pos = olm::unpickle_bytes( pos, end, value.private_key.private_key, sizeof(value.private_key.private_key) ); return pos; } ////// pickle.h implementations std::size_t _olm_pickle_ed25519_public_key_length( const _olm_ed25519_public_key * value ) { return sizeof(value->public_key); } std::uint8_t * _olm_pickle_ed25519_public_key( std::uint8_t * pos, const _olm_ed25519_public_key *value ) { pos = olm::pickle_bytes( pos, value->public_key, sizeof(value->public_key) ); return pos; } std::uint8_t const * _olm_unpickle_ed25519_public_key( std::uint8_t const * pos, std::uint8_t const * end, _olm_ed25519_public_key * value ) { pos = olm::unpickle_bytes( pos, end, value->public_key, sizeof(value->public_key) ); return pos; } std::size_t _olm_pickle_ed25519_key_pair_length( const _olm_ed25519_key_pair *value ) { return sizeof(value->public_key.public_key) + sizeof(value->private_key.private_key); } std::uint8_t * _olm_pickle_ed25519_key_pair( std::uint8_t * pos, const _olm_ed25519_key_pair *value ) { pos = olm::pickle_bytes( pos, value->public_key.public_key, sizeof(value->public_key.public_key) ); pos = olm::pickle_bytes( pos, value->private_key.private_key, sizeof(value->private_key.private_key) ); return pos; } std::uint8_t const * _olm_unpickle_ed25519_key_pair( std::uint8_t const * pos, std::uint8_t const * end, _olm_ed25519_key_pair *value ) { pos = olm::unpickle_bytes( pos, end, value->public_key.public_key, sizeof(value->public_key.public_key) ); pos = olm::unpickle_bytes( pos, end, value->private_key.private_key, sizeof(value->private_key.private_key) ); return pos; } uint8_t * _olm_pickle_uint32(uint8_t * pos, uint32_t value) { return olm::pickle(pos, value); } uint8_t const * _olm_unpickle_uint32( uint8_t const * pos, uint8_t const * end, uint32_t *value ) { return olm::unpickle(pos, end, *value); } uint8_t * _olm_pickle_bool(uint8_t * pos, int value) { return olm::pickle(pos, (bool)value); } uint8_t const * _olm_unpickle_bool( uint8_t const * pos, uint8_t const * end, int *value ) { return olm::unpickle(pos, end, *reinterpret_cast(value)); } uint8_t * _olm_pickle_bytes(uint8_t * pos, uint8_t const * bytes, size_t bytes_length) { return olm::pickle_bytes(pos, bytes, bytes_length); } uint8_t const * _olm_unpickle_bytes(uint8_t const * pos, uint8_t const * end, uint8_t * bytes, size_t bytes_length) { return olm::unpickle_bytes(pos, end, bytes, bytes_length); } olm-2.2.2+git20170526.0fd768e+dfsg/src/pickle_encoding.c000066400000000000000000000057651311755073500220070ustar00rootroot00000000000000/* 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. */ #include "olm/pickle_encoding.h" #include "olm/base64.h" #include "olm/cipher.h" #include "olm/olm.h" static const struct _olm_cipher_aes_sha_256 PICKLE_CIPHER = OLM_CIPHER_INIT_AES_SHA_256("Pickle"); size_t _olm_enc_output_length( size_t raw_length ) { const struct _olm_cipher *cipher = OLM_CIPHER_BASE(&PICKLE_CIPHER); size_t length = cipher->ops->encrypt_ciphertext_length(cipher, raw_length); length += cipher->ops->mac_length(cipher); return _olm_encode_base64_length(length); } uint8_t * _olm_enc_output_pos( uint8_t * output, size_t raw_length ) { const struct _olm_cipher *cipher = OLM_CIPHER_BASE(&PICKLE_CIPHER); size_t length = cipher->ops->encrypt_ciphertext_length(cipher, raw_length); length += cipher->ops->mac_length(cipher); return output + _olm_encode_base64_length(length) - length; } size_t _olm_enc_output( uint8_t const * key, size_t key_length, uint8_t * output, size_t raw_length ) { const struct _olm_cipher *cipher = OLM_CIPHER_BASE(&PICKLE_CIPHER); size_t ciphertext_length = cipher->ops->encrypt_ciphertext_length( cipher, raw_length ); size_t length = ciphertext_length + cipher->ops->mac_length(cipher); size_t base64_length = _olm_encode_base64_length(length); uint8_t * raw_output = output + base64_length - length; cipher->ops->encrypt( cipher, key, key_length, raw_output, raw_length, raw_output, ciphertext_length, raw_output, length ); _olm_encode_base64(raw_output, length, output); return base64_length; } size_t _olm_enc_input(uint8_t const * key, size_t key_length, uint8_t * input, size_t b64_length, enum OlmErrorCode * last_error ) { size_t enc_length = _olm_decode_base64_length(b64_length); if (enc_length == (size_t)-1) { if (last_error) { *last_error = OLM_INVALID_BASE64; } return (size_t)-1; } _olm_decode_base64(input, b64_length, input); const struct _olm_cipher *cipher = OLM_CIPHER_BASE(&PICKLE_CIPHER); size_t raw_length = enc_length - cipher->ops->mac_length(cipher); size_t result = cipher->ops->decrypt( cipher, key, key_length, input, enc_length, input, raw_length, input, raw_length ); if (result == (size_t)-1 && last_error) { *last_error = OLM_BAD_ACCOUNT_KEY; } return result; } olm-2.2.2+git20170526.0fd768e+dfsg/src/ratchet.cpp000066400000000000000000000445601311755073500206600ustar00rootroot00000000000000/* Copyright 2015, 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. */ #include "olm/ratchet.hh" #include "olm/message.hh" #include "olm/memory.hh" #include "olm/cipher.h" #include "olm/pickle.hh" #include namespace { static const std::uint8_t PROTOCOL_VERSION = 3; static const std::uint8_t MESSAGE_KEY_SEED[1] = {0x01}; static const std::uint8_t CHAIN_KEY_SEED[1] = {0x02}; static const std::size_t MAX_MESSAGE_GAP = 2000; /** * Advance the root key, creating a new message chain. * * @param root_key previous root key R(n-1) * @param our_key our new ratchet key T(n) * @param their_key their most recent ratchet key T(n-1) * @param info table of constants for the ratchet function * @param new_root_key[out] returns the new root key R(n) * @param new_chain_key[out] returns the first chain key in the new chain * C(n,0) */ static void create_chain_key( olm::SharedKey const & root_key, _olm_curve25519_key_pair const & our_key, _olm_curve25519_public_key const & their_key, olm::KdfInfo const & info, olm::SharedKey & new_root_key, olm::ChainKey & new_chain_key ) { olm::SharedKey secret; _olm_crypto_curve25519_shared_secret(&our_key, &their_key, secret); std::uint8_t derived_secrets[2 * olm::OLM_SHARED_KEY_LENGTH]; _olm_crypto_hkdf_sha256( secret, sizeof(secret), root_key, sizeof(root_key), info.ratchet_info, info.ratchet_info_length, derived_secrets, sizeof(derived_secrets) ); std::uint8_t const * pos = derived_secrets; pos = olm::load_array(new_root_key, pos); pos = olm::load_array(new_chain_key.key, pos); new_chain_key.index = 0; olm::unset(derived_secrets); olm::unset(secret); } static void advance_chain_key( olm::ChainKey const & chain_key, olm::ChainKey & new_chain_key ) { _olm_crypto_hmac_sha256( chain_key.key, sizeof(chain_key.key), CHAIN_KEY_SEED, sizeof(CHAIN_KEY_SEED), new_chain_key.key ); new_chain_key.index = chain_key.index + 1; } static void create_message_keys( olm::ChainKey const & chain_key, olm::KdfInfo const & info, olm::MessageKey & message_key) { _olm_crypto_hmac_sha256( chain_key.key, sizeof(chain_key.key), MESSAGE_KEY_SEED, sizeof(MESSAGE_KEY_SEED), message_key.key ); message_key.index = chain_key.index; } static std::size_t verify_mac_and_decrypt( _olm_cipher const *cipher, olm::MessageKey const & message_key, olm::MessageReader const & reader, std::uint8_t * plaintext, std::size_t max_plaintext_length ) { return cipher->ops->decrypt( cipher, message_key.key, sizeof(message_key.key), reader.input, reader.input_length, reader.ciphertext, reader.ciphertext_length, plaintext, max_plaintext_length ); } static std::size_t verify_mac_and_decrypt_for_existing_chain( olm::Ratchet const & session, olm::ChainKey const & chain, olm::MessageReader const & reader, std::uint8_t * plaintext, std::size_t max_plaintext_length ) { if (reader.counter < chain.index) { return std::size_t(-1); } /* Limit the number of hashes we're prepared to compute */ if (reader.counter - chain.index > MAX_MESSAGE_GAP) { return std::size_t(-1); } olm::ChainKey new_chain = chain; while (new_chain.index < reader.counter) { advance_chain_key(new_chain, new_chain); } olm::MessageKey message_key; create_message_keys(new_chain, session.kdf_info, message_key); std::size_t result = verify_mac_and_decrypt( session.ratchet_cipher, message_key, reader, plaintext, max_plaintext_length ); olm::unset(new_chain); return result; } static std::size_t verify_mac_and_decrypt_for_new_chain( olm::Ratchet const & session, olm::MessageReader const & reader, std::uint8_t * plaintext, std::size_t max_plaintext_length ) { olm::SharedKey new_root_key; olm::ReceiverChain new_chain; /* They shouldn't move to a new chain until we've sent them a message * acknowledging the last one */ if (session.sender_chain.empty()) { return std::size_t(-1); } /* Limit the number of hashes we're prepared to compute */ if (reader.counter > MAX_MESSAGE_GAP) { return std::size_t(-1); } olm::load_array(new_chain.ratchet_key.public_key, reader.ratchet_key); create_chain_key( session.root_key, session.sender_chain[0].ratchet_key, new_chain.ratchet_key, session.kdf_info, new_root_key, new_chain.chain_key ); std::size_t result = verify_mac_and_decrypt_for_existing_chain( session, new_chain.chain_key, reader, plaintext, max_plaintext_length ); olm::unset(new_root_key); olm::unset(new_chain); return result; } } // namespace olm::Ratchet::Ratchet( olm::KdfInfo const & kdf_info, _olm_cipher const * ratchet_cipher ) : kdf_info(kdf_info), ratchet_cipher(ratchet_cipher), last_error(OlmErrorCode::OLM_SUCCESS) { } void olm::Ratchet::initialise_as_bob( std::uint8_t const * shared_secret, std::size_t shared_secret_length, _olm_curve25519_public_key const & their_ratchet_key ) { std::uint8_t derived_secrets[2 * olm::OLM_SHARED_KEY_LENGTH]; _olm_crypto_hkdf_sha256( shared_secret, shared_secret_length, nullptr, 0, kdf_info.root_info, kdf_info.root_info_length, derived_secrets, sizeof(derived_secrets) ); receiver_chains.insert(); receiver_chains[0].chain_key.index = 0; std::uint8_t const * pos = derived_secrets; pos = olm::load_array(root_key, pos); pos = olm::load_array(receiver_chains[0].chain_key.key, pos); receiver_chains[0].ratchet_key = their_ratchet_key; olm::unset(derived_secrets); } void olm::Ratchet::initialise_as_alice( std::uint8_t const * shared_secret, std::size_t shared_secret_length, _olm_curve25519_key_pair const & our_ratchet_key ) { std::uint8_t derived_secrets[2 * olm::OLM_SHARED_KEY_LENGTH]; _olm_crypto_hkdf_sha256( shared_secret, shared_secret_length, nullptr, 0, kdf_info.root_info, kdf_info.root_info_length, derived_secrets, sizeof(derived_secrets) ); sender_chain.insert(); sender_chain[0].chain_key.index = 0; std::uint8_t const * pos = derived_secrets; pos = olm::load_array(root_key, pos); pos = olm::load_array(sender_chain[0].chain_key.key, pos); sender_chain[0].ratchet_key = our_ratchet_key; olm::unset(derived_secrets); } namespace olm { static std::size_t pickle_length( const olm::SharedKey & value ) { return olm::OLM_SHARED_KEY_LENGTH; } static std::uint8_t * pickle( std::uint8_t * pos, const olm::SharedKey & value ) { return olm::pickle_bytes(pos, value, olm::OLM_SHARED_KEY_LENGTH); } static std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, olm::SharedKey & value ) { return olm::unpickle_bytes(pos, end, value, olm::OLM_SHARED_KEY_LENGTH); } static std::size_t pickle_length( const olm::SenderChain & value ) { std::size_t length = 0; length += olm::pickle_length(value.ratchet_key); length += olm::pickle_length(value.chain_key.key); length += olm::pickle_length(value.chain_key.index); return length; } static std::uint8_t * pickle( std::uint8_t * pos, const olm::SenderChain & value ) { pos = olm::pickle(pos, value.ratchet_key); pos = olm::pickle(pos, value.chain_key.key); pos = olm::pickle(pos, value.chain_key.index); return pos; } static std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, olm::SenderChain & value ) { pos = olm::unpickle(pos, end, value.ratchet_key); pos = olm::unpickle(pos, end, value.chain_key.key); pos = olm::unpickle(pos, end, value.chain_key.index); return pos; } static std::size_t pickle_length( const olm::ReceiverChain & value ) { std::size_t length = 0; length += olm::pickle_length(value.ratchet_key); length += olm::pickle_length(value.chain_key.key); length += olm::pickle_length(value.chain_key.index); return length; } static std::uint8_t * pickle( std::uint8_t * pos, const olm::ReceiverChain & value ) { pos = olm::pickle(pos, value.ratchet_key); pos = olm::pickle(pos, value.chain_key.key); pos = olm::pickle(pos, value.chain_key.index); return pos; } static std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, olm::ReceiverChain & value ) { pos = olm::unpickle(pos, end, value.ratchet_key); pos = olm::unpickle(pos, end, value.chain_key.key); pos = olm::unpickle(pos, end, value.chain_key.index); return pos; } static std::size_t pickle_length( const olm::SkippedMessageKey & value ) { std::size_t length = 0; length += olm::pickle_length(value.ratchet_key); length += olm::pickle_length(value.message_key.key); length += olm::pickle_length(value.message_key.index); return length; } static std::uint8_t * pickle( std::uint8_t * pos, const olm::SkippedMessageKey & value ) { pos = olm::pickle(pos, value.ratchet_key); pos = olm::pickle(pos, value.message_key.key); pos = olm::pickle(pos, value.message_key.index); return pos; } static std::uint8_t const * unpickle( std::uint8_t const * pos, std::uint8_t const * end, olm::SkippedMessageKey & value ) { pos = olm::unpickle(pos, end, value.ratchet_key); pos = olm::unpickle(pos, end, value.message_key.key); pos = olm::unpickle(pos, end, value.message_key.index); return pos; } } // namespace olm std::size_t olm::pickle_length( olm::Ratchet const & value ) { std::size_t length = 0; length += olm::OLM_SHARED_KEY_LENGTH; length += olm::pickle_length(value.sender_chain); length += olm::pickle_length(value.receiver_chains); length += olm::pickle_length(value.skipped_message_keys); return length; } std::uint8_t * olm::pickle( std::uint8_t * pos, olm::Ratchet const & value ) { pos = pickle(pos, value.root_key); pos = pickle(pos, value.sender_chain); pos = pickle(pos, value.receiver_chains); pos = pickle(pos, value.skipped_message_keys); return pos; } std::uint8_t const * olm::unpickle( std::uint8_t const * pos, std::uint8_t const * end, olm::Ratchet & value, bool includes_chain_index ) { pos = unpickle(pos, end, value.root_key); pos = unpickle(pos, end, value.sender_chain); pos = unpickle(pos, end, value.receiver_chains); pos = unpickle(pos, end, value.skipped_message_keys); // pickle v 0x80000001 includes a chain index; pickle v1 does not. if (includes_chain_index) { std::uint32_t dummy; pos = unpickle(pos, end, dummy); } return pos; } std::size_t olm::Ratchet::encrypt_output_length( std::size_t plaintext_length ) { std::size_t counter = 0; if (!sender_chain.empty()) { counter = sender_chain[0].chain_key.index; } std::size_t padded = ratchet_cipher->ops->encrypt_ciphertext_length( ratchet_cipher, plaintext_length ); return olm::encode_message_length( counter, CURVE25519_KEY_LENGTH, padded, ratchet_cipher->ops->mac_length(ratchet_cipher) ); } std::size_t olm::Ratchet::encrypt_random_length() { return sender_chain.empty() ? CURVE25519_RANDOM_LENGTH : 0; } std::size_t olm::Ratchet::encrypt( std::uint8_t const * plaintext, std::size_t plaintext_length, std::uint8_t const * random, std::size_t random_length, std::uint8_t * output, std::size_t max_output_length ) { std::size_t output_length = encrypt_output_length(plaintext_length); if (random_length < encrypt_random_length()) { last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM; return std::size_t(-1); } if (max_output_length < output_length) { last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } if (sender_chain.empty()) { sender_chain.insert(); _olm_crypto_curve25519_generate_key(random, &sender_chain[0].ratchet_key); create_chain_key( root_key, sender_chain[0].ratchet_key, receiver_chains[0].ratchet_key, kdf_info, root_key, sender_chain[0].chain_key ); } MessageKey keys; create_message_keys(sender_chain[0].chain_key, kdf_info, keys); advance_chain_key(sender_chain[0].chain_key, sender_chain[0].chain_key); std::size_t ciphertext_length = ratchet_cipher->ops->encrypt_ciphertext_length( ratchet_cipher, plaintext_length ); std::uint32_t counter = keys.index; _olm_curve25519_public_key const & ratchet_key = sender_chain[0].ratchet_key.public_key; olm::MessageWriter writer; olm::encode_message( writer, PROTOCOL_VERSION, counter, CURVE25519_KEY_LENGTH, ciphertext_length, output ); olm::store_array(writer.ratchet_key, ratchet_key.public_key); ratchet_cipher->ops->encrypt( ratchet_cipher, keys.key, sizeof(keys.key), plaintext, plaintext_length, writer.ciphertext, ciphertext_length, output, output_length ); olm::unset(keys); return output_length; } std::size_t olm::Ratchet::decrypt_max_plaintext_length( std::uint8_t const * input, std::size_t input_length ) { olm::MessageReader reader; olm::decode_message( reader, input, input_length, ratchet_cipher->ops->mac_length(ratchet_cipher) ); if (!reader.ciphertext) { last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT; return std::size_t(-1); } return ratchet_cipher->ops->decrypt_max_plaintext_length( ratchet_cipher, reader.ciphertext_length); } std::size_t olm::Ratchet::decrypt( std::uint8_t const * input, std::size_t input_length, std::uint8_t * plaintext, std::size_t max_plaintext_length ) { olm::MessageReader reader; olm::decode_message( reader, input, input_length, ratchet_cipher->ops->mac_length(ratchet_cipher) ); if (reader.version != PROTOCOL_VERSION) { last_error = OlmErrorCode::OLM_BAD_MESSAGE_VERSION; return std::size_t(-1); } if (!reader.has_counter || !reader.ratchet_key || !reader.ciphertext) { last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT; return std::size_t(-1); } std::size_t max_length = ratchet_cipher->ops->decrypt_max_plaintext_length( ratchet_cipher, reader.ciphertext_length ); if (max_plaintext_length < max_length) { last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } if (reader.ratchet_key_length != CURVE25519_KEY_LENGTH) { last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT; return std::size_t(-1); } ReceiverChain * chain = nullptr; for (olm::ReceiverChain & receiver_chain : receiver_chains) { if (0 == std::memcmp( receiver_chain.ratchet_key.public_key, reader.ratchet_key, CURVE25519_KEY_LENGTH )) { chain = &receiver_chain; break; } } std::size_t result = std::size_t(-1); if (!chain) { result = verify_mac_and_decrypt_for_new_chain( *this, reader, plaintext, max_plaintext_length ); } else if (chain->chain_key.index > reader.counter) { /* Chain already advanced beyond the key for this message * Check if the message keys are in the skipped key list. */ for (olm::SkippedMessageKey & skipped : skipped_message_keys) { if (reader.counter == skipped.message_key.index && 0 == std::memcmp( skipped.ratchet_key.public_key, reader.ratchet_key, CURVE25519_KEY_LENGTH ) ) { /* Found the key for this message. Check the MAC. */ result = verify_mac_and_decrypt( ratchet_cipher, skipped.message_key, reader, plaintext, max_plaintext_length ); if (result != std::size_t(-1)) { /* Remove the key from the skipped keys now that we've * decoded the message it corresponds to. */ olm::unset(skipped); skipped_message_keys.erase(&skipped); return result; } } } } else { result = verify_mac_and_decrypt_for_existing_chain( *this, chain->chain_key, reader, plaintext, max_plaintext_length ); } if (result == std::size_t(-1)) { last_error = OlmErrorCode::OLM_BAD_MESSAGE_MAC; return std::size_t(-1); } if (!chain) { /* They have started using a new ephemeral ratchet key. * We need to derive a new set of chain keys. * We can discard our previous empheral ratchet key. * We will generate a new key when we send the next message. */ chain = receiver_chains.insert(); olm::load_array(chain->ratchet_key.public_key, reader.ratchet_key); // TODO: we've already done this once, in // verify_mac_and_decrypt_for_new_chain(). we could reuse the result. create_chain_key( root_key, sender_chain[0].ratchet_key, chain->ratchet_key, kdf_info, root_key, chain->chain_key ); olm::unset(sender_chain[0]); sender_chain.erase(sender_chain.begin()); } while (chain->chain_key.index < reader.counter) { olm::SkippedMessageKey & key = *skipped_message_keys.insert(); create_message_keys(chain->chain_key, kdf_info, key.message_key); key.ratchet_key = chain->ratchet_key; advance_chain_key(chain->chain_key, chain->chain_key); } advance_chain_key(chain->chain_key, chain->chain_key); return result; } olm-2.2.2+git20170526.0fd768e+dfsg/src/session.cpp000066400000000000000000000337221311755073500207070ustar00rootroot00000000000000/* Copyright 2015, 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. */ #include "olm/session.hh" #include "olm/cipher.h" #include "olm/crypto.h" #include "olm/account.hh" #include "olm/memory.hh" #include "olm/message.hh" #include "olm/pickle.hh" #include namespace { static const std::uint8_t PROTOCOL_VERSION = 0x3; static const std::uint8_t ROOT_KDF_INFO[] = "OLM_ROOT"; static const std::uint8_t RATCHET_KDF_INFO[] = "OLM_RATCHET"; static const std::uint8_t CIPHER_KDF_INFO[] = "OLM_KEYS"; static const olm::KdfInfo OLM_KDF_INFO = { ROOT_KDF_INFO, sizeof(ROOT_KDF_INFO) - 1, RATCHET_KDF_INFO, sizeof(RATCHET_KDF_INFO) - 1 }; static const struct _olm_cipher_aes_sha_256 OLM_CIPHER = OLM_CIPHER_INIT_AES_SHA_256(CIPHER_KDF_INFO); } // namespace olm::Session::Session( ) : ratchet(OLM_KDF_INFO, OLM_CIPHER_BASE(&OLM_CIPHER)), last_error(OlmErrorCode::OLM_SUCCESS), received_message(false) { } std::size_t olm::Session::new_outbound_session_random_length() { return CURVE25519_RANDOM_LENGTH * 2; } std::size_t olm::Session::new_outbound_session( olm::Account const & local_account, _olm_curve25519_public_key const & identity_key, _olm_curve25519_public_key const & one_time_key, std::uint8_t const * random, std::size_t random_length ) { if (random_length < new_outbound_session_random_length()) { last_error = OlmErrorCode::OLM_NOT_ENOUGH_RANDOM; return std::size_t(-1); } _olm_curve25519_key_pair base_key; _olm_crypto_curve25519_generate_key(random, &base_key); _olm_curve25519_key_pair ratchet_key; _olm_crypto_curve25519_generate_key(random + CURVE25519_RANDOM_LENGTH, &ratchet_key); _olm_curve25519_key_pair const & alice_identity_key_pair = ( local_account.identity_keys.curve25519_key ); received_message = false; alice_identity_key = alice_identity_key_pair.public_key; alice_base_key = base_key.public_key; bob_one_time_key = one_time_key; // Calculate the shared secret S via triple DH std::uint8_t secret[3 * CURVE25519_SHARED_SECRET_LENGTH]; std::uint8_t * pos = secret; _olm_crypto_curve25519_shared_secret(&alice_identity_key_pair, &one_time_key, pos); pos += CURVE25519_SHARED_SECRET_LENGTH; _olm_crypto_curve25519_shared_secret(&base_key, &identity_key, pos); pos += CURVE25519_SHARED_SECRET_LENGTH; _olm_crypto_curve25519_shared_secret(&base_key, &one_time_key, pos); ratchet.initialise_as_alice(secret, sizeof(secret), ratchet_key); olm::unset(base_key); olm::unset(ratchet_key); olm::unset(secret); return std::size_t(0); } namespace { static bool check_message_fields( olm::PreKeyMessageReader & reader, bool have_their_identity_key ) { bool ok = true; ok = ok && (have_their_identity_key || reader.identity_key); if (reader.identity_key) { ok = ok && reader.identity_key_length == CURVE25519_KEY_LENGTH; } ok = ok && reader.message; ok = ok && reader.base_key; ok = ok && reader.base_key_length == CURVE25519_KEY_LENGTH; ok = ok && reader.one_time_key; ok = ok && reader.one_time_key_length == CURVE25519_KEY_LENGTH; return ok; } } // namespace std::size_t olm::Session::new_inbound_session( olm::Account & local_account, _olm_curve25519_public_key const * their_identity_key, std::uint8_t const * one_time_key_message, std::size_t message_length ) { olm::PreKeyMessageReader reader; decode_one_time_key_message(reader, one_time_key_message, message_length); if (!check_message_fields(reader, their_identity_key)) { last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT; return std::size_t(-1); } if (reader.identity_key && their_identity_key) { bool same = 0 == std::memcmp( their_identity_key->public_key, reader.identity_key, CURVE25519_KEY_LENGTH ); if (!same) { last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID; return std::size_t(-1); } } olm::load_array(alice_identity_key.public_key, reader.identity_key); olm::load_array(alice_base_key.public_key, reader.base_key); olm::load_array(bob_one_time_key.public_key, reader.one_time_key); olm::MessageReader message_reader; decode_message( message_reader, reader.message, reader.message_length, ratchet.ratchet_cipher->ops->mac_length(ratchet.ratchet_cipher) ); if (!message_reader.ratchet_key || message_reader.ratchet_key_length != CURVE25519_KEY_LENGTH) { last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT; return std::size_t(-1); } _olm_curve25519_public_key ratchet_key; olm::load_array(ratchet_key.public_key, message_reader.ratchet_key); olm::OneTimeKey const * our_one_time_key = local_account.lookup_key( bob_one_time_key ); if (!our_one_time_key) { last_error = OlmErrorCode::OLM_BAD_MESSAGE_KEY_ID; return std::size_t(-1); } _olm_curve25519_key_pair const & bob_identity_key = ( local_account.identity_keys.curve25519_key ); _olm_curve25519_key_pair const & bob_one_time_key = our_one_time_key->key; // Calculate the shared secret S via triple DH std::uint8_t secret[CURVE25519_SHARED_SECRET_LENGTH * 3]; std::uint8_t * pos = secret; _olm_crypto_curve25519_shared_secret(&bob_one_time_key, &alice_identity_key, pos); pos += CURVE25519_SHARED_SECRET_LENGTH; _olm_crypto_curve25519_shared_secret(&bob_identity_key, &alice_base_key, pos); pos += CURVE25519_SHARED_SECRET_LENGTH; _olm_crypto_curve25519_shared_secret(&bob_one_time_key, &alice_base_key, pos); ratchet.initialise_as_bob(secret, sizeof(secret), ratchet_key); olm::unset(secret); return std::size_t(0); } std::size_t olm::Session::session_id_length() { return SHA256_OUTPUT_LENGTH; } std::size_t olm::Session::session_id( std::uint8_t * id, std::size_t id_length ) { if (id_length < session_id_length()) { last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } std::uint8_t tmp[CURVE25519_KEY_LENGTH * 3]; std::uint8_t * pos = tmp; pos = olm::store_array(pos, alice_identity_key.public_key); pos = olm::store_array(pos, alice_base_key.public_key); pos = olm::store_array(pos, bob_one_time_key.public_key); _olm_crypto_sha256(tmp, sizeof(tmp), id); return session_id_length(); } bool olm::Session::matches_inbound_session( _olm_curve25519_public_key const * their_identity_key, std::uint8_t const * one_time_key_message, std::size_t message_length ) { olm::PreKeyMessageReader reader; decode_one_time_key_message(reader, one_time_key_message, message_length); if (!check_message_fields(reader, their_identity_key)) { return false; } bool same = true; if (reader.identity_key) { same = same && 0 == std::memcmp( reader.identity_key, alice_identity_key.public_key, CURVE25519_KEY_LENGTH ); } if (their_identity_key) { same = same && 0 == std::memcmp( their_identity_key->public_key, alice_identity_key.public_key, CURVE25519_KEY_LENGTH ); } same = same && 0 == std::memcmp( reader.base_key, alice_base_key.public_key, CURVE25519_KEY_LENGTH ); same = same && 0 == std::memcmp( reader.one_time_key, bob_one_time_key.public_key, CURVE25519_KEY_LENGTH ); return same; } olm::MessageType olm::Session::encrypt_message_type() { if (received_message) { return olm::MessageType::MESSAGE; } else { return olm::MessageType::PRE_KEY; } } std::size_t olm::Session::encrypt_message_length( std::size_t plaintext_length ) { std::size_t message_length = ratchet.encrypt_output_length( plaintext_length ); if (received_message) { return message_length; } return encode_one_time_key_message_length( CURVE25519_KEY_LENGTH, CURVE25519_KEY_LENGTH, CURVE25519_KEY_LENGTH, message_length ); } std::size_t olm::Session::encrypt_random_length() { return ratchet.encrypt_random_length(); } std::size_t olm::Session::encrypt( std::uint8_t const * plaintext, std::size_t plaintext_length, std::uint8_t const * random, std::size_t random_length, std::uint8_t * message, std::size_t message_length ) { if (message_length < encrypt_message_length(plaintext_length)) { last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } std::uint8_t * message_body; std::size_t message_body_length = ratchet.encrypt_output_length( plaintext_length ); if (received_message) { message_body = message; } else { olm::PreKeyMessageWriter writer; encode_one_time_key_message( writer, PROTOCOL_VERSION, CURVE25519_KEY_LENGTH, CURVE25519_KEY_LENGTH, CURVE25519_KEY_LENGTH, message_body_length, message ); olm::store_array(writer.one_time_key, bob_one_time_key.public_key); olm::store_array(writer.identity_key, alice_identity_key.public_key); olm::store_array(writer.base_key, alice_base_key.public_key); message_body = writer.message; } std::size_t result = ratchet.encrypt( plaintext, plaintext_length, random, random_length, message_body, message_body_length ); if (result == std::size_t(-1)) { last_error = ratchet.last_error; ratchet.last_error = OlmErrorCode::OLM_SUCCESS; return result; } return result; } std::size_t olm::Session::decrypt_max_plaintext_length( MessageType message_type, std::uint8_t const * message, std::size_t message_length ) { std::uint8_t const * message_body; std::size_t message_body_length; if (message_type == olm::MessageType::MESSAGE) { message_body = message; message_body_length = message_length; } else { olm::PreKeyMessageReader reader; decode_one_time_key_message(reader, message, message_length); if (!reader.message) { last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT; return std::size_t(-1); } message_body = reader.message; message_body_length = reader.message_length; } std::size_t result = ratchet.decrypt_max_plaintext_length( message_body, message_body_length ); if (result == std::size_t(-1)) { last_error = ratchet.last_error; ratchet.last_error = OlmErrorCode::OLM_SUCCESS; } return result; } std::size_t olm::Session::decrypt( olm::MessageType message_type, std::uint8_t const * message, std::size_t message_length, std::uint8_t * plaintext, std::size_t max_plaintext_length ) { std::uint8_t const * message_body; std::size_t message_body_length; if (message_type == olm::MessageType::MESSAGE) { message_body = message; message_body_length = message_length; } else { olm::PreKeyMessageReader reader; decode_one_time_key_message(reader, message, message_length); if (!reader.message) { last_error = OlmErrorCode::OLM_BAD_MESSAGE_FORMAT; return std::size_t(-1); } message_body = reader.message; message_body_length = reader.message_length; } std::size_t result = ratchet.decrypt( message_body, message_body_length, plaintext, max_plaintext_length ); if (result == std::size_t(-1)) { last_error = ratchet.last_error; ratchet.last_error = OlmErrorCode::OLM_SUCCESS; return result; } received_message = true; return result; } namespace { // the master branch writes pickle version 1; the logging_enabled branch writes // 0x80000001. static const std::uint32_t SESSION_PICKLE_VERSION = 1; } std::size_t olm::pickle_length( Session const & value ) { std::size_t length = 0; length += olm::pickle_length(SESSION_PICKLE_VERSION); length += olm::pickle_length(value.received_message); length += olm::pickle_length(value.alice_identity_key); length += olm::pickle_length(value.alice_base_key); length += olm::pickle_length(value.bob_one_time_key); length += olm::pickle_length(value.ratchet); return length; } std::uint8_t * olm::pickle( std::uint8_t * pos, Session const & value ) { pos = olm::pickle(pos, SESSION_PICKLE_VERSION); pos = olm::pickle(pos, value.received_message); pos = olm::pickle(pos, value.alice_identity_key); pos = olm::pickle(pos, value.alice_base_key); pos = olm::pickle(pos, value.bob_one_time_key); pos = olm::pickle(pos, value.ratchet); return pos; } std::uint8_t const * olm::unpickle( std::uint8_t const * pos, std::uint8_t const * end, Session & value ) { uint32_t pickle_version; pos = olm::unpickle(pos, end, pickle_version); bool includes_chain_index; switch (pickle_version) { case 1: includes_chain_index = false; break; case 0x80000001UL: includes_chain_index = true; break; default: value.last_error = OlmErrorCode::OLM_UNKNOWN_PICKLE_VERSION; return end; } pos = olm::unpickle(pos, end, value.received_message); pos = olm::unpickle(pos, end, value.alice_identity_key); pos = olm::unpickle(pos, end, value.alice_base_key); pos = olm::unpickle(pos, end, value.bob_one_time_key); pos = olm::unpickle(pos, end, value.ratchet, includes_chain_index); return pos; } olm-2.2.2+git20170526.0fd768e+dfsg/src/utility.cpp000066400000000000000000000033101311755073500207150ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "olm/utility.hh" #include "olm/crypto.h" olm::Utility::Utility( ) : last_error(OlmErrorCode::OLM_SUCCESS) { } size_t olm::Utility::sha256_length() { return SHA256_OUTPUT_LENGTH; } size_t olm::Utility::sha256( std::uint8_t const * input, std::size_t input_length, std::uint8_t * output, std::size_t output_length ) { if (output_length < sha256_length()) { last_error = OlmErrorCode::OLM_OUTPUT_BUFFER_TOO_SMALL; return std::size_t(-1); } _olm_crypto_sha256(input, input_length, output); return SHA256_OUTPUT_LENGTH; } size_t olm::Utility::ed25519_verify( _olm_ed25519_public_key const & key, std::uint8_t const * message, std::size_t message_length, std::uint8_t const * signature, std::size_t signature_length ) { if (signature_length < ED25519_SIGNATURE_LENGTH) { last_error = OlmErrorCode::OLM_BAD_MESSAGE_MAC; return std::size_t(-1); } if (!_olm_crypto_ed25519_verify(&key, message, message_length, signature)) { last_error = OlmErrorCode::OLM_BAD_MESSAGE_MAC; return std::size_t(-1); } return std::size_t(0); } olm-2.2.2+git20170526.0fd768e+dfsg/tests/000077500000000000000000000000001311755073500170645ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/tests/include/000077500000000000000000000000001311755073500205075ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/tests/include/unittest.hh000066400000000000000000000061661311755073500227200ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include std::ostream & print_hex( std::ostream & os, std::uint8_t const * data, std::size_t length ) { for (std::size_t i = 0; i < length; i++) { os << std::setw(2) << std::setfill('0') << std::right << std::hex << (int) data[i]; } return os; } char const * TEST_CASE; template void assert_equals( const char *file, unsigned line, const char *expected_expr, const char *actual_expr, T const & expected, T const & actual ) { if (expected != actual) { std::cout << "FAILED: " << TEST_CASE << std::endl; std::cout << file << ":" << line << std::endl; std::cout << expected_expr << " == " << actual_expr << std::endl; std::cout << "Expected: " << expected << std::endl; std::cout << "Actual: " << actual << std::endl; std::exit(1); } } template void assert_not_equals( const char *file, unsigned line, const char *expected_expr, const char *actual_expr, T const & expected, T const & actual ) { if (expected == actual) { std::cout << "FAILED: " << TEST_CASE << std::endl; std::cout << file << ":" << line << std::endl; std::cout << expected_expr << " == " << actual_expr << std::endl; std::cout << "Unexpected: " << expected << std::endl; std::cout << "Actual: " << actual << std::endl; std::exit(1); } } void assert_equals( const char *file, unsigned line, const char *expected_expr, const char *actual_expr, std::uint8_t const * expected, std::uint8_t const * actual, std::size_t length ) { if (std::memcmp(expected, actual, length)) { std::cout << "FAILED: " << TEST_CASE << std::endl; std::cout << file << ":" << line << std::endl; std::cout << expected_expr << " == " << actual_expr << std::endl; print_hex(std::cout << "Expected: ", expected, length) << std::endl; print_hex(std::cout << "Actual: ", actual, length) << std::endl; std::exit(1); } } #define assert_equals(expected, actual, ...) assert_equals( \ __FILE__, __LINE__, #expected, #actual, expected, actual, ##__VA_ARGS__ \ ) #define assert_not_equals(expected, actual, ...) assert_not_equals( \ __FILE__, __LINE__, #expected, #actual, expected, actual, ##__VA_ARGS__ \ ) class TestCase { public: TestCase(const char *name) { TEST_CASE = name; } ~TestCase() { std::cout << "PASSED: " << TEST_CASE << std::endl; } }; olm-2.2.2+git20170526.0fd768e+dfsg/tests/test_base64.cpp000066400000000000000000000040461311755073500217170ustar00rootroot00000000000000#include "olm/base64.hh" #include "olm/base64.h" #include "unittest.hh" int main() { { /* Base64 encode test */ TestCase test_case("Base64 C++ binding encode test"); std::uint8_t input[] = "Hello World"; std::uint8_t expected_output[] = "SGVsbG8gV29ybGQ"; std::size_t input_length = sizeof(input) - 1; std::size_t output_length = olm::encode_base64_length(input_length); assert_equals(std::size_t(15), output_length); std::uint8_t output[output_length]; olm::encode_base64(input, input_length, output); assert_equals(expected_output, output, output_length); } { TestCase test_case("Base64 C binding encode test"); std::uint8_t input[] = "Hello World"; std::uint8_t expected_output[] = "SGVsbG8gV29ybGQ"; std::size_t input_length = sizeof(input) - 1; std::size_t output_length = ::_olm_encode_base64_length(input_length); assert_equals(std::size_t(15), output_length); std::uint8_t output[output_length]; output_length = ::_olm_encode_base64(input, input_length, output); assert_equals(std::size_t(15), output_length); assert_equals(expected_output, output, output_length); } { /* Base64 decode test */ TestCase test_case("Base64 C++ binding decode test"); std::uint8_t input[] = "SGVsbG8gV29ybGQ"; std::uint8_t expected_output[] = "Hello World"; std::size_t input_length = sizeof(input) - 1; std::size_t output_length = olm::decode_base64_length(input_length); assert_equals(std::size_t(11), output_length); std::uint8_t output[output_length]; olm::decode_base64(input, input_length, output); assert_equals(expected_output, output, output_length); } { TestCase test_case("Base64 C binding decode test"); std::uint8_t input[] = "SGVsbG8gV29ybGQ"; std::uint8_t expected_output[] = "Hello World"; std::size_t input_length = sizeof(input) - 1; std::size_t output_length = ::_olm_decode_base64_length(input_length); assert_equals(std::size_t(11), output_length); std::uint8_t output[output_length]; output_length = ::_olm_decode_base64(input, input_length, output); assert_equals(std::size_t(11), output_length); assert_equals(expected_output, output, output_length); } } olm-2.2.2+git20170526.0fd768e+dfsg/tests/test_crypto.cpp000066400000000000000000000153441311755073500221560ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "olm/crypto.h" #include "unittest.hh" int main() { { /* Curve25529 Test Case 1 */ TestCase test_case("Curve25529 Test Case 1"); std::uint8_t alice_private[32] = { 0x77, 0x07, 0x6D, 0x0A, 0x73, 0x18, 0xA5, 0x7D, 0x3C, 0x16, 0xC1, 0x72, 0x51, 0xB2, 0x66, 0x45, 0xDF, 0x4C, 0x2F, 0x87, 0xEB, 0xC0, 0x99, 0x2A, 0xB1, 0x77, 0xFB, 0xA5, 0x1D, 0xB9, 0x2C, 0x2A }; std::uint8_t alice_public[32] = { 0x85, 0x20, 0xF0, 0x09, 0x89, 0x30, 0xA7, 0x54, 0x74, 0x8B, 0x7D, 0xDC, 0xB4, 0x3E, 0xF7, 0x5A, 0x0D, 0xBF, 0x3A, 0x0D, 0x26, 0x38, 0x1A, 0xF4, 0xEB, 0xA4, 0xA9, 0x8E, 0xAA, 0x9B, 0x4E, 0x6A }; std::uint8_t bob_private[32] = { 0x5D, 0xAB, 0x08, 0x7E, 0x62, 0x4A, 0x8A, 0x4B, 0x79, 0xE1, 0x7F, 0x8B, 0x83, 0x80, 0x0E, 0xE6, 0x6F, 0x3B, 0xB1, 0x29, 0x26, 0x18, 0xB6, 0xFD, 0x1C, 0x2F, 0x8B, 0x27, 0xFF, 0x88, 0xE0, 0xEB }; std::uint8_t bob_public[32] = { 0xDE, 0x9E, 0xDB, 0x7D, 0x7B, 0x7D, 0xC1, 0xB4, 0xD3, 0x5B, 0x61, 0xC2, 0xEC, 0xE4, 0x35, 0x37, 0x3F, 0x83, 0x43, 0xC8, 0x5B, 0x78, 0x67, 0x4D, 0xAD, 0xFC, 0x7E, 0x14, 0x6F, 0x88, 0x2B, 0x4F }; std::uint8_t expected_agreement[32] = { 0x4A, 0x5D, 0x9D, 0x5B, 0xA4, 0xCE, 0x2D, 0xE1, 0x72, 0x8E, 0x3B, 0xF4, 0x80, 0x35, 0x0F, 0x25, 0xE0, 0x7E, 0x21, 0xC9, 0x47, 0xD1, 0x9E, 0x33, 0x76, 0xF0, 0x9B, 0x3C, 0x1E, 0x16, 0x17, 0x42 }; _olm_curve25519_key_pair alice_pair; _olm_crypto_curve25519_generate_key(alice_private, &alice_pair); assert_equals(alice_private, alice_pair.private_key.private_key, 32); assert_equals(alice_public, alice_pair.public_key.public_key, 32); _olm_curve25519_key_pair bob_pair; _olm_crypto_curve25519_generate_key(bob_private, &bob_pair); assert_equals(bob_private, bob_pair.private_key.private_key, 32); assert_equals(bob_public, bob_pair.public_key.public_key, 32); std::uint8_t actual_agreement[CURVE25519_SHARED_SECRET_LENGTH] = {}; _olm_crypto_curve25519_shared_secret(&alice_pair, &bob_pair.public_key, actual_agreement); assert_equals(expected_agreement, actual_agreement, 32); _olm_crypto_curve25519_shared_secret(&bob_pair, &alice_pair.public_key, actual_agreement); assert_equals(expected_agreement, actual_agreement, 32); } /* Curve25529 Test Case 1 */ { TestCase test_case("Ed25519 Signature Test Case 1"); std::uint8_t private_key[33] = "This key is a string of 32 bytes"; std::uint8_t message[] = "Hello, World"; std::size_t message_length = sizeof(message) - 1; _olm_ed25519_key_pair key_pair; _olm_crypto_ed25519_generate_key(private_key, &key_pair); std::uint8_t signature[64]; _olm_crypto_ed25519_sign( &key_pair, message, message_length, signature ); bool result = _olm_crypto_ed25519_verify( &key_pair.public_key, message, message_length, signature ); assert_equals(true, result); message[0] = 'n'; result = _olm_crypto_ed25519_verify( &key_pair.public_key, message, message_length, signature ); assert_equals(false, result); } { /* AES Test Case 1 */ TestCase test_case("AES Test Case 1"); _olm_aes256_key key = {}; _olm_aes256_iv iv = {}; std::uint8_t input[16] = {}; std::uint8_t expected[32] = { 0xDC, 0x95, 0xC0, 0x78, 0xA2, 0x40, 0x89, 0x89, 0xAD, 0x48, 0xA2, 0x14, 0x92, 0x84, 0x20, 0x87, 0xF3, 0xC0, 0x03, 0xDD, 0xC4, 0xA7, 0xB8, 0xA9, 0x4B, 0xAE, 0xDF, 0xFC, 0x3D, 0x21, 0x4C, 0x38 }; std::size_t length = _olm_crypto_aes_encrypt_cbc_length(sizeof(input)); assert_equals(std::size_t(32), length); std::uint8_t actual[32] = {}; _olm_crypto_aes_encrypt_cbc(&key, &iv, input, sizeof(input), actual); assert_equals(expected, actual, 32); length = _olm_crypto_aes_decrypt_cbc(&key, &iv, expected, sizeof(expected), actual); assert_equals(std::size_t(16), length); assert_equals(input, actual, length); } /* AES Test Case 1 */ { /* SHA 256 Test Case 1 */ TestCase test_case("SHA 256 Test Case 1"); std::uint8_t input[0] = {}; std::uint8_t expected[32] = { 0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, 0x1C, 0x14, 0x9A, 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24, 0x27, 0xAE, 0x41, 0xE4, 0x64, 0x9B, 0x93, 0x4C, 0xA4, 0x95, 0x99, 0x1B, 0x78, 0x52, 0xB8, 0x55 }; std::uint8_t actual[32]; _olm_crypto_sha256(input, sizeof(input), actual); assert_equals(expected, actual, 32); } /* SHA 256 Test Case 1 */ { /* HMAC Test Case 1 */ TestCase test_case("HMAC Test Case 1"); std::uint8_t input[0] = {}; std::uint8_t expected[32] = { 0xb6, 0x13, 0x67, 0x9a, 0x08, 0x14, 0xd9, 0xec, 0x77, 0x2f, 0x95, 0xd7, 0x78, 0xc3, 0x5f, 0xc5, 0xff, 0x16, 0x97, 0xc4, 0x93, 0x71, 0x56, 0x53, 0xc6, 0xc7, 0x12, 0x14, 0x42, 0x92, 0xc5, 0xad }; std::uint8_t actual[32]; _olm_crypto_hmac_sha256(input, sizeof(input), input, sizeof(input), actual); assert_equals(expected, actual, 32); } /* HMAC Test Case 1 */ { /* HDKF Test Case 1 */ TestCase test_case("HDKF Test Case 1"); std::uint8_t input[22] = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b }; std::uint8_t salt[13] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c }; std::uint8_t info[10] = { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9 }; std::uint8_t hmac_expected_output[32] = { 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31, 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5, }; std::uint8_t hmac_actual_output[32] = {}; _olm_crypto_hmac_sha256( salt, sizeof(salt), input, sizeof(input), hmac_actual_output ); assert_equals(hmac_expected_output, hmac_actual_output, 32); std::uint8_t hkdf_expected_output[42] = { 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65 }; std::uint8_t hkdf_actual_output[42] = {}; _olm_crypto_hkdf_sha256( input, sizeof(input), salt, sizeof(salt), info, sizeof(info), hkdf_actual_output, sizeof(hkdf_actual_output) ); assert_equals(hkdf_expected_output, hkdf_actual_output, 42); } /* HDKF Test Case 1 */ } olm-2.2.2+git20170526.0fd768e+dfsg/tests/test_group_session.cpp000066400000000000000000000270671311755073500235420ustar00rootroot00000000000000/* 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. */ #include "olm/inbound_group_session.h" #include "olm/outbound_group_session.h" #include "unittest.hh" int main() { { TestCase test_case("Pickle outbound group session"); size_t size = olm_outbound_group_session_size(); uint8_t memory[size]; OlmOutboundGroupSession *session = olm_outbound_group_session(memory); size_t pickle_length = olm_pickle_outbound_group_session_length(session); uint8_t pickle1[pickle_length]; size_t res = olm_pickle_outbound_group_session( session, "secret_key", 10, pickle1, pickle_length ); assert_equals(pickle_length, res); uint8_t pickle2[pickle_length]; memcpy(pickle2, pickle1, pickle_length); uint8_t buffer2[size]; OlmOutboundGroupSession *session2 = olm_outbound_group_session(buffer2); res = olm_unpickle_outbound_group_session( session2, "secret_key", 10, pickle2, pickle_length ); assert_not_equals((size_t)-1, res); assert_equals(pickle_length, olm_pickle_outbound_group_session_length(session2)); res = olm_pickle_outbound_group_session( session2, "secret_key", 10, pickle2, pickle_length ); assert_equals(pickle_length, res); assert_equals(pickle1, pickle2, pickle_length); } { TestCase test_case("Pickle inbound group session"); size_t size = olm_inbound_group_session_size(); uint8_t memory[size]; OlmInboundGroupSession *session = olm_inbound_group_session(memory); size_t pickle_length = olm_pickle_inbound_group_session_length(session); uint8_t pickle1[pickle_length]; size_t res = olm_pickle_inbound_group_session( session, "secret_key", 10, pickle1, pickle_length ); assert_equals(pickle_length, res); uint8_t pickle2[pickle_length]; memcpy(pickle2, pickle1, pickle_length); uint8_t buffer2[size]; OlmInboundGroupSession *session2 = olm_inbound_group_session(buffer2); res = olm_unpickle_inbound_group_session( session2, "secret_key", 10, pickle2, pickle_length ); assert_not_equals((size_t)-1, res); assert_equals(pickle_length, olm_pickle_inbound_group_session_length(session2)); res = olm_pickle_inbound_group_session( session2, "secret_key", 10, pickle2, pickle_length ); assert_equals(pickle1, pickle2, pickle_length); } { TestCase test_case("Group message send/receive"); uint8_t random_bytes[] = "0123456789ABDEF0123456789ABCDEF" "0123456789ABDEF0123456789ABCDEF" "0123456789ABDEF0123456789ABCDEF" "0123456789ABDEF0123456789ABCDEF" "0123456789ABDEF0123456789ABCDEF" "0123456789ABDEF0123456789ABCDEF"; /* build the outbound session */ size_t size = olm_outbound_group_session_size(); uint8_t memory[size]; OlmOutboundGroupSession *session = olm_outbound_group_session(memory); assert_equals((size_t)160, olm_init_outbound_group_session_random_length(session)); size_t res = olm_init_outbound_group_session( session, random_bytes, sizeof(random_bytes)); assert_equals((size_t)0, res); assert_equals(0U, olm_outbound_group_session_message_index(session)); size_t session_key_len = olm_outbound_group_session_key_length(session); uint8_t session_key[session_key_len]; olm_outbound_group_session_key(session, session_key, session_key_len); /* encode the message */ uint8_t plaintext[] = "Message"; size_t plaintext_length = sizeof(plaintext) - 1; size_t msglen = olm_group_encrypt_message_length( session, plaintext_length); uint8_t msg[msglen]; res = olm_group_encrypt(session, plaintext, plaintext_length, msg, msglen); assert_equals(msglen, res); assert_equals(1U, olm_outbound_group_session_message_index(session)); /* build the inbound session */ size = olm_inbound_group_session_size(); uint8_t inbound_session_memory[size]; OlmInboundGroupSession *inbound_session = olm_inbound_group_session(inbound_session_memory); assert_equals(0, olm_inbound_group_session_is_verified(inbound_session)); res = olm_init_inbound_group_session( inbound_session, session_key, session_key_len); assert_equals((size_t)0, res); assert_equals(1, olm_inbound_group_session_is_verified(inbound_session)); /* Check the session ids */ size_t out_session_id_len = olm_outbound_group_session_id_length(session); uint8_t out_session_id[out_session_id_len]; assert_equals(out_session_id_len, olm_outbound_group_session_id( session, out_session_id, out_session_id_len )); size_t in_session_id_len = olm_inbound_group_session_id_length( inbound_session ); uint8_t in_session_id[in_session_id_len]; assert_equals(in_session_id_len, olm_inbound_group_session_id( inbound_session, in_session_id, in_session_id_len )); assert_equals(in_session_id_len, out_session_id_len); assert_equals(out_session_id, in_session_id, in_session_id_len); /* decode the message */ /* olm_group_decrypt_max_plaintext_length destroys the input so we have to copy it. */ uint8_t msgcopy[msglen]; memcpy(msgcopy, msg, msglen); size = olm_group_decrypt_max_plaintext_length(inbound_session, msgcopy, msglen); uint8_t plaintext_buf[size]; uint32_t message_index; res = olm_group_decrypt(inbound_session, msg, msglen, plaintext_buf, size, &message_index); assert_equals(plaintext_length, res); assert_equals(plaintext, plaintext_buf, res); assert_equals(message_index, uint32_t(0)); } { TestCase test_case("Inbound group session export/import"); uint8_t session_key[] = "AgAAAAAwMTIzNDU2Nzg5QUJERUYwMTIzNDU2Nzg5QUJDREVGMDEyMzQ1Njc4OUFCREVGM" "DEyMzQ1Njc4OUFCQ0RFRjAxMjM0NTY3ODlBQkRFRjAxMjM0NTY3ODlBQkNERUYwMTIzND" "U2Nzg5QUJERUYwMTIzNDU2Nzg5QUJDREVGMDEyMw0bdg1BDq4Px/slBow06q8n/B9WBfw" "WYyNOB8DlUmXGGwrFmaSb9bR/eY8xgERrxmP07hFmD9uqA2p8PMHdnV5ysmgufE6oLZ5+" "8/mWQOW3VVTnDIlnwd8oHUYRuk8TCQ"; const uint8_t message[] = "AwgAEhAcbh6UpbByoyZxufQ+h2B+8XHMjhR69G8F4+qjMaFlnIXusJZX3r8LnRORG9T3D" "XFdbVuvIWrLyRfm4i8QRbe8VPwGRFG57B1CtmxanuP8bHtnnYqlwPsD"; const std::size_t msglen = sizeof(message)-1; /* init first inbound group session, and decrypt */ std::size_t size = olm_inbound_group_session_size(); uint8_t session_memory1[size]; OlmInboundGroupSession *session1 = olm_inbound_group_session(session_memory1); assert_equals(0, olm_inbound_group_session_is_verified(session1)); std::size_t res = olm_init_inbound_group_session( session1, session_key, sizeof(session_key)-1 ); assert_equals((size_t)0, res); assert_equals(1, olm_inbound_group_session_is_verified(session1)); /* olm_group_decrypt_max_plaintext_length destroys the input so we have to copy it. */ uint8_t msgcopy[msglen]; memcpy(msgcopy, message, msglen); size = olm_group_decrypt_max_plaintext_length(session1, msgcopy, msglen); uint8_t plaintext_buf[size]; uint32_t message_index; memcpy(msgcopy, message, msglen); res = olm_group_decrypt( session1, msgcopy, msglen, plaintext_buf, size, &message_index ); assert_equals((std::size_t)7, res); assert_equals((const uint8_t *)"Message", plaintext_buf, res); assert_equals(uint32_t(0), message_index); /* export the keys */ size = olm_export_inbound_group_session_length(session1); uint8_t export_memory[size]; res = olm_export_inbound_group_session( session1, export_memory, size, 0 ); assert_equals(size, res); /* free the old session to check there is no shared data */ olm_clear_inbound_group_session(session1); /* import the keys into another inbound group session */ size = olm_inbound_group_session_size(); uint8_t session_memory2[size]; OlmInboundGroupSession *session2 = olm_inbound_group_session(session_memory2); res = olm_import_inbound_group_session( session2, export_memory, sizeof(export_memory) ); assert_equals((size_t)0, res); assert_equals(0, olm_inbound_group_session_is_verified(session2)); /* decrypt the message with the new session */ memcpy(msgcopy, message, msglen); size = olm_group_decrypt_max_plaintext_length(session2, msgcopy, msglen); uint8_t plaintext_buf2[size]; memcpy(msgcopy, message, msglen); res = olm_group_decrypt( session2, msgcopy, msglen, plaintext_buf2, size, &message_index ); assert_equals((std::size_t)7, res); assert_equals((const uint8_t *)"Message", plaintext_buf2, res); assert_equals(uint32_t(0), message_index); assert_equals(1, olm_inbound_group_session_is_verified(session2)); } { TestCase test_case("Invalid signature group message"); uint8_t plaintext[] = "Message"; size_t plaintext_length = sizeof(plaintext) - 1; uint8_t session_key[] = "AgAAAAAwMTIzNDU2Nzg5QUJERUYwMTIzNDU2Nzg5QUJDREVGMDEyMzQ1Njc4OUFCREVGM" "DEyMzQ1Njc4OUFCQ0RFRjAxMjM0NTY3ODlBQkRFRjAxMjM0NTY3ODlBQkNERUYwMTIzND" "U2Nzg5QUJERUYwMTIzNDU2Nzg5QUJDREVGMDEyMztqJ7zOtqQtYqOo0CpvDXNlMhV3HeJ" "DpjrASKGLWdop4lx1cSN3Xv1TgfLPW8rhGiW+hHiMxd36nRuxscNv9k4oJA/KP+o0mi1w" "v44StrEJ1wwx9WZHBUIWkQbaBSuBDw"; uint8_t message[] = "AwgAEhAcbh6UpbByoyZxufQ+h2B+8XHMjhR69G8nP4pNZGl/3QMgrzCZPmP+F2aPLyKPz" "xRPBMUkeXRJ6Iqm5NeOdx2eERgTW7P20CM+lL3Xpk+ZUOOPvsSQNaAL"; size_t msglen = sizeof(message)-1; /* build the inbound session */ size_t size = olm_inbound_group_session_size(); uint8_t inbound_session_memory[size]; OlmInboundGroupSession *inbound_session = olm_inbound_group_session(inbound_session_memory); size_t res = olm_init_inbound_group_session( inbound_session, session_key, sizeof(session_key)-1 ); assert_equals((size_t)0, res); /* decode the message */ /* olm_group_decrypt_max_plaintext_length destroys the input so we have to copy it. */ uint8_t msgcopy[msglen]; memcpy(msgcopy, message, msglen); size = olm_group_decrypt_max_plaintext_length( inbound_session, msgcopy, msglen ); memcpy(msgcopy, message, msglen); uint8_t plaintext_buf[size]; uint32_t message_index; res = olm_group_decrypt( inbound_session, msgcopy, msglen, plaintext_buf, size, &message_index ); assert_equals(message_index, uint32_t(0)); assert_equals(plaintext_length, res); assert_equals(plaintext, plaintext_buf, res); /* now twiddle the signature */ message[msglen-1] = 'E'; memcpy(msgcopy, message, msglen); assert_equals( size, olm_group_decrypt_max_plaintext_length( inbound_session, msgcopy, msglen ) ); memcpy(msgcopy, message, msglen); res = olm_group_decrypt( inbound_session, msgcopy, msglen, plaintext_buf, size, &message_index ); assert_equals((size_t)-1, res); assert_equals( std::string("BAD_SIGNATURE"), std::string(olm_inbound_group_session_last_error(inbound_session)) ); } } olm-2.2.2+git20170526.0fd768e+dfsg/tests/test_list.cpp000066400000000000000000000036701311755073500216100ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "olm/list.hh" #include "unittest.hh" int main() { { /** List insert test **/ TestCase test_case("List insert"); olm::List test_list; assert_equals(std::size_t(0), test_list.size()); for (int i = 0; i < 4; ++i) { test_list.insert(test_list.end(), i); } assert_equals(std::size_t(4), test_list.size()); int i = 0; for (auto item : test_list) { assert_equals(i++, item); } assert_equals(4, i); test_list.insert(test_list.end(), 4); assert_equals(4, test_list[3]); } /** List insert test **/ { /** List insert beginning test **/ TestCase test_case("List insert beginning"); olm::List test_list; assert_equals(std::size_t(0), test_list.size()); for (int i = 0; i < 4; ++i) { test_list.insert(test_list.begin(), i); } assert_equals(std::size_t(4), test_list.size()); int i = 4; for (auto item : test_list) { assert_equals(--i, item); } } /** List insert test **/ { /** List erase test **/ TestCase test_case("List erase"); olm::List test_list; assert_equals(std::size_t(0), test_list.size()); for (int i = 0; i < 4; ++i) { test_list.insert(test_list.end(), i); } assert_equals(std::size_t(4), test_list.size()); test_list.erase(test_list.begin()); assert_equals(std::size_t(3), test_list.size()); int i = 0; for (auto item : test_list) { assert_equals(i + 1, item); ++i; } assert_equals(3, i); } } olm-2.2.2+git20170526.0fd768e+dfsg/tests/test_megolm.cpp000066400000000000000000000127351311755073500221170ustar00rootroot00000000000000/* 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. */ #include "olm/megolm.h" #include "olm/memory.hh" #include "unittest.hh" int main() { std::uint8_t random_bytes[] = "0123456789ABCDEF0123456789ABCDEF" "0123456789ABCDEF0123456789ABCDEF" "0123456789ABCDEF0123456789ABCDEF" "0123456789ABCDEF0123456789ABCDEF"; { TestCase test_case("Megolm::advance"); Megolm mr; megolm_init(&mr, random_bytes, 0); // single-step advance megolm_advance(&mr); const std::uint8_t expected1[] = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0xba, 0x9c, 0xd9, 0x55, 0x74, 0x1d, 0x1c, 0x16, 0x23, 0x23, 0xec, 0x82, 0x5e, 0x7c, 0x5c, 0xe8, 0x89, 0xbb, 0xb4, 0x23, 0xa1, 0x8f, 0x23, 0x82, 0x8f, 0xb2, 0x09, 0x0d, 0x6e, 0x2a, 0xf8, 0x6a }; assert_equals(1U, mr.counter); assert_equals(expected1, megolm_get_data(&mr), MEGOLM_RATCHET_LENGTH); // repeat with complex advance megolm_init(&mr, random_bytes, 0); megolm_advance_to(&mr, 1); assert_equals(1U, mr.counter); assert_equals(expected1, megolm_get_data(&mr), MEGOLM_RATCHET_LENGTH); megolm_advance_to(&mr, 0x1000000); const std::uint8_t expected2[] = { 0x54, 0x02, 0x2d, 0x7d, 0xc0, 0x29, 0x8e, 0x16, 0x37, 0xe2, 0x1c, 0x97, 0x15, 0x30, 0x92, 0xf9, 0x33, 0xc0, 0x56, 0xff, 0x74, 0xfe, 0x1b, 0x92, 0x2d, 0x97, 0x1f, 0x24, 0x82, 0xc2, 0x85, 0x9c, 0x70, 0x04, 0xc0, 0x1e, 0xe4, 0x9b, 0xd6, 0xef, 0xe0, 0x07, 0x35, 0x25, 0xaf, 0x9b, 0x16, 0x32, 0xc5, 0xbe, 0x72, 0x6d, 0x12, 0x34, 0x9c, 0xc5, 0xbd, 0x47, 0x2b, 0xdc, 0x2d, 0xf6, 0x54, 0x0f, 0x31, 0x12, 0x59, 0x11, 0x94, 0xfd, 0xa6, 0x17, 0xe5, 0x68, 0xc6, 0x83, 0x10, 0x1e, 0xae, 0xcd, 0x7e, 0xdd, 0xd6, 0xde, 0x1f, 0xbc, 0x07, 0x67, 0xae, 0x34, 0xda, 0x1a, 0x09, 0xa5, 0x4e, 0xab, 0xba, 0x9c, 0xd9, 0x55, 0x74, 0x1d, 0x1c, 0x16, 0x23, 0x23, 0xec, 0x82, 0x5e, 0x7c, 0x5c, 0xe8, 0x89, 0xbb, 0xb4, 0x23, 0xa1, 0x8f, 0x23, 0x82, 0x8f, 0xb2, 0x09, 0x0d, 0x6e, 0x2a, 0xf8, 0x6a, }; assert_equals(0x1000000U, mr.counter); assert_equals(expected2, megolm_get_data(&mr), MEGOLM_RATCHET_LENGTH); megolm_advance_to(&mr, 0x1041506); const std::uint8_t expected3[] = { 0x54, 0x02, 0x2d, 0x7d, 0xc0, 0x29, 0x8e, 0x16, 0x37, 0xe2, 0x1c, 0x97, 0x15, 0x30, 0x92, 0xf9, 0x33, 0xc0, 0x56, 0xff, 0x74, 0xfe, 0x1b, 0x92, 0x2d, 0x97, 0x1f, 0x24, 0x82, 0xc2, 0x85, 0x9c, 0x55, 0x58, 0x8d, 0xf5, 0xb7, 0xa4, 0x88, 0x78, 0x42, 0x89, 0x27, 0x86, 0x81, 0x64, 0x58, 0x9f, 0x36, 0x63, 0x44, 0x7b, 0x51, 0xed, 0xc3, 0x59, 0x5b, 0x03, 0x6c, 0xa6, 0x04, 0xc4, 0x6d, 0xcd, 0x5c, 0x54, 0x85, 0x0b, 0xfa, 0x98, 0xa1, 0xfd, 0x79, 0xa9, 0xdf, 0x1c, 0xbe, 0x8f, 0xc5, 0x68, 0x19, 0x37, 0xd3, 0x0c, 0x85, 0xc8, 0xc3, 0x1f, 0x7b, 0xb8, 0x28, 0x81, 0x6c, 0xf9, 0xff, 0x3b, 0x95, 0x6c, 0xbf, 0x80, 0x7e, 0x65, 0x12, 0x6a, 0x49, 0x55, 0x8d, 0x45, 0xc8, 0x4a, 0x2e, 0x4c, 0xd5, 0x6f, 0x03, 0xe2, 0x44, 0x16, 0xb9, 0x8e, 0x1c, 0xfd, 0x97, 0xc2, 0x06, 0xaa, 0x90, 0x7a }; assert_equals(0x1041506U, mr.counter); assert_equals(expected3, megolm_get_data(&mr), MEGOLM_RATCHET_LENGTH); } { TestCase test_case("Megolm::advance wraparound"); Megolm mr1, mr2; megolm_init(&mr1, random_bytes, 0xffffffffUL); megolm_advance_to(&mr1, 0x1000000); assert_equals(0x1000000U, mr1.counter); megolm_init(&mr2, random_bytes, 0); megolm_advance_to(&mr2, 0x2000000); assert_equals(0x2000000U, mr2.counter); assert_equals(megolm_get_data(&mr2), megolm_get_data(&mr1), MEGOLM_RATCHET_LENGTH); } { TestCase test_case("Megolm::advance overflow by one"); Megolm mr1, mr2; megolm_init(&mr1, random_bytes, 0xffffffffUL); megolm_advance_to(&mr1, 0x0); assert_equals(0x0U, mr1.counter); megolm_init(&mr2, random_bytes, 0xffffffffUL); megolm_advance(&mr2); assert_equals(0x0U, mr2.counter); assert_equals(megolm_get_data(&mr2), megolm_get_data(&mr1), MEGOLM_RATCHET_LENGTH); } { TestCase test_case("Megolm::advance overflow"); Megolm mr1, mr2; megolm_init(&mr1, random_bytes, 0x1UL); megolm_advance_to(&mr1, 0x80000000UL); megolm_advance_to(&mr1, 0x0); assert_equals(0x0U, mr1.counter); megolm_init(&mr2, random_bytes, 0x1UL); megolm_advance_to(&mr2, 0x0UL); assert_equals(0x0U, mr2.counter); assert_equals(megolm_get_data(&mr2), megolm_get_data(&mr1), MEGOLM_RATCHET_LENGTH); } } olm-2.2.2+git20170526.0fd768e+dfsg/tests/test_message.cpp000066400000000000000000000064071311755073500222620ustar00rootroot00000000000000/* Copyright 2015-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. */ #include "olm/message.hh" #include "unittest.hh" int main() { std::uint8_t message1[36] = "\x03\x10\x01\n\nratchetkey\"\nciphertexthmacsha2"; std::uint8_t message2[36] = "\x03\n\nratchetkey\x10\x01\"\nciphertexthmacsha2"; std::uint8_t ratchetkey[11] = "ratchetkey"; std::uint8_t ciphertext[11] = "ciphertext"; std::uint8_t hmacsha2[9] = "hmacsha2"; { /* Message decode test */ TestCase test_case("Message decode test"); olm::MessageReader reader; olm::decode_message(reader, message1, 35, 8); assert_equals(std::uint8_t(3), reader.version); assert_equals(true, reader.has_counter); assert_equals(std::uint32_t(1), reader.counter); assert_equals(std::size_t(10), reader.ratchet_key_length); assert_equals(std::size_t(10), reader.ciphertext_length); assert_equals(ratchetkey, reader.ratchet_key, 10); assert_equals(ciphertext, reader.ciphertext, 10); } /* Message decode test */ { /* Message encode test */ TestCase test_case("Message encode test"); std::size_t length = olm::encode_message_length(1, 10, 10, 8); assert_equals(std::size_t(35), length); std::uint8_t output[length]; olm::MessageWriter writer; olm::encode_message(writer, 3, 1, 10, 10, output); std::memcpy(writer.ratchet_key, ratchetkey, 10); std::memcpy(writer.ciphertext, ciphertext, 10); std::memcpy(output + length - 8, hmacsha2, 8); assert_equals(message2, output, 35); } /* Message encode test */ { /* group message encode test */ TestCase test_case("Group message encode test"); size_t length = _olm_encode_group_message_length(200, 10, 8, 64); size_t expected_length = 1 + (1+2) + (2+10) + 8 + 64; assert_equals(expected_length, length); uint8_t output[50]; uint8_t *ciphertext_ptr; _olm_encode_group_message( 3, 200, // counter 10, // ciphertext length output, &ciphertext_ptr ); uint8_t expected[] = "\x03" "\x08\xC8\x01" "\x12\x0A"; assert_equals(expected, output, sizeof(expected)-1); assert_equals(output+sizeof(expected)-1, ciphertext_ptr); } /* group message encode test */ { TestCase test_case("Group message decode test"); struct _OlmDecodeGroupMessageResults results; std::uint8_t message[] = "\x03" "\x08\xC8\x01" "\x12\x0A" "ciphertext" "hmacsha2" "ed25519signature"; _olm_decode_group_message(message, sizeof(message)-1, 8, 16, &results); assert_equals(std::uint8_t(3), results.version); assert_equals(1, results.has_message_index); assert_equals(std::uint32_t(200), results.message_index); assert_equals(std::size_t(10), results.ciphertext_length); assert_equals(ciphertext, results.ciphertext, 10); } /* group message decode test */ } olm-2.2.2+git20170526.0fd768e+dfsg/tests/test_olm.cpp000066400000000000000000000342611311755073500214240ustar00rootroot00000000000000#include "olm/olm.h" #include "unittest.hh" #include #include #include struct MockRandom { MockRandom(std::uint8_t tag, std::uint8_t offset = 0) : tag(tag), current(offset) {} void operator()( std::uint8_t * bytes, std::size_t length ) { while (length > 32) { bytes[0] = tag; std::memset(bytes + 1, current, 31); length -= 32; bytes += 32; current += 1; } if (length) { bytes[0] = tag; std::memset(bytes + 1, current, length - 1); current += 1; } } std::uint8_t tag; std::uint8_t current; }; int main() { { /** Pickle account test */ TestCase test_case("Pickle account test"); MockRandom mock_random('P'); std::uint8_t account_buffer[::olm_account_size()]; ::OlmAccount *account = ::olm_account(account_buffer); std::uint8_t random[::olm_create_account_random_length(account)]; mock_random(random, sizeof(random)); ::olm_create_account(account, random, sizeof(random)); std::uint8_t ot_random[::olm_account_generate_one_time_keys_random_length( account, 42 )]; mock_random(ot_random, sizeof(ot_random)); ::olm_account_generate_one_time_keys(account, 42, ot_random, sizeof(ot_random)); std::size_t pickle_length = ::olm_pickle_account_length(account); std::uint8_t pickle1[pickle_length]; std::size_t res = ::olm_pickle_account(account, "secret_key", 10, pickle1, pickle_length); assert_equals(pickle_length, res); std::uint8_t pickle2[pickle_length]; std::memcpy(pickle2, pickle1, pickle_length); std::uint8_t account_buffer2[::olm_account_size()]; ::OlmAccount *account2 = ::olm_account(account_buffer2); assert_not_equals(std::size_t(-1), ::olm_unpickle_account( account2, "secret_key", 10, pickle2, pickle_length )); assert_equals(pickle_length, ::olm_pickle_account_length(account2)); res = ::olm_pickle_account(account2, "secret_key", 10, pickle2, pickle_length); assert_equals(pickle_length, res); assert_equals(pickle1, pickle2, pickle_length); } { TestCase test_case("Old account unpickle test"); // this uses the old pickle format, which did not use enough space // for the Ed25519 key. We should reject it. std::uint8_t pickle[] = "x3h9er86ygvq56pM1yesdAxZou4ResPQC9Rszk/fhEL9JY/umtZ2N/foL/SUgVXS" "v0IxHHZTafYjDdzJU9xr8dQeBoOTGfV9E/lCqDGBnIlu7SZndqjEKXtzGyQr4sP4" "K/A/8TOu9iK2hDFszy6xETiousHnHgh2ZGbRUh4pQx+YMm8ZdNZeRnwFGLnrWyf9" "O5TmXua1FcU"; std::uint8_t account_buffer[::olm_account_size()]; ::OlmAccount *account = ::olm_account(account_buffer); assert_equals( std::size_t(-1), ::olm_unpickle_account( account, "", 0, pickle, sizeof(pickle)-1 ) ); assert_equals( std::string("BAD_LEGACY_ACCOUNT_PICKLE"), std::string(::olm_account_last_error(account)) ); } { /** Pickle session test */ TestCase test_case("Pickle session test"); MockRandom mock_random('P'); std::uint8_t account_buffer[::olm_account_size()]; ::OlmAccount *account = ::olm_account(account_buffer); std::uint8_t random[::olm_create_account_random_length(account)]; mock_random(random, sizeof(random)); ::olm_create_account(account, random, sizeof(random)); std::uint8_t session_buffer[::olm_session_size()]; ::OlmSession *session = ::olm_session(session_buffer); std::uint8_t identity_key[32]; std::uint8_t one_time_key[32]; mock_random(identity_key, sizeof(identity_key)); mock_random(one_time_key, sizeof(one_time_key)); std::uint8_t random2[::olm_create_outbound_session_random_length(session)]; mock_random(random2, sizeof(random2)); ::olm_create_outbound_session( session, account, identity_key, sizeof(identity_key), one_time_key, sizeof(one_time_key), random2, sizeof(random2) ); std::size_t pickle_length = ::olm_pickle_session_length(session); std::uint8_t pickle1[pickle_length]; std::size_t res = ::olm_pickle_session(session, "secret_key", 10, pickle1, pickle_length); assert_equals(pickle_length, res); std::uint8_t pickle2[pickle_length]; std::memcpy(pickle2, pickle1, pickle_length); std::uint8_t session_buffer2[::olm_session_size()]; ::OlmSession *session2 = ::olm_session(session_buffer2); assert_not_equals(std::size_t(-1), ::olm_unpickle_session( session2, "secret_key", 10, pickle2, pickle_length )); assert_equals(pickle_length, ::olm_pickle_session_length(session2)); res = ::olm_pickle_session(session2, "secret_key", 10, pickle2, pickle_length); assert_equals(pickle_length, res); assert_equals(pickle1, pickle2, pickle_length); } { /** Loopback test */ TestCase test_case("Loopback test"); MockRandom mock_random_a('A', 0x00); MockRandom mock_random_b('B', 0x80); std::uint8_t a_account_buffer[::olm_account_size()]; ::OlmAccount *a_account = ::olm_account(a_account_buffer); std::uint8_t a_random[::olm_create_account_random_length(a_account)]; mock_random_a(a_random, sizeof(a_random)); ::olm_create_account(a_account, a_random, sizeof(a_random)); std::uint8_t b_account_buffer[::olm_account_size()]; ::OlmAccount *b_account = ::olm_account(b_account_buffer); std::uint8_t b_random[::olm_create_account_random_length(b_account)]; mock_random_b(b_random, sizeof(b_random)); ::olm_create_account(b_account, b_random, sizeof(b_random)); std::uint8_t o_random[::olm_account_generate_one_time_keys_random_length( b_account, 42 )]; mock_random_b(o_random, sizeof(o_random)); ::olm_account_generate_one_time_keys(b_account, 42, o_random, sizeof(o_random)); std::uint8_t a_id_keys[::olm_account_identity_keys_length(a_account)]; ::olm_account_identity_keys(a_account, a_id_keys, sizeof(a_id_keys)); std::uint8_t b_id_keys[::olm_account_identity_keys_length(b_account)]; std::uint8_t b_ot_keys[::olm_account_one_time_keys_length(b_account)]; ::olm_account_identity_keys(b_account, b_id_keys, sizeof(b_id_keys)); ::olm_account_one_time_keys(b_account, b_ot_keys, sizeof(b_ot_keys)); std::uint8_t a_session_buffer[::olm_session_size()]; ::OlmSession *a_session = ::olm_session(a_session_buffer); std::uint8_t a_rand[::olm_create_outbound_session_random_length(a_session)]; mock_random_a(a_rand, sizeof(a_rand)); assert_not_equals(std::size_t(-1), ::olm_create_outbound_session( a_session, a_account, b_id_keys + 15, 43, // B's curve25519 identity key b_ot_keys + 25, 43, // B's curve25519 one time key a_rand, sizeof(a_rand) )); std::uint8_t plaintext[] = "Hello, World"; std::uint8_t message_1[::olm_encrypt_message_length(a_session, 12)]; std::uint8_t a_message_random[::olm_encrypt_random_length(a_session)]; mock_random_a(a_message_random, sizeof(a_message_random)); assert_equals(std::size_t(0), ::olm_encrypt_message_type(a_session)); assert_not_equals(std::size_t(-1), ::olm_encrypt( a_session, plaintext, 12, a_message_random, sizeof(a_message_random), message_1, sizeof(message_1) )); std::uint8_t tmp_message_1[sizeof(message_1)]; std::memcpy(tmp_message_1, message_1, sizeof(message_1)); std::uint8_t b_session_buffer[::olm_account_size()]; ::OlmSession *b_session = ::olm_session(b_session_buffer); ::olm_create_inbound_session( b_session, b_account, tmp_message_1, sizeof(message_1) ); // Check that the inbound session matches the message it was created from. std::memcpy(tmp_message_1, message_1, sizeof(message_1)); assert_equals(std::size_t(1), ::olm_matches_inbound_session( b_session, tmp_message_1, sizeof(message_1) )); // Check that the inbound session matches the key this message is supposed // to be from. std::memcpy(tmp_message_1, message_1, sizeof(message_1)); assert_equals(std::size_t(1), ::olm_matches_inbound_session_from( b_session, a_id_keys + 15, 43, // A's curve125519 identity key. tmp_message_1, sizeof(message_1) )); // Check that the inbound session isn't from a different user. std::memcpy(tmp_message_1, message_1, sizeof(message_1)); assert_equals(std::size_t(0), ::olm_matches_inbound_session_from( b_session, b_id_keys + 15, 43, // B's curve25519 identity key. tmp_message_1, sizeof(message_1) )); // Check that we can decrypt the message. std::memcpy(tmp_message_1, message_1, sizeof(message_1)); std::uint8_t plaintext_1[::olm_decrypt_max_plaintext_length( b_session, 0, tmp_message_1, sizeof(message_1) )]; std::memcpy(tmp_message_1, message_1, sizeof(message_1)); assert_equals(std::size_t(12), ::olm_decrypt( b_session, 0, tmp_message_1, sizeof(message_1), plaintext_1, sizeof(plaintext_1) )); assert_equals(plaintext, plaintext_1, 12); std::uint8_t message_2[::olm_encrypt_message_length(b_session, 12)]; std::uint8_t b_message_random[::olm_encrypt_random_length(b_session)]; mock_random_b(b_message_random, sizeof(b_message_random)); assert_equals(std::size_t(1), ::olm_encrypt_message_type(b_session)); assert_not_equals(std::size_t(-1), ::olm_encrypt( b_session, plaintext, 12, b_message_random, sizeof(b_message_random), message_2, sizeof(message_2) )); std::uint8_t tmp_message_2[sizeof(message_2)]; std::memcpy(tmp_message_2, message_2, sizeof(message_2)); std::uint8_t plaintext_2[::olm_decrypt_max_plaintext_length( a_session, 1, tmp_message_2, sizeof(message_2) )]; std::memcpy(tmp_message_2, message_2, sizeof(message_2)); assert_equals(std::size_t(12), ::olm_decrypt( a_session, 1, tmp_message_2, sizeof(message_2), plaintext_2, sizeof(plaintext_2) )); assert_equals(plaintext, plaintext_2, 12); std::memcpy(tmp_message_2, message_2, sizeof(message_2)); assert_equals(std::size_t(-1), ::olm_decrypt( a_session, 1, tmp_message_2, sizeof(message_2), plaintext_2, sizeof(plaintext_2) )); std::uint8_t a_session_id[::olm_session_id_length(a_session)]; assert_not_equals(std::size_t(-1), ::olm_session_id( a_session, a_session_id, sizeof(a_session_id) )); std::uint8_t b_session_id[::olm_session_id_length(b_session)]; assert_not_equals(std::size_t(-1), ::olm_session_id( b_session, b_session_id, sizeof(b_session_id) )); assert_equals(sizeof(a_session_id), sizeof(b_session_id)); assert_equals(a_session_id, b_session_id, sizeof(b_session_id)); } { /** More messages test */ TestCase test_case("More messages test"); MockRandom mock_random_a('A', 0x00); MockRandom mock_random_b('B', 0x80); std::uint8_t a_account_buffer[::olm_account_size()]; ::OlmAccount *a_account = ::olm_account(a_account_buffer); std::uint8_t a_random[::olm_create_account_random_length(a_account)]; mock_random_a(a_random, sizeof(a_random)); ::olm_create_account(a_account, a_random, sizeof(a_random)); std::uint8_t b_account_buffer[::olm_account_size()]; ::OlmAccount *b_account = ::olm_account(b_account_buffer); std::uint8_t b_random[::olm_create_account_random_length(b_account)]; mock_random_b(b_random, sizeof(b_random)); ::olm_create_account(b_account, b_random, sizeof(b_random)); std::uint8_t o_random[::olm_account_generate_one_time_keys_random_length( b_account, 42 )]; mock_random_b(o_random, sizeof(o_random)); ::olm_account_generate_one_time_keys(b_account, 42, o_random, sizeof(o_random)); std::uint8_t b_id_keys[::olm_account_identity_keys_length(b_account)]; std::uint8_t b_ot_keys[::olm_account_one_time_keys_length(b_account)]; ::olm_account_identity_keys(b_account, b_id_keys, sizeof(b_id_keys)); ::olm_account_one_time_keys(b_account, b_ot_keys, sizeof(b_ot_keys)); std::uint8_t a_session_buffer[::olm_session_size()]; ::OlmSession *a_session = ::olm_session(a_session_buffer); std::uint8_t a_rand[::olm_create_outbound_session_random_length(a_session)]; mock_random_a(a_rand, sizeof(a_rand)); assert_not_equals(std::size_t(-1), ::olm_create_outbound_session( a_session, a_account, b_id_keys + 15, 43, b_ot_keys + 25, 43, a_rand, sizeof(a_rand) )); std::uint8_t plaintext[] = "Hello, World"; std::uint8_t message_1[::olm_encrypt_message_length(a_session, 12)]; std::uint8_t a_message_random[::olm_encrypt_random_length(a_session)]; mock_random_a(a_message_random, sizeof(a_message_random)); assert_equals(std::size_t(0), ::olm_encrypt_message_type(a_session)); assert_not_equals(std::size_t(-1), ::olm_encrypt( a_session, plaintext, 12, a_message_random, sizeof(a_message_random), message_1, sizeof(message_1) )); std::uint8_t tmp_message_1[sizeof(message_1)]; std::memcpy(tmp_message_1, message_1, sizeof(message_1)); std::uint8_t b_session_buffer[::olm_account_size()]; ::OlmSession *b_session = ::olm_session(b_session_buffer); ::olm_create_inbound_session( b_session, b_account, tmp_message_1, sizeof(message_1) ); std::memcpy(tmp_message_1, message_1, sizeof(message_1)); std::uint8_t plaintext_1[::olm_decrypt_max_plaintext_length( b_session, 0, tmp_message_1, sizeof(message_1) )]; std::memcpy(tmp_message_1, message_1, sizeof(message_1)); assert_equals(std::size_t(12), ::olm_decrypt( b_session, 0, tmp_message_1, sizeof(message_1), plaintext_1, sizeof(plaintext_1) )); for (unsigned i = 0; i < 8; ++i) { { std::uint8_t msg_a[::olm_encrypt_message_length(a_session, 12)]; std::uint8_t rnd_a[::olm_encrypt_random_length(a_session)]; mock_random_a(rnd_a, sizeof(rnd_a)); std::size_t type_a = ::olm_encrypt_message_type(a_session); assert_not_equals(std::size_t(-1), ::olm_encrypt( a_session, plaintext, 12, rnd_a, sizeof(rnd_a), msg_a, sizeof(msg_a) )); std::uint8_t tmp_a[sizeof(msg_a)]; std::memcpy(tmp_a, msg_a, sizeof(msg_a)); std::uint8_t out_a[::olm_decrypt_max_plaintext_length( b_session, type_a, tmp_a, sizeof(tmp_a) )]; std::memcpy(tmp_a, msg_a, sizeof(msg_a)); assert_equals(std::size_t(12), ::olm_decrypt( b_session, type_a, msg_a, sizeof(msg_a), out_a, sizeof(out_a) )); } { std::uint8_t msg_b[::olm_encrypt_message_length(b_session, 12)]; std::uint8_t rnd_b[::olm_encrypt_random_length(b_session)]; mock_random_b(rnd_b, sizeof(rnd_b)); std::size_t type_b = ::olm_encrypt_message_type(b_session); assert_not_equals(std::size_t(-1), ::olm_encrypt( b_session, plaintext, 12, rnd_b, sizeof(rnd_b), msg_b, sizeof(msg_b) )); std::uint8_t tmp_b[sizeof(msg_b)]; std::memcpy(tmp_b, msg_b, sizeof(msg_b)); std::uint8_t out_b[::olm_decrypt_max_plaintext_length( a_session, type_b, tmp_b, sizeof(tmp_b) )]; std::memcpy(tmp_b, msg_b, sizeof(msg_b)); assert_equals(std::size_t(12), ::olm_decrypt( a_session, type_b, msg_b, sizeof(msg_b), out_b, sizeof(out_b) )); } } } } olm-2.2.2+git20170526.0fd768e+dfsg/tests/test_olm_decrypt.cpp000066400000000000000000000052021311755073500231470ustar00rootroot00000000000000#include "olm/olm.h" #include "unittest.hh" struct test_case { const char *msghex; const char *expected_error; }; const test_case test_cases[] = { { "41776f", "BAD_MESSAGE_FORMAT" }, { "7fff6f0101346d671201", "BAD_MESSAGE_FORMAT" }, { "ee776f41496f674177804177778041776f6716670a677d6f670a67c2677d", "BAD_MESSAGE_FORMAT" }, { "e9e9c9c1e9e9c9e9c9c1e9e9c9c1", "BAD_MESSAGE_FORMAT" }, }; const char * session_data = "E0p44KO2y2pzp9FIjv0rud2wIvWDi2dx367kP4Fz/9JCMrH+aG369HGymkFtk0+PINTLB9lQRt" "ohea5d7G/UXQx3r5y4IWuyh1xaRnojEZQ9a5HRZSNtvmZ9NY1f1gutYa4UtcZcbvczN8b/5Bqg" "e16cPUH1v62JKLlhoAJwRkH1wU6fbyOudERg5gdXA971btR+Q2V8GKbVbO5fGKL5phmEPVXyMs" "rfjLdzQrgjOTxN8Pf6iuP+WFPvfnR9lDmNCFxJUVAdLIMnLuAdxf1TGcS+zzCzEE8btIZ99mHF" "dGvPXeH8qLeNZA"; void decode_hex( const char * input, std::uint8_t * output, std::size_t output_length ) { std::uint8_t * end = output + output_length; while (output != end) { char high = *(input++); char low = *(input++); if (high >= 'a') high -= 'a' - ('9' + 1); if (low >= 'a') low -= 'a' - ('9' + 1); uint8_t value = ((high - '0') << 4) | (low - '0'); *(output++) = value; } } void decrypt_case(int message_type, const test_case * test_case) { std::uint8_t session_memory[olm_session_size()]; ::OlmSession * session = ::olm_session(session_memory); std::uint8_t pickled[strlen(session_data)]; ::memcpy(pickled, session_data, sizeof(pickled)); assert_not_equals( ::olm_error(), ::olm_unpickle_session(session, "", 0, pickled, sizeof(pickled)) ); std::size_t message_length = strlen(test_case->msghex) / 2; std::uint8_t * message = (std::uint8_t *) ::malloc(message_length); decode_hex(test_case->msghex, message, message_length); size_t max_length = olm_decrypt_max_plaintext_length( session, message_type, message, message_length ); if (test_case->expected_error) { assert_equals(::olm_error(), max_length); assert_equals( std::string(test_case->expected_error), std::string(::olm_session_last_error(session)) ); free(message); return; } assert_not_equals(::olm_error(), max_length); uint8_t plaintext[max_length]; decode_hex(test_case->msghex, message, message_length); olm_decrypt( session, message_type, message, message_length, plaintext, max_length ); free(message); } int main() { { TestCase my_test("Olm decrypt test"); for (unsigned int i = 0; i < sizeof(test_cases)/ sizeof(test_cases[0]); ++i) { decrypt_case(0, &test_cases[i]); } } } olm-2.2.2+git20170526.0fd768e+dfsg/tests/test_olm_sha256.cpp000066400000000000000000000007271311755073500225140ustar00rootroot00000000000000#include "olm/olm.h" #include "unittest.hh" int main() { { TestCase("Olm sha256 test"); std::uint8_t utility_buffer[::olm_utility_size()]; ::OlmUtility * utility = ::olm_utility(utility_buffer); assert_equals(std::size_t(43), ::olm_sha256_length(utility)); std::uint8_t output[43]; ::olm_sha256(utility, "Hello, World", 12, output, 43); std::uint8_t expected_output[] = "A2daxT/5zRU1zMffzfosRYxSGDcfQY3BNvLRmsH76KU"; assert_equals(output, expected_output, 43); } } olm-2.2.2+git20170526.0fd768e+dfsg/tests/test_olm_signature.cpp000066400000000000000000000043061311755073500235020ustar00rootroot00000000000000#include "olm/olm.h" #include "unittest.hh" #include #include #include struct MockRandom { MockRandom(std::uint8_t tag, std::uint8_t offset = 0) : tag(tag), current(offset) {} void operator()( void * buf, std::size_t length ) { std::uint8_t * bytes = (std::uint8_t *) buf; while (length > 32) { bytes[0] = tag; std::memset(bytes + 1, current, 31); length -= 32; bytes += 32; current += 1; } if (length) { bytes[0] = tag; std::memset(bytes + 1, current, length - 1); current += 1; } } std::uint8_t tag; std::uint8_t current; }; std::uint8_t * check_malloc(std::size_t size) { if (size == std::size_t(-1)) { assert_not_equals(std::size_t(-1), size); } return (std::uint8_t *)::malloc(size); } int main() { { /** Signing Test */ TestCase test_case("Signing test"); MockRandom mock_random_a('A', 0x00); void * account_buffer = check_malloc(::olm_account_size()); ::OlmAccount * account = ::olm_account(account_buffer); std::size_t random_size = ::olm_create_account_random_length(account); void * random = check_malloc(random_size); mock_random_a(random, random_size); ::olm_create_account(account, random, random_size); ::free(random); std::size_t message_size = 12; void * message = check_malloc(message_size); ::memcpy(message, "Hello, World", message_size); std::size_t signature_size = ::olm_account_signature_length(account); void * signature = check_malloc(signature_size); assert_not_equals(std::size_t(-1), ::olm_account_sign( account, message, message_size, signature, signature_size )); std::size_t id_keys_size = ::olm_account_identity_keys_length(account); std::uint8_t * id_keys = (std::uint8_t *) check_malloc(id_keys_size); assert_not_equals(std::size_t(-1), ::olm_account_identity_keys( account, id_keys, id_keys_size )); void * utility_buffer = check_malloc(::olm_utility_size()); ::OlmUtility * utility = ::olm_utility(utility_buffer); assert_not_equals(std::size_t(-1), ::olm_ed25519_verify( utility, id_keys + 71, 43, message, message_size, signature, signature_size )); } } olm-2.2.2+git20170526.0fd768e+dfsg/tests/test_olm_using_malloc.cpp000066400000000000000000000151741311755073500241620ustar00rootroot00000000000000#include "olm/olm.h" #include "unittest.hh" #include #include #include struct MockRandom { MockRandom(std::uint8_t tag, std::uint8_t offset = 0) : tag(tag), current(offset) {} void operator()( void * buf, std::size_t length ) { std::uint8_t * bytes = (std::uint8_t *) buf; while (length > 32) { bytes[0] = tag; std::memset(bytes + 1, current, 31); length -= 32; bytes += 32; current += 1; } if (length) { bytes[0] = tag; std::memset(bytes + 1, current, length - 1); current += 1; } } std::uint8_t tag; std::uint8_t current; }; std::uint8_t * check_malloc(std::size_t size) { if (size == std::size_t(-1)) { assert_not_equals(std::size_t(-1), size); } return (std::uint8_t *)::malloc(size); } int main() { { /** More messages test */ TestCase test_case("More messages test"); MockRandom mock_random_a('A', 0x00); MockRandom mock_random_b('B', 0x80); void * a_account_buffer = check_malloc(::olm_account_size()); ::OlmAccount *a_account = ::olm_account(a_account_buffer); std::size_t a_random_size = ::olm_create_account_random_length(a_account); void * a_random = check_malloc(a_random_size); mock_random_a(a_random, a_random_size); ::olm_create_account(a_account, a_random, a_random_size); free(a_random); void * b_account_buffer = check_malloc(::olm_account_size()); ::OlmAccount *b_account = ::olm_account(b_account_buffer); std::size_t b_random_size = ::olm_create_account_random_length(b_account); void * b_random = check_malloc(b_random_size); mock_random_b(b_random, b_random_size); ::olm_create_account(b_account, b_random, b_random_size); free(b_random); std::size_t o_random_size = ::olm_account_generate_one_time_keys_random_length( b_account, 42 ); void * o_random = check_malloc(o_random_size); mock_random_b(o_random, o_random_size); ::olm_account_generate_one_time_keys(b_account, 42, o_random, o_random_size); free(o_random); std::size_t b_id_keys_size = ::olm_account_identity_keys_length(b_account); std::size_t b_ot_keys_size = ::olm_account_one_time_keys_length(b_account); std::uint8_t * b_id_keys = (std::uint8_t *) check_malloc(b_id_keys_size); std::uint8_t * b_ot_keys = (std::uint8_t *) check_malloc(b_ot_keys_size); ::olm_account_identity_keys(b_account, b_id_keys, b_id_keys_size); ::olm_account_one_time_keys(b_account, b_ot_keys, b_ot_keys_size); void * a_session_buffer = check_malloc(::olm_session_size()); ::OlmSession *a_session = ::olm_session(a_session_buffer); std::size_t a_rand_size = ::olm_create_outbound_session_random_length(a_session); void * a_rand = check_malloc(a_rand_size); mock_random_a(a_rand, a_rand_size); assert_not_equals(std::size_t(-1), ::olm_create_outbound_session( a_session, a_account, b_id_keys + 15, 43, b_ot_keys + 25, 43, a_rand, a_rand_size )); free(b_id_keys); free(b_ot_keys); free(a_rand); void * plaintext = malloc(12); ::memcpy(plaintext, "Hello, World", 12); std::size_t message_1_size = ::olm_encrypt_message_length(a_session, 12); void * message_1 = check_malloc(message_1_size); std::size_t a_message_random_size = ::olm_encrypt_random_length(a_session); void * a_message_random = check_malloc(a_message_random_size); mock_random_a(a_message_random, a_message_random_size); assert_equals(std::size_t(0), ::olm_encrypt_message_type(a_session)); assert_not_equals(std::size_t(-1), ::olm_encrypt( a_session, plaintext, 12, a_message_random, a_message_random_size, message_1, message_1_size )); free(a_message_random); void * tmp_message_1 = check_malloc(message_1_size); std::memcpy(tmp_message_1, message_1, message_1_size); void * b_session_buffer = check_malloc(olm_account_size()); ::OlmSession *b_session = ::olm_session(b_session_buffer); ::olm_create_inbound_session( b_session, b_account, tmp_message_1, message_1_size ); std::memcpy(tmp_message_1, message_1, message_1_size); std::size_t plaintext_1_size = ::olm_decrypt_max_plaintext_length( b_session, 0, tmp_message_1, message_1_size ); void * plaintext_1 = check_malloc(plaintext_1_size); std::memcpy(tmp_message_1, message_1, message_1_size); assert_equals(std::size_t(12), ::olm_decrypt( b_session, 0, tmp_message_1, message_1_size, plaintext_1, plaintext_1_size )); free(tmp_message_1); free(plaintext_1); free(message_1); assert_not_equals( std::size_t(-1), ::olm_remove_one_time_keys(b_account, b_session) ); for (unsigned i = 0; i < 8; ++i) { { std::size_t msg_a_size = ::olm_encrypt_message_length(a_session, 12); std::size_t rnd_a_size = ::olm_encrypt_random_length(a_session); void * msg_a = check_malloc(msg_a_size); void * rnd_a = check_malloc(rnd_a_size); mock_random_a(rnd_a, rnd_a_size); std::size_t type_a = ::olm_encrypt_message_type(a_session); assert_not_equals(std::size_t(-1), ::olm_encrypt( a_session, plaintext, 12, rnd_a, rnd_a_size, msg_a, msg_a_size )); free(rnd_a); void * tmp_a = check_malloc(msg_a_size); std::memcpy(tmp_a, msg_a, msg_a_size); std::size_t out_a_size = ::olm_decrypt_max_plaintext_length( b_session, type_a, tmp_a, msg_a_size ); void * out_a = check_malloc(out_a_size); std::memcpy(tmp_a, msg_a, msg_a_size); assert_equals(std::size_t(12), ::olm_decrypt( b_session, type_a, tmp_a, msg_a_size, out_a, out_a_size )); free(tmp_a); free(msg_a); free(out_a); } { std::size_t msg_b_size = ::olm_encrypt_message_length(b_session, 12); std::size_t rnd_b_size = ::olm_encrypt_random_length(b_session); void * msg_b = check_malloc(msg_b_size); void * rnd_b = check_malloc(rnd_b_size); mock_random_b(rnd_b, rnd_b_size); std::size_t type_b = ::olm_encrypt_message_type(b_session); assert_not_equals(std::size_t(-1), ::olm_encrypt( b_session, plaintext, 12, rnd_b, rnd_b_size, msg_b, msg_b_size )); free(rnd_b); void * tmp_b = check_malloc(msg_b_size); std::memcpy(tmp_b, msg_b, msg_b_size); std::size_t out_b_size = ::olm_decrypt_max_plaintext_length( a_session, type_b, tmp_b, msg_b_size ); void * out_b = check_malloc(out_b_size); std::memcpy(tmp_b, msg_b, msg_b_size); assert_equals(std::size_t(12), ::olm_decrypt( a_session, type_b, msg_b, msg_b_size, out_b, out_b_size )); free(tmp_b); free(msg_b); free(out_b); } } ::olm_clear_account(a_account); ::olm_clear_account(b_account); ::olm_clear_session(a_session); ::olm_clear_session(b_session); free(a_account_buffer); free(b_account_buffer); free(a_session_buffer); free(b_session_buffer); free(plaintext); } } olm-2.2.2+git20170526.0fd768e+dfsg/tests/test_ratchet.cpp000066400000000000000000000155411311755073500222670ustar00rootroot00000000000000/* Copyright 2015 OpenMarket Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "olm/ratchet.hh" #include "olm/cipher.h" #include "unittest.hh" int main() { std::uint8_t root_info[] = "Olm"; std::uint8_t ratchet_info[] = "OlmRatchet"; std::uint8_t message_info[] = "OlmMessageKeys"; olm::KdfInfo kdf_info = { root_info, sizeof(root_info) - 1, ratchet_info, sizeof(ratchet_info) - 1 }; _olm_cipher_aes_sha_256 cipher0 = OLM_CIPHER_INIT_AES_SHA_256(message_info); _olm_cipher *cipher = OLM_CIPHER_BASE(&cipher0); std::uint8_t random_bytes[] = "0123456789ABDEF0123456789ABCDEF"; _olm_curve25519_key_pair alice_key; _olm_crypto_curve25519_generate_key(random_bytes, &alice_key); std::uint8_t shared_secret[] = "A secret"; { /* Send/Receive test case */ TestCase test_case("Olm Send/Receive"); olm::Ratchet alice(kdf_info, cipher); olm::Ratchet bob(kdf_info, cipher); alice.initialise_as_alice(shared_secret, sizeof(shared_secret) - 1, alice_key); bob.initialise_as_bob(shared_secret, sizeof(shared_secret) - 1, alice_key.public_key); std::uint8_t plaintext[] = "Message"; std::size_t plaintext_length = sizeof(plaintext) - 1; std::size_t message_length, random_length, output_length; std::size_t encrypt_length, decrypt_length; { /* Alice sends Bob a message */ message_length = alice.encrypt_output_length(plaintext_length); random_length = alice.encrypt_random_length(); assert_equals(std::size_t(0), random_length); std::uint8_t message[message_length]; encrypt_length = alice.encrypt( plaintext, plaintext_length, NULL, 0, message, message_length ); assert_equals(message_length, encrypt_length); output_length = bob.decrypt_max_plaintext_length(message, message_length); std::uint8_t output[output_length]; decrypt_length = bob.decrypt( message, message_length, output, output_length ); assert_equals(plaintext_length, decrypt_length); assert_equals(plaintext, output, decrypt_length); } { /* Bob sends Alice a message */ message_length = bob.encrypt_output_length(plaintext_length); random_length = bob.encrypt_random_length(); assert_equals(std::size_t(32), random_length); std::uint8_t message[message_length]; std::uint8_t random[] = "This is a random 32 byte string."; encrypt_length = bob.encrypt( plaintext, plaintext_length, random, 32, message, message_length ); assert_equals(message_length, encrypt_length); output_length = alice.decrypt_max_plaintext_length(message, message_length); std::uint8_t output[output_length]; decrypt_length = alice.decrypt( message, message_length, output, output_length ); assert_equals(plaintext_length, decrypt_length); assert_equals(plaintext, output, decrypt_length); } } /* Send/receive message test case */ { /* Out of order test case */ TestCase test_case("Olm Out of Order"); olm::Ratchet alice(kdf_info, cipher); olm::Ratchet bob(kdf_info, cipher); alice.initialise_as_alice(shared_secret, sizeof(shared_secret) - 1, alice_key); bob.initialise_as_bob(shared_secret, sizeof(shared_secret) - 1, alice_key.public_key); std::uint8_t plaintext_1[] = "First Message"; std::size_t plaintext_1_length = sizeof(plaintext_1) - 1; std::uint8_t plaintext_2[] = "Second Messsage. A bit longer than the first."; std::size_t plaintext_2_length = sizeof(plaintext_2) - 1; std::size_t message_1_length, message_2_length, random_length, output_length; std::size_t encrypt_length, decrypt_length; { /* Alice sends Bob two messages and they arrive out of order */ message_1_length = alice.encrypt_output_length(plaintext_1_length); random_length = alice.encrypt_random_length(); assert_equals(std::size_t(0), random_length); std::uint8_t message_1[message_1_length]; std::uint8_t random[] = "This is a random 32 byte string."; encrypt_length = alice.encrypt( plaintext_1, plaintext_1_length, random, 32, message_1, message_1_length ); assert_equals(message_1_length, encrypt_length); message_2_length = alice.encrypt_output_length(plaintext_2_length); random_length = alice.encrypt_random_length(); assert_equals(std::size_t(0), random_length); std::uint8_t message_2[message_2_length]; encrypt_length = alice.encrypt( plaintext_2, plaintext_2_length, NULL, 0, message_2, message_2_length ); assert_equals(message_2_length, encrypt_length); output_length = bob.decrypt_max_plaintext_length( message_2, message_2_length ); std::uint8_t output_1[output_length]; decrypt_length = bob.decrypt( message_2, message_2_length, output_1, output_length ); assert_equals(plaintext_2_length, decrypt_length); assert_equals(plaintext_2, output_1, decrypt_length); output_length = bob.decrypt_max_plaintext_length( message_1, message_1_length ); std::uint8_t output_2[output_length]; decrypt_length = bob.decrypt( message_1, message_1_length, output_2, output_length ); assert_equals(plaintext_1_length, decrypt_length); assert_equals(plaintext_1, output_2, decrypt_length); } } /* Out of order test case */ { /* More messages */ TestCase test_case("Olm More Messages"); olm::Ratchet alice(kdf_info, cipher); olm::Ratchet bob(kdf_info, cipher); alice.initialise_as_alice(shared_secret, sizeof(shared_secret) - 1, alice_key); bob.initialise_as_bob(shared_secret, sizeof(shared_secret) - 1, alice_key.public_key); std::uint8_t plaintext[] = "These 15 bytes"; assert_equals(std::size_t(15), sizeof(plaintext)); std::uint8_t random[] = "This is a random 32 byte string"; for (unsigned i = 0; i < 8; ++i) { { std::uint8_t msg[alice.encrypt_output_length(sizeof(plaintext))]; alice.encrypt( plaintext, 15, random, 32, msg, sizeof(msg) ); std::uint8_t output[bob.decrypt_max_plaintext_length(msg, sizeof(msg))]; assert_equals( std::size_t(15), bob.decrypt(msg, sizeof(msg), output, sizeof(output)) ); } random[31]++; { std::uint8_t msg[bob.encrypt_output_length(sizeof(plaintext))]; bob.encrypt( plaintext, 15, random, 32, msg, sizeof(msg) ); std::uint8_t output[alice.decrypt_max_plaintext_length(msg, sizeof(msg))]; assert_equals( std::size_t(15), alice.decrypt(msg, sizeof(msg), output, sizeof(output)) ); } random[31]++; } } } olm-2.2.2+git20170526.0fd768e+dfsg/tests/test_session.cpp000066400000000000000000000102551311755073500223150ustar00rootroot00000000000000#include "olm/session.hh" #include "olm/pickle_encoding.h" #include "unittest.hh" /* decode into a buffer, which is returned */ std::uint8_t *decode_hex( const char * input ) { static std::uint8_t buf[256]; std::uint8_t *p = buf; while (*input != '\0') { char high = *(input++); char low = *(input++); if (high >= 'a') high -= 'a' - ('9' + 1); if (low >= 'a') low -= 'a' - ('9' + 1); uint8_t value = ((high - '0') << 4) | (low - '0'); *p++ = value; } return buf; } void check_session(const olm::Session &session) { assert_equals( decode_hex("49d640dc96b80176694af69fc4b8ca9fac49aecbd697d01fd8bee1ed2693b6c9"), session.ratchet.root_key, 32 ); assert_equals( std::size_t(1), session.ratchet.sender_chain.size() ); assert_equals( decode_hex("f77a03eaa9b301fa7d2a5aa6b50286906de12cc96044f526dbbcb12839ad7003"), session.ratchet.sender_chain[0].ratchet_key.public_key.public_key, 32 ); assert_equals( decode_hex("d945c6ed4c7c277117adf11fb133a7936d287afe97c0b3ac989644b4490d4f31"), session.ratchet.sender_chain[0].ratchet_key.private_key.private_key, 32 ); assert_equals( std::uint32_t(0), session.ratchet.sender_chain[0].chain_key.index ); assert_equals( std::size_t(0), session.ratchet.receiver_chains.size() ); assert_equals( std::size_t(0), session.ratchet.skipped_message_keys.size() ); assert_equals(OLM_SUCCESS, session.last_error); assert_equals(false, session.received_message); assert_equals( decode_hex("7326b58623a3f7bd8da11a1bab51f432c02a7430241b326e9fc8916a21eb257e"), session.alice_identity_key.public_key, 32 ); assert_equals( decode_hex("0ab4b30bde20bd374ceccc72861660f0fd046f7516900796c3e5de41c598316c"), session.alice_base_key.public_key, 32 ); assert_equals( decode_hex("585dba930b10d90d81702c715f4085d07c42b0cd2d676010bb6086c86c4cc618"), session.bob_one_time_key.public_key, 32 ); } int main() { { TestCase test_case("V1 session pickle"); const uint8_t *PICKLE_KEY=(uint8_t *)"secret_key"; uint8_t pickled[] = "wkEpwMgiAqD7B1/Lw2cKYYDcUZVOd9QHes7ZroWxr/Rp/nWEAySgRsIu/a54YhO67rwitr" "Lpos7tFxxK9IZ7pKB1qrR1coVWIt78V9lp9WgmBAvxHBSY+tu1lkL/JjLi963/yFdPancZ" "+WHMVfaKlV3gWGpo7EfNK6qAOxI1Ea/eCsE2sYrsHEDvLLGlKAA9E56rmmoe2w6TKzsQjs" "ZM2/XT2eJ82EgMO9pL02iLElXWmGNv72Ut7DouR0pQIT50HIEEKcFxYcoTb3WCfJD76Coe" "sE4kx+TA6d45Xu1bwQNNkTGF+nCCu/GmKY+sECXbz9U6WhxG0YdF9Z4T8YkWYAgpKNS0FW" "RV"; size_t pickle_len = _olm_enc_input( PICKLE_KEY, strlen((char *)PICKLE_KEY), pickled, strlen((char *)pickled), NULL ); olm::Session session; const uint8_t *unpickle_res = olm::unpickle(pickled, pickled+sizeof(pickled), session); assert_equals( pickle_len, (size_t)(unpickle_res - pickled) ); check_session(session); #if 0 size_t rawlen = olm::pickle_length(session); uint8_t *r1 = _olm_enc_output_pos(pickled, rawlen); olm::pickle(r1, session); _olm_enc_output( PICKLE_KEY, strlen((char *)PICKLE_KEY), pickled, rawlen); printf("%s\n", pickled); #endif } { TestCase test_case("V2 session pickle"); const uint8_t *PICKLE_KEY=(uint8_t *)"secret_key"; uint8_t pickled[] = "m+DS/q34MXpw2xp50ZD0B7val1mlMpQXo0mx+VPje0weFYRRuuZQBdJgcFPEpi2MVSpA4c" "qgqHyj2/bU7/lz+BXkEBrCFVx0BJidxXfOLDW4TNtRhLS1YHJNGP8GvTg1+dCytBTLsCdm" "5f945Eq1U/pY3Cg96YTUufFP6EYrfRoDbAsRHc+h+wKKftQv+W44yUmRhcCemGHtpxk3UQ" "AMCI7EBv9BvveyZMy3p9qZ3xvFK34Hef+R7gjtFycz7Nk/4UF46sT3cTmUlXz9iFW4uz2F" "rTI1Wjym+l0DadsbSpHSUjmp9zt4qRP2UjwfZ5QNLv+cdObIfqFsiThGu/PlKigdF4SLHr" "nG"; size_t pickle_len = _olm_enc_input( PICKLE_KEY, strlen((char *)PICKLE_KEY), pickled, strlen((char *)pickled), NULL ); olm::Session session; const uint8_t *unpickle_res = olm::unpickle(pickled, pickled+sizeof(pickled), session); assert_equals( pickle_len, (size_t)(unpickle_res - pickled) ); check_session(session); } return 0; } olm-2.2.2+git20170526.0fd768e+dfsg/tracing/000077500000000000000000000000001311755073500173515ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/tracing/README.rst000066400000000000000000000003171311755073500210410ustar00rootroot00000000000000Tracing ======= To see what crypto functions are being called with what input run .. code:: bash gdb --batch -x tracing/trace.gdb ./build/test_ratchet | grep "^[- ]" | tr "{}" "[]" | tracing/graph.py olm-2.2.2+git20170526.0fd768e+dfsg/tracing/graph.py000077500000000000000000000057531311755073500210410ustar00rootroot00000000000000#! /usr/bin/env python import sys import yaml import array class Call(object): def __init__(self, call): self.func, = call args = dict(call[self.func]) self.output = array.array("B", args.pop("output")).tostring() self.inputs = { name: array.array("B", args[name]).tostring() for name in args if not name.endswith("_length") } self.bind = {} def expr(self, stream, indent=" ", level=""): stream.write(self.func + "(\n") for name, value in self.inputs.items(): stream.write(level + indent + name + "=") self.bind.get(name, Literal(value)).expr( stream, indent, level + indent ) stream.write(",\n") stream.write(level + ")") class Literal(str): def expr(self, stream, indent, level): stream.write("\"" + self.encode("hex") + "\"") class Slice(object): def __init__(self, thing, start, end): self.thing = thing self.start = start self.end = end def expr(self, stream, indent=" ", level=""): self.thing.expr(stream, indent, level) stream.write("[%d:%d]" % (self.start, self.end)) class Concat(list): def expr(self, stream, indent=" ", level=""): stream.write("concat(\n") for thing in self: stream.write(level + indent) thing.expr(stream, indent, level + indent) stream.write(",\n") stream.write(level + ")") calls = [Call(c) for c in yaml.load(sys.stdin)] outputs = {} for call in calls: for i in range(8, len(call.output)): outputs.setdefault(call.output[i - 8: i], []).append(call) for call in calls: for name, value in call.inputs.items(): for bind in outputs.get(value[:8], ()): if value == bind.output: call.bind[name] = bind else: for end in range(len(value), len(bind.output) + 1): start = end - len(value) if value == bind.output[start:end]: call.bind[name] = Slice(bind, start, end) if not name in call.bind: i = 0 j = 1 k = 0 concat = Concat() while i < len(value): for bind in outputs.get(value[i:i+8], ()): if value[i:].startswith(bind.output): if k != i: concat.append(Literal(value[k:i])) concat.append(bind) j = len(bind.output) k = i + j break i += j j = 1 if concat: if k != i: concat.append(Literal(value[k:i])) call.bind[name] = concat for call in calls: if call.func.startswith("h"): sys.stdout.write("\"" + call.output.encode("hex") + "\" = ") call.expr(sys.stdout) sys.stdout.write("\n") olm-2.2.2+git20170526.0fd768e+dfsg/tracing/trace.gdb000066400000000000000000000042471311755073500211340ustar00rootroot00000000000000set print pretty off set print repeats unlimited set print elements unlimited set breakpoint pending on break crypto.cpp:273 commands silent printf "- hmac_sha256:\n" printf " key_length: %d\n", key_length printf " input_length: %d\n", input_length if key_length > 0 printf " key: " output/x *key@key_length printf "\n" else printf " key: {}\n" end if input_length > 0 printf " input: " output/x *input@input_length printf "\n" else printf " input: {}\n" end cont end break crypto.cpp:280 commands silent printf " output: " output/x *output@32 printf "\n" cont end break crypto.cpp:307 commands silent set $hkdf_output = output cont end break crypto.cpp:323 commands silent printf "- hkdf_sha256:\n" printf " input_length: %d\n", input_length printf " salt_length: %d\n", salt_length printf " info_length: %d\n", info_length printf " output_length: %d\n", output_length if input_length > 0 printf " input: " output/x *input@input_length printf "\n" else printf " input: {}\n" end if salt_length > 0 printf " salt: " output/x *salt@salt_length printf "\n" else printf " salt: {}\n" end if info_length > 0 printf " info: " output/x *info@info_length printf "\n" else printf " info: {}\n" end printf " output: " output/x *$hkdf_output@output_length printf "\n" cont end break crypto.cpp:156 commands silent printf "- curve25519:\n" printf " public: " output/x *their_key.public_key@32 printf "\n" printf " private: " output/x *our_key.private_key@32 printf "\n" printf " output: " output/x *output@32 printf "\n" cont end break crypto.cpp:156 commands silent printf "- curve25519:\n" printf " public: " output/x *their_key.public_key@32 printf "\n" printf " private: " output/x *our_key.private_key@32 printf "\n" printf " output: " output/x *output@32 printf "\n" cont end break crypto.cpp:147 commands silent printf "- curve25519:\n" printf " public: " output/x *CURVE25519_BASEPOINT@32 printf "\n" printf " private: " output/x *key_pair.private_key@32 printf "\n" printf " output: " output/x *key_pair.public_key@32 printf "\n" cont end run olm-2.2.2+git20170526.0fd768e+dfsg/version_script.ver000066400000000000000000000002341311755073500215100ustar00rootroot00000000000000# this is a 'version script' for the linker which tells it to only export # symbols starting 'olm_'. { global: olm_*; local: *; }; olm-2.2.2+git20170526.0fd768e+dfsg/xcode/000077500000000000000000000000001311755073500170245ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit.xcodeproj/000077500000000000000000000000001311755073500221175ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit.xcodeproj/project.pbxproj000066400000000000000000000501461311755073500252010ustar00rootroot00000000000000// !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 3274F6021D9A633A005282E4 /* OLMKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3274F5F81D9A633A005282E4 /* OLMKit.framework */; }; 3274F6071D9A633A005282E4 /* OLMKitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3274F6061D9A633A005282E4 /* OLMKitTests.m */; }; 3274F6131D9A698E005282E4 /* OLMKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 3274F6121D9A698E005282E4 /* OLMKit.h */; }; 32A151311DABDD4300400192 /* OLMKitGroupTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A151301DABDD4300400192 /* OLMKitGroupTests.m */; }; 7DBAD311AEA85CF6DB80DCFA /* libPods-OLMKitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7123FABE917D0FB140E036B7 /* libPods-OLMKitTests.a */; }; D667051A0BA47E17CCC4E5D7 /* libPods-OLMKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F2F22FE8F173AF845B882805 /* libPods-OLMKit.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 3274F6031D9A633A005282E4 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 3274F5EF1D9A633A005282E4 /* Project object */; proxyType = 1; remoteGlobalIDString = 3274F5F71D9A633A005282E4; remoteInfo = OLMKit; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ 1B226B371526F2782C9D6372 /* Pods-OLMKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OLMKit.release.xcconfig"; path = "Pods/Target Support Files/Pods-OLMKit/Pods-OLMKit.release.xcconfig"; sourceTree = ""; }; 3274F5F81D9A633A005282E4 /* OLMKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OLMKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3274F5FC1D9A633A005282E4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3274F6011D9A633A005282E4 /* OLMKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OLMKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3274F6061D9A633A005282E4 /* OLMKitTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OLMKitTests.m; sourceTree = ""; }; 3274F6081D9A633A005282E4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3274F6121D9A698E005282E4 /* OLMKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OLMKit.h; sourceTree = ""; }; 32A151301DABDD4300400192 /* OLMKitGroupTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OLMKitGroupTests.m; sourceTree = ""; }; 7123FABE917D0FB140E036B7 /* libPods-OLMKitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-OLMKitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 875BA7A520258EA15A31DD82 /* Pods-OLMKitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OLMKitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-OLMKitTests/Pods-OLMKitTests.debug.xcconfig"; sourceTree = ""; }; D48E486DAE1F59F4F7EA8C25 /* Pods-OLMKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OLMKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-OLMKitTests/Pods-OLMKitTests.release.xcconfig"; sourceTree = ""; }; E50E6B16E3433A5EB3297DEE /* Pods-OLMKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OLMKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-OLMKit/Pods-OLMKit.debug.xcconfig"; sourceTree = ""; }; F2F22FE8F173AF845B882805 /* libPods-OLMKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-OLMKit.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 3274F5F41D9A633A005282E4 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( D667051A0BA47E17CCC4E5D7 /* libPods-OLMKit.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 3274F5FE1D9A633A005282E4 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 3274F6021D9A633A005282E4 /* OLMKit.framework in Frameworks */, 7DBAD311AEA85CF6DB80DCFA /* libPods-OLMKitTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 1FA3F53DFAAAA773F07F5E56 /* Pods */ = { isa = PBXGroup; children = ( E50E6B16E3433A5EB3297DEE /* Pods-OLMKit.debug.xcconfig */, 1B226B371526F2782C9D6372 /* Pods-OLMKit.release.xcconfig */, 875BA7A520258EA15A31DD82 /* Pods-OLMKitTests.debug.xcconfig */, D48E486DAE1F59F4F7EA8C25 /* Pods-OLMKitTests.release.xcconfig */, ); name = Pods; sourceTree = ""; }; 3274F5EE1D9A633A005282E4 = { isa = PBXGroup; children = ( 3274F5FA1D9A633A005282E4 /* OLMKit */, 3274F6051D9A633A005282E4 /* OLMKitTests */, 3274F5F91D9A633A005282E4 /* Products */, 1FA3F53DFAAAA773F07F5E56 /* Pods */, A5D2E6F079A29F7CC2A8D9FE /* Frameworks */, ); sourceTree = ""; }; 3274F5F91D9A633A005282E4 /* Products */ = { isa = PBXGroup; children = ( 3274F5F81D9A633A005282E4 /* OLMKit.framework */, 3274F6011D9A633A005282E4 /* OLMKitTests.xctest */, ); name = Products; sourceTree = ""; }; 3274F5FA1D9A633A005282E4 /* OLMKit */ = { isa = PBXGroup; children = ( 3274F6121D9A698E005282E4 /* OLMKit.h */, 3274F5FC1D9A633A005282E4 /* Info.plist */, ); path = OLMKit; sourceTree = ""; }; 3274F6051D9A633A005282E4 /* OLMKitTests */ = { isa = PBXGroup; children = ( 3274F6061D9A633A005282E4 /* OLMKitTests.m */, 32A151301DABDD4300400192 /* OLMKitGroupTests.m */, 3274F6081D9A633A005282E4 /* Info.plist */, ); path = OLMKitTests; sourceTree = ""; }; A5D2E6F079A29F7CC2A8D9FE /* Frameworks */ = { isa = PBXGroup; children = ( F2F22FE8F173AF845B882805 /* libPods-OLMKit.a */, 7123FABE917D0FB140E036B7 /* libPods-OLMKitTests.a */, ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ 3274F5F51D9A633A005282E4 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 3274F6131D9A698E005282E4 /* OLMKit.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 3274F5F71D9A633A005282E4 /* OLMKit */ = { isa = PBXNativeTarget; buildConfigurationList = 3274F60C1D9A633B005282E4 /* Build configuration list for PBXNativeTarget "OLMKit" */; buildPhases = ( 7FBCB292198F4156D9CA3B8D /* [CP] Check Pods Manifest.lock */, 3274F5F31D9A633A005282E4 /* Sources */, 3274F5F41D9A633A005282E4 /* Frameworks */, 3274F5F51D9A633A005282E4 /* Headers */, 3274F5F61D9A633A005282E4 /* Resources */, 30F93582035CD30D211A6C76 /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); name = OLMKit; productName = OLMKit; productReference = 3274F5F81D9A633A005282E4 /* OLMKit.framework */; productType = "com.apple.product-type.framework"; }; 3274F6001D9A633A005282E4 /* OLMKitTests */ = { isa = PBXNativeTarget; buildConfigurationList = 3274F60F1D9A633B005282E4 /* Build configuration list for PBXNativeTarget "OLMKitTests" */; buildPhases = ( 47E69E5BE6A019858DC41D4F /* [CP] Check Pods Manifest.lock */, 3274F5FD1D9A633A005282E4 /* Sources */, 3274F5FE1D9A633A005282E4 /* Frameworks */, 3274F5FF1D9A633A005282E4 /* Resources */, 0A185F0CAE96B33A4CD91B6A /* [CP] Embed Pods Frameworks */, 793D0533290528B7C0E17CAD /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( 3274F6041D9A633A005282E4 /* PBXTargetDependency */, ); name = OLMKitTests; productName = OLMKitTests; productReference = 3274F6011D9A633A005282E4 /* OLMKitTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 3274F5EF1D9A633A005282E4 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0810; ORGANIZATIONNAME = matrix.org; TargetAttributes = { 3274F5F71D9A633A005282E4 = { CreatedOnToolsVersion = 8.0; ProvisioningStyle = Automatic; }; 3274F6001D9A633A005282E4 = { CreatedOnToolsVersion = 8.0; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 3274F5F21D9A633A005282E4 /* Build configuration list for PBXProject "OLMKit" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, ); mainGroup = 3274F5EE1D9A633A005282E4; productRefGroup = 3274F5F91D9A633A005282E4 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 3274F5F71D9A633A005282E4 /* OLMKit */, 3274F6001D9A633A005282E4 /* OLMKitTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 3274F5F61D9A633A005282E4 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 3274F5FF1D9A633A005282E4 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 0A185F0CAE96B33A4CD91B6A /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-OLMKitTests/Pods-OLMKitTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; 30F93582035CD30D211A6C76 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-OLMKit/Pods-OLMKit-resources.sh\"\n"; showEnvVarsInLog = 0; }; 47E69E5BE6A019858DC41D4F /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; 793D0533290528B7C0E17CAD /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-OLMKitTests/Pods-OLMKitTests-resources.sh\"\n"; showEnvVarsInLog = 0; }; 7FBCB292198F4156D9CA3B8D /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 3274F5F31D9A633A005282E4 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 3274F5FD1D9A633A005282E4 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 3274F6071D9A633A005282E4 /* OLMKitTests.m in Sources */, 32A151311DABDD4300400192 /* OLMKitGroupTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 3274F6041D9A633A005282E4 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 3274F5F71D9A633A005282E4 /* OLMKit */; targetProxy = 3274F6031D9A633A005282E4 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ 3274F60A1D9A633B005282E4 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVES = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 10.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 3274F60B1D9A633B005282E4 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVES = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 10.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 3274F60D1D9A633B005282E4 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = E50E6B16E3433A5EB3297DEE /* Pods-OLMKit.debug.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = OLMKit/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.matrix.OLMKit; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; }; name = Debug; }; 3274F60E1D9A633B005282E4 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 1B226B371526F2782C9D6372 /* Pods-OLMKit.release.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = OLMKit/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.matrix.OLMKit; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; }; name = Release; }; 3274F6101D9A633B005282E4 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 875BA7A520258EA15A31DD82 /* Pods-OLMKitTests.debug.xcconfig */; buildSettings = { INFOPLIST_FILE = OLMKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.matrix.OLMKitTests; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; 3274F6111D9A633B005282E4 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = D48E486DAE1F59F4F7EA8C25 /* Pods-OLMKitTests.release.xcconfig */; buildSettings = { INFOPLIST_FILE = OLMKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.matrix.OLMKitTests; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 3274F5F21D9A633A005282E4 /* Build configuration list for PBXProject "OLMKit" */ = { isa = XCConfigurationList; buildConfigurations = ( 3274F60A1D9A633B005282E4 /* Debug */, 3274F60B1D9A633B005282E4 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 3274F60C1D9A633B005282E4 /* Build configuration list for PBXNativeTarget "OLMKit" */ = { isa = XCConfigurationList; buildConfigurations = ( 3274F60D1D9A633B005282E4 /* Debug */, 3274F60E1D9A633B005282E4 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 3274F60F1D9A633B005282E4 /* Build configuration list for PBXNativeTarget "OLMKitTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 3274F6101D9A633B005282E4 /* Debug */, 3274F6111D9A633B005282E4 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 3274F5EF1D9A633A005282E4 /* Project object */; } olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit/000077500000000000000000000000001311755073500201235ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit/Info.plist000066400000000000000000000014461311755073500221000ustar00rootroot00000000000000 CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType FMWK CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit/OLMAccount.h000066400000000000000000000031601311755073500222400ustar00rootroot00000000000000/* Copyright 2016 Chris Ballinger 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. */ #import #import "OLMSerializable.h" @class OLMSession; @interface OLMAccount : NSObject /** Creates new account */ - (instancetype) initNewAccount; /** public identity keys. base64 encoded in "curve25519" and "ed25519" keys */ - (NSDictionary*) identityKeys; /** signs message with ed25519 key for account */ - (NSString*) signMessage:(NSData*)messageData; /** Public parts of the unpublished one time keys for the account */ - (NSDictionary*) oneTimeKeys; - (BOOL) removeOneTimeKeysForSession:(OLMSession*)session; /** Marks the current set of one time keys as being published. */ - (void) markOneTimeKeysAsPublished; /** The largest number of one time keys this account can store. */ - (NSUInteger) maxOneTimeKeys; /** Generates a number of new one time keys. If the total number of keys stored * by this account exceeds -maxOneTimeKeys then the old keys are * discarded. */ - (void) generateOneTimeKeys:(NSUInteger)numberOfKeys; @end olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit/OLMAccount.m000066400000000000000000000214151311755073500222500ustar00rootroot00000000000000/* Copyright 2016 Chris Ballinger 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. */ #import "OLMAccount.h" #import "OLMAccount_Private.h" #import "OLMSession.h" #import "OLMSession_Private.h" #import "OLMUtility.h" @import Security; @implementation OLMAccount - (void) dealloc { olm_clear_account(_account); free(_account); } - (BOOL) initializeAccountMemory { size_t accountSize = olm_account_size(); _account = malloc(accountSize); NSParameterAssert(_account != nil); if (!_account) { return NO; } _account = olm_account(_account); NSParameterAssert(_account != nil); if (!_account) { return NO; } return YES; } - (instancetype) init { self = [super init]; if (!self) { return nil; } BOOL success = [self initializeAccountMemory]; if (!success) { return nil; } return self; } - (instancetype) initNewAccount { self = [self init]; if (!self) { return nil; } size_t randomLength = olm_create_account_random_length(_account); NSMutableData *random = [OLMUtility randomBytesOfLength:randomLength]; size_t accountResult = olm_create_account(_account, random.mutableBytes, random.length); [random resetBytesInRange:NSMakeRange(0, random.length)]; if (accountResult == olm_error()) { const char *error = olm_account_last_error(_account); NSLog(@"error creating account: %s", error); return nil; } return self; } - (NSUInteger) maxOneTimeKeys { return olm_account_max_number_of_one_time_keys(_account); } /** public identity keys */ - (NSDictionary*) identityKeys { size_t identityKeysLength = olm_account_identity_keys_length(_account); uint8_t *identityKeysBytes = malloc(identityKeysLength); if (!identityKeysBytes) { return nil; } size_t result = olm_account_identity_keys(_account, identityKeysBytes, identityKeysLength); if (result == olm_error()) { const char *error = olm_account_last_error(_account); NSLog(@"error getting id keys: %s", error); free(identityKeysBytes); return nil; } NSData *idKeyData = [NSData dataWithBytesNoCopy:identityKeysBytes length:identityKeysLength freeWhenDone:YES]; NSError *error = nil; NSDictionary *keysDictionary = [NSJSONSerialization JSONObjectWithData:idKeyData options:0 error:&error]; if (error) { NSLog(@"Could not decode JSON: %@", error.localizedDescription); } return keysDictionary; } - (NSString *)signMessage:(NSData *)messageData { size_t signatureLength = olm_account_signature_length(_account); uint8_t *signatureBytes = malloc(signatureLength); if (!signatureBytes) { return nil; } size_t result = olm_account_sign(_account, messageData.bytes, messageData.length, signatureBytes, signatureLength); if (result == olm_error()) { const char *error = olm_account_last_error(_account); NSLog(@"error signing message: %s", error); free(signatureBytes); return nil; } NSData *signatureData = [NSData dataWithBytesNoCopy:signatureBytes length:signatureLength freeWhenDone:YES]; return [[NSString alloc] initWithData:signatureData encoding:NSUTF8StringEncoding]; } - (NSDictionary*) oneTimeKeys { size_t otkLength = olm_account_one_time_keys_length(_account); uint8_t *otkBytes = malloc(otkLength); if (!otkBytes) { return nil; } size_t result = olm_account_one_time_keys(_account, otkBytes, otkLength); if (result == olm_error()) { const char *error = olm_account_last_error(_account); NSLog(@"error getting id keys: %s", error); free(otkBytes); } NSData *otk = [NSData dataWithBytesNoCopy:otkBytes length:otkLength freeWhenDone:YES]; NSError *error = nil; NSDictionary *keysDictionary = [NSJSONSerialization JSONObjectWithData:otk options:0 error:&error]; if (error) { NSLog(@"Could not decode JSON: %@", error.localizedDescription); } return keysDictionary; } - (void) generateOneTimeKeys:(NSUInteger)numberOfKeys { size_t randomLength = olm_account_generate_one_time_keys_random_length(_account, numberOfKeys); NSMutableData *random = [OLMUtility randomBytesOfLength:randomLength]; size_t result = olm_account_generate_one_time_keys(_account, numberOfKeys, random.mutableBytes, random.length); [random resetBytesInRange:NSMakeRange(0, random.length)]; if (result == olm_error()) { const char *error = olm_account_last_error(_account); NSLog(@"error generating keys: %s", error); } } - (BOOL) removeOneTimeKeysForSession:(OLMSession *)session { NSParameterAssert(session != nil); if (!session) { return NO; } size_t result = olm_remove_one_time_keys(self.account, session.session); if (result == olm_error()) { const char *error = olm_account_last_error(_account); NSLog(@"olm_remove_one_time_keys error: %s", error); return NO; } return YES; } - (void)markOneTimeKeysAsPublished { olm_account_mark_keys_as_published(self.account); } #pragma mark OLMSerializable /** Initializes from encrypted serialized data. Will throw error if invalid key or invalid base64. */ - (instancetype) initWithSerializedData:(NSString*)serializedData key:(NSData*)key error:(NSError**)error { self = [self init]; if (!self) { return nil; } NSParameterAssert(key.length > 0); NSParameterAssert(serializedData.length > 0); if (key.length == 0 || serializedData.length == 0) { if (error) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey: @"Bad length."}]; } return nil; } NSMutableData *pickle = [serializedData dataUsingEncoding:NSUTF8StringEncoding].mutableCopy; size_t result = olm_unpickle_account(_account, key.bytes, key.length, pickle.mutableBytes, pickle.length); if (result == olm_error()) { const char *olm_error = olm_account_last_error(_account); NSString *errorString = [NSString stringWithUTF8String:olm_error]; if (error && errorString) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey: errorString}]; } return nil; } return self; } /** Serializes and encrypts object data, outputs base64 blob */ - (NSString*) serializeDataWithKey:(NSData*)key error:(NSError**)error { NSParameterAssert(key.length > 0); size_t length = olm_pickle_account_length(_account); NSMutableData *pickled = [NSMutableData dataWithLength:length]; size_t result = olm_pickle_account(_account, key.bytes, key.length, pickled.mutableBytes, pickled.length); if (result == olm_error()) { const char *olm_error = olm_account_last_error(_account); NSString *errorString = [NSString stringWithUTF8String:olm_error]; if (error && errorString) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey: errorString}]; } return nil; } NSString *pickleString = [[NSString alloc] initWithData:pickled encoding:NSUTF8StringEncoding]; return pickleString; } #pragma mark NSSecureCoding + (BOOL) supportsSecureCoding { return YES; } #pragma mark NSCoding - (id)initWithCoder:(NSCoder *)decoder { NSString *version = [decoder decodeObjectOfClass:[NSString class] forKey:@"version"]; NSError *error = nil; if ([version isEqualToString:@"1"]) { NSString *pickle = [decoder decodeObjectOfClass:[NSString class] forKey:@"pickle"]; NSData *key = [decoder decodeObjectOfClass:[NSData class] forKey:@"key"]; self = [self initWithSerializedData:pickle key:key error:&error]; } NSParameterAssert(error == nil); NSParameterAssert(self != nil); if (!self) { return nil; } return self; } - (void)encodeWithCoder:(NSCoder *)encoder { NSData *key = [OLMUtility randomBytesOfLength:32]; NSError *error = nil; NSString *pickle = [self serializeDataWithKey:key error:&error]; NSParameterAssert(pickle.length > 0 && error == nil); [encoder encodeObject:pickle forKey:@"pickle"]; [encoder encodeObject:key forKey:@"key"]; [encoder encodeObject:@"1" forKey:@"version"]; } @end olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit/OLMAccount_Private.h000066400000000000000000000013361311755073500237350ustar00rootroot00000000000000/* Copyright 2016 Chris Ballinger 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/olm.h" @interface OLMAccount() @property (nonatomic) OlmAccount *account; @end olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit/OLMInboundGroupSession.h000066400000000000000000000024151311755073500246250ustar00rootroot00000000000000/* 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. */ #import #import "OLMSerializable.h" @interface OLMInboundGroupSession : NSObject - (instancetype)initInboundGroupSessionWithSessionKey:(NSString*)sessionKey error:(NSError**)error; - (instancetype)initInboundGroupSessionWithImportedSession:(NSString*)sessionKey error:(NSError**)error; - (NSString*)sessionIdentifier; /** base64 ciphertext -> UTF-8 plaintext */ - (NSString*)decryptMessage:(NSString*)message messageIndex:(NSUInteger*)messageIndex error:(NSError**)error; - (NSUInteger)firstKnownIndex; - (BOOL)isVerified; - (NSString*)exportSessionAtMessageIndex:(NSUInteger)messageIndex error:(NSError**)error; @end olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit/OLMInboundGroupSession.m000066400000000000000000000264501311755073500246370ustar00rootroot00000000000000/* 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. */ #import "OLMInboundGroupSession.h" #import "OLMUtility.h" #include "olm/olm.h" @interface OLMInboundGroupSession () { OlmInboundGroupSession *session; } @end @implementation OLMInboundGroupSession - (void)dealloc { olm_clear_inbound_group_session(session); free(session); } - (instancetype)init { self = [super init]; if (self) { session = malloc(olm_inbound_group_session_size()); if (session) { session = olm_inbound_group_session(session); } if (!session) { return nil; } } return self; } - (instancetype)initInboundGroupSessionWithSessionKey:(NSString *)sessionKey error:(NSError**)error { self = [self init]; if (self) { NSData *sessionKeyData = [sessionKey dataUsingEncoding:NSUTF8StringEncoding]; size_t result = olm_init_inbound_group_session(session, sessionKeyData.bytes, sessionKeyData.length); if (result == olm_error()) { const char *olm_error = olm_inbound_group_session_last_error(session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; NSLog(@"olm_init_inbound_group_session error: %@", errorString); if (error && olm_error && errorString) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{ NSLocalizedDescriptionKey: errorString, NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_init_inbound_group_session error: %@", errorString] }]; } return nil; } } return self; } - (instancetype)initInboundGroupSessionWithImportedSession:(NSString *)sessionKey error:(NSError *__autoreleasing *)error { self = [self init]; if (self) { NSData *sessionKeyData = [sessionKey dataUsingEncoding:NSUTF8StringEncoding]; size_t result = olm_import_inbound_group_session(session, sessionKeyData.bytes, sessionKeyData.length); if (result == olm_error()) { const char *olm_error = olm_inbound_group_session_last_error(session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; NSLog(@"olm_import_inbound_group_session error: %@", errorString); if (error && olm_error && errorString) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{ NSLocalizedDescriptionKey: errorString, NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_import_inbound_group_session error: %@", errorString] }]; } return nil; } } return self; } - (NSString *)sessionIdentifier { size_t length = olm_inbound_group_session_id_length(session); NSMutableData *idData = [NSMutableData dataWithLength:length]; if (!idData) { return nil; } size_t result = olm_inbound_group_session_id(session, idData.mutableBytes, idData.length); if (result == olm_error()) { const char *error = olm_inbound_group_session_last_error(session); NSLog(@"olm_inbound_group_session_id error: %s", error); return nil; } NSString *idString = [[NSString alloc] initWithData:idData encoding:NSUTF8StringEncoding]; return idString; } - (NSString *)decryptMessage:(NSString *)message messageIndex:(NSUInteger*)messageIndex error:(NSError**)error { NSParameterAssert(message != nil); NSData *messageData = [message dataUsingEncoding:NSUTF8StringEncoding]; if (!messageData) { return nil; } NSMutableData *mutMessage = messageData.mutableCopy; size_t maxPlaintextLength = olm_group_decrypt_max_plaintext_length(session, mutMessage.mutableBytes, mutMessage.length); if (maxPlaintextLength == olm_error()) { const char *olm_error = olm_inbound_group_session_last_error(session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; NSLog(@"olm_group_decrypt_max_plaintext_length error: %@", errorString); if (error && olm_error && errorString) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{ NSLocalizedDescriptionKey: errorString, NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_group_decrypt_max_plaintext_length error: %@", errorString] }]; } return nil; } // message buffer is destroyed by olm_group_decrypt_max_plaintext_length mutMessage = messageData.mutableCopy; NSMutableData *plaintextData = [NSMutableData dataWithLength:maxPlaintextLength]; uint32_t message_index; size_t plaintextLength = olm_group_decrypt(session, mutMessage.mutableBytes, mutMessage.length, plaintextData.mutableBytes, plaintextData.length, &message_index); if (plaintextLength == olm_error()) { const char *olm_error = olm_inbound_group_session_last_error(session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; NSLog(@"olm_group_decrypt error: %@", errorString); if (error && olm_error && errorString) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{ NSLocalizedDescriptionKey: errorString, NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_group_decrypt error: %@", errorString] }]; } return nil; } plaintextData.length = plaintextLength; NSString *plaintext = [[NSString alloc] initWithData:plaintextData encoding:NSUTF8StringEncoding]; [plaintextData resetBytesInRange:NSMakeRange(0, plaintextData.length)]; if (messageIndex) { *messageIndex = message_index; } return plaintext; } - (NSUInteger)firstKnownIndex { return olm_inbound_group_session_first_known_index(session); } - (BOOL)isVerified { return (0 != olm_inbound_group_session_is_verified(session)); } - (NSString*)exportSessionAtMessageIndex:(NSUInteger)messageIndex error:(NSError**)error; { size_t length = olm_export_inbound_group_session_length(session); NSMutableData *key = [NSMutableData dataWithLength:length]; size_t result = olm_export_inbound_group_session(session, key.mutableBytes, key.length, messageIndex); if (result == olm_error()) { const char *olm_error = olm_inbound_group_session_last_error(session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; if (error && errorString) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey: errorString}]; } return nil; } NSString *keyString = [[NSString alloc] initWithData:key encoding:NSUTF8StringEncoding]; [key resetBytesInRange:NSMakeRange(0, key.length)]; return keyString; } #pragma mark OLMSerializable /** Initializes from encrypted serialized data. Will throw error if invalid key or invalid base64. */ - (instancetype) initWithSerializedData:(NSString *)serializedData key:(NSData *)key error:(NSError *__autoreleasing *)error { self = [self init]; if (!self) { return nil; } NSParameterAssert(key.length > 0); NSParameterAssert(serializedData.length > 0); if (key.length == 0 || serializedData.length == 0) { if (error) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey: @"Bad length."}]; } return nil; } NSMutableData *pickle = [serializedData dataUsingEncoding:NSUTF8StringEncoding].mutableCopy; size_t result = olm_unpickle_inbound_group_session(session, key.bytes, key.length, pickle.mutableBytes, pickle.length); if (result == olm_error()) { const char *olm_error = olm_inbound_group_session_last_error(session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; if (error && errorString) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey: errorString}]; } return nil; } return self; } /** Serializes and encrypts object data, outputs base64 blob */ - (NSString*) serializeDataWithKey:(NSData*)key error:(NSError**)error { NSParameterAssert(key.length > 0); size_t length = olm_pickle_inbound_group_session_length(session); NSMutableData *pickled = [NSMutableData dataWithLength:length]; size_t result = olm_pickle_inbound_group_session(session, key.bytes, key.length, pickled.mutableBytes, pickled.length); if (result == olm_error()) { const char *olm_error = olm_inbound_group_session_last_error(session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; if (error && errorString) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey: errorString}]; } return nil; } NSString *pickleString = [[NSString alloc] initWithData:pickled encoding:NSUTF8StringEncoding]; return pickleString; } #pragma mark NSSecureCoding + (BOOL) supportsSecureCoding { return YES; } #pragma mark NSCoding - (id)initWithCoder:(NSCoder *)decoder { NSString *version = [decoder decodeObjectOfClass:[NSString class] forKey:@"version"]; NSError *error = nil; if ([version isEqualToString:@"1"]) { NSString *pickle = [decoder decodeObjectOfClass:[NSString class] forKey:@"pickle"]; NSData *key = [decoder decodeObjectOfClass:[NSData class] forKey:@"key"]; self = [self initWithSerializedData:pickle key:key error:&error]; } NSParameterAssert(error == nil); NSParameterAssert(self != nil); if (!self) { return nil; } return self; } - (void)encodeWithCoder:(NSCoder *)encoder { NSData *key = [OLMUtility randomBytesOfLength:32]; NSError *error = nil; NSString *pickle = [self serializeDataWithKey:key error:&error]; NSParameterAssert(pickle.length > 0 && error == nil); [encoder encodeObject:pickle forKey:@"pickle"]; [encoder encodeObject:key forKey:@"key"]; [encoder encodeObject:@"1" forKey:@"version"]; } @end olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit/OLMKit.h000066400000000000000000000021571311755073500214000ustar00rootroot00000000000000/* Copyright 2016 Chris Ballinger 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. */ #import // In this header, you should import all the public headers of your framework using statements like #import #import #import #import #import #import #import @interface OLMKit : NSObject //! Project version string for OLMKit, the same as libolm. + (NSString*)versionString; @end olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit/OLMKit.m000066400000000000000000000015611311755073500214030ustar00rootroot00000000000000/* 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. */ #import "OLMKit.h" #include "olm/olm.h" @implementation OLMKit + (NSString*)versionString { uint8_t major, minor, patch; olm_get_library_version(&major, &minor, &patch); return [NSString stringWithFormat:@"%tu.%tu.%tu", major, minor, patch]; } @end olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit/OLMMessage.h000066400000000000000000000022061311755073500222300ustar00rootroot00000000000000/* Copyright 2016 Chris Ballinger 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. */ #import /* from olm.hh static const size_t OLM_MESSAGE_TYPE_PRE_KEY = 0; static const size_t OLM_MESSAGE_TYPE_MESSAGE = 1; */ typedef NS_ENUM(NSInteger, OLMMessageType) { OLMMessageTypePreKey = 0, OLMMessageTypeMessage = 1 }; @interface OLMMessage : NSObject @property (nonatomic, copy, readonly, nonnull) NSString *ciphertext; @property (readonly) OLMMessageType type; - (nullable instancetype) initWithCiphertext:(nonnull NSString*)ciphertext type:(OLMMessageType)type; @end olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit/OLMMessage.m000066400000000000000000000017271311755073500222440ustar00rootroot00000000000000/* Copyright 2016 Chris Ballinger 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. */ #import "OLMMessage.h" @implementation OLMMessage - (nullable instancetype) initWithCiphertext:(nonnull NSString*)ciphertext type:(OLMMessageType)type { NSParameterAssert(ciphertext != nil); self = [super init]; if (!self) { return nil; } _ciphertext = [ciphertext copy]; _type = type; return self; } @end olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit/OLMOutboundGroupSession.h000066400000000000000000000017511311755073500250300ustar00rootroot00000000000000/* 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. */ #import #import "OLMSerializable.h" @interface OLMOutboundGroupSession : NSObject - (instancetype) initOutboundGroupSession; - (NSString*)sessionIdentifier; - (NSUInteger)messageIndex; - (NSString*)sessionKey; /** UTF-8 plaintext -> base64 ciphertext */ - (NSString*)encryptMessage:(NSString*)message error:(NSError**)error; @end olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit/OLMOutboundGroupSession.m000066400000000000000000000175271311755073500250450ustar00rootroot00000000000000/* 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. */ #import "OLMOutboundGroupSession.h" #import "OLMUtility.h" #include "olm/olm.h" @interface OLMOutboundGroupSession () { OlmOutboundGroupSession *session; } @end @implementation OLMOutboundGroupSession - (void)dealloc { olm_clear_outbound_group_session(session); free(session); } - (instancetype)init { self = [super init]; if (self) { session = malloc(olm_outbound_group_session_size()); if (session) { session = olm_outbound_group_session(session); } if (!session) { return nil; } } return self; } - (instancetype)initOutboundGroupSession { self = [self init]; if (self) { NSMutableData *random = [OLMUtility randomBytesOfLength:olm_init_outbound_group_session_random_length(session)]; size_t result = olm_init_outbound_group_session(session, random.mutableBytes, random.length); [random resetBytesInRange:NSMakeRange(0, random.length)]; if (result == olm_error()) { const char *error = olm_outbound_group_session_last_error(session); NSLog(@"olm_init_outbound_group_session error: %s", error); return nil; } } return self; } - (NSString *)sessionIdentifier { size_t length = olm_outbound_group_session_id_length(session); NSMutableData *idData = [NSMutableData dataWithLength:length]; if (!idData) { return nil; } size_t result = olm_outbound_group_session_id(session, idData.mutableBytes, idData.length); if (result == olm_error()) { const char *error = olm_outbound_group_session_last_error(session); NSLog(@"olm_outbound_group_session_id error: %s", error); return nil; } NSString *idString = [[NSString alloc] initWithData:idData encoding:NSUTF8StringEncoding]; return idString; } - (NSUInteger)messageIndex { return olm_outbound_group_session_message_index(session); } - (NSString *)sessionKey { size_t length = olm_outbound_group_session_key_length(session); NSMutableData *sessionKeyData = [NSMutableData dataWithLength:length]; if (!sessionKeyData) { return nil; } size_t result = olm_outbound_group_session_key(session, sessionKeyData.mutableBytes, sessionKeyData.length); if (result == olm_error()) { const char *error = olm_outbound_group_session_last_error(session); NSLog(@"olm_outbound_group_session_key error: %s", error); return nil; } NSString *sessionKey = [[NSString alloc] initWithData:sessionKeyData encoding:NSUTF8StringEncoding]; [sessionKeyData resetBytesInRange:NSMakeRange(0, sessionKeyData.length)]; return sessionKey; } - (NSString *)encryptMessage:(NSString *)message error:(NSError**)error { NSData *plaintextData = [message dataUsingEncoding:NSUTF8StringEncoding]; size_t ciphertextLength = olm_group_encrypt_message_length(session, plaintextData.length); NSMutableData *ciphertext = [NSMutableData dataWithLength:ciphertextLength]; if (!ciphertext) { return nil; } size_t result = olm_group_encrypt(session, plaintextData.bytes, plaintextData.length, ciphertext.mutableBytes, ciphertext.length); if (result == olm_error()) { const char *olm_error = olm_outbound_group_session_last_error(session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; NSLog(@"olm_group_encrypt error: %@", errorString); if (error && olm_error && errorString) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{ NSLocalizedDescriptionKey: errorString, NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_group_encrypt error: %@", errorString] }]; } return nil; } return [[NSString alloc] initWithData:ciphertext encoding:NSUTF8StringEncoding]; } #pragma mark OLMSerializable /** Initializes from encrypted serialized data. Will throw error if invalid key or invalid base64. */ - (instancetype) initWithSerializedData:(NSString *)serializedData key:(NSData *)key error:(NSError *__autoreleasing *)error { self = [self init]; if (!self) { return nil; } NSParameterAssert(key.length > 0); NSParameterAssert(serializedData.length > 0); if (key.length == 0 || serializedData.length == 0) { if (error) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey: @"Bad length."}]; } return nil; } NSMutableData *pickle = [serializedData dataUsingEncoding:NSUTF8StringEncoding].mutableCopy; size_t result = olm_unpickle_outbound_group_session(session, key.bytes, key.length, pickle.mutableBytes, pickle.length); if (result == olm_error()) { const char *olm_error = olm_outbound_group_session_last_error(session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; if (error && errorString) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey: errorString}]; } return nil; } return self; } /** Serializes and encrypts object data, outputs base64 blob */ - (NSString*) serializeDataWithKey:(NSData*)key error:(NSError**)error { NSParameterAssert(key.length > 0); size_t length = olm_pickle_outbound_group_session_length(session); NSMutableData *pickled = [NSMutableData dataWithLength:length]; size_t result = olm_pickle_outbound_group_session(session, key.bytes, key.length, pickled.mutableBytes, pickled.length); if (result == olm_error()) { const char *olm_error = olm_outbound_group_session_last_error(session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; if (error && errorString) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey: errorString}]; } return nil; } NSString *pickleString = [[NSString alloc] initWithData:pickled encoding:NSUTF8StringEncoding]; return pickleString; } #pragma mark NSSecureCoding + (BOOL) supportsSecureCoding { return YES; } #pragma mark NSCoding - (id)initWithCoder:(NSCoder *)decoder { NSString *version = [decoder decodeObjectOfClass:[NSString class] forKey:@"version"]; NSError *error = nil; if ([version isEqualToString:@"1"]) { NSString *pickle = [decoder decodeObjectOfClass:[NSString class] forKey:@"pickle"]; NSData *key = [decoder decodeObjectOfClass:[NSData class] forKey:@"key"]; self = [self initWithSerializedData:pickle key:key error:&error]; } NSParameterAssert(error == nil); NSParameterAssert(self != nil); if (!self) { return nil; } return self; } - (void)encodeWithCoder:(NSCoder *)encoder { NSData *key = [OLMUtility randomBytesOfLength:32]; NSError *error = nil; NSString *pickle = [self serializeDataWithKey:key error:&error]; NSParameterAssert(pickle.length > 0 && error == nil); [encoder encodeObject:pickle forKey:@"pickle"]; [encoder encodeObject:key forKey:@"key"]; [encoder encodeObject:@"1" forKey:@"version"]; } @end olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit/OLMSerializable.h000066400000000000000000000020471311755073500232550ustar00rootroot00000000000000/* Copyright 2016 Chris Ballinger 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. */ #import @protocol OLMSerializable /** Initializes from encrypted serialized data. Will throw error if invalid key or invalid base64. */ - (instancetype) initWithSerializedData:(NSString*)serializedData key:(NSData*)key error:(NSError**)error; /** Serializes and encrypts object data, outputs base64 blob */ - (NSString*) serializeDataWithKey:(NSData*)key error:(NSError**)error; @end olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit/OLMSession.h000066400000000000000000000033561311755073500222760ustar00rootroot00000000000000/* Copyright 2016 Chris Ballinger 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. */ #import #import "OLMSerializable.h" #import "OLMAccount.h" #import "OLMMessage.h" @interface OLMSession : NSObject - (instancetype) initOutboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSString*)theirIdentityKey theirOneTimeKey:(NSString*)theirOneTimeKey error:(NSError**)error; - (instancetype) initInboundSessionWithAccount:(OLMAccount*)account oneTimeKeyMessage:(NSString*)oneTimeKeyMessage error:(NSError**)error; - (instancetype) initInboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSString*)theirIdentityKey oneTimeKeyMessage:(NSString*)oneTimeKeyMessage error:(NSError**)error; - (NSString*) sessionIdentifier; - (BOOL) matchesInboundSession:(NSString*)oneTimeKeyMessage; - (BOOL) matchesInboundSessionFrom:(NSString*)theirIdentityKey oneTimeKeyMessage:(NSString *)oneTimeKeyMessage; /** UTF-8 plaintext -> base64 ciphertext */ - (OLMMessage*) encryptMessage:(NSString*)message error:(NSError**)error; /** base64 ciphertext -> UTF-8 plaintext */ - (NSString*) decryptMessage:(OLMMessage*)message error:(NSError**)error; @end olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit/OLMSession.m000066400000000000000000000364151311755073500223050ustar00rootroot00000000000000/* Copyright 2016 Chris Ballinger 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. */ #import "OLMSession.h" #import "OLMUtility.h" #import "OLMAccount_Private.h" #import "OLMSession_Private.h" #include "olm/olm.h" @implementation OLMSession - (void) dealloc { olm_clear_session(_session); free(_session); } - (BOOL) initializeSessionMemory { size_t size = olm_session_size(); _session = malloc(size); NSParameterAssert(_session != nil); if (!_session) { return NO; } _session = olm_session(_session); NSParameterAssert(_session != nil); if (!_session) { return NO; } return YES; } - (instancetype) init { self = [super init]; if (!self) { return nil; } BOOL success = [self initializeSessionMemory]; if (!success) { return nil; } return self; } - (instancetype) initWithAccount:(OLMAccount*)account { self = [self init]; if (!self) { return nil; } NSParameterAssert(account != nil && account.account != NULL); if (account == nil || account.account == NULL) { return nil; } _account = account; return self; } - (instancetype) initOutboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSString*)theirIdentityKey theirOneTimeKey:(NSString*)theirOneTimeKey error:(NSError**)error { self = [self initWithAccount:account]; if (!self) { return nil; } NSMutableData *random = [OLMUtility randomBytesOfLength:olm_create_outbound_session_random_length(_session)]; NSData *idKey = [theirIdentityKey dataUsingEncoding:NSUTF8StringEncoding]; NSData *otKey = [theirOneTimeKey dataUsingEncoding:NSUTF8StringEncoding]; size_t result = olm_create_outbound_session(_session, account.account, idKey.bytes, idKey.length, otKey.bytes, otKey.length, random.mutableBytes, random.length); [random resetBytesInRange:NSMakeRange(0, random.length)]; if (result == olm_error()) { const char *olm_error = olm_session_last_error(_session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; NSLog(@"olm_create_outbound_session error: %@", errorString); if (error && olm_error && errorString) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{ NSLocalizedDescriptionKey: errorString, NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_create_outbound_session error: %@", errorString] }]; } return nil; } return self; } - (instancetype) initInboundSessionWithAccount:(OLMAccount*)account oneTimeKeyMessage:(NSString*)oneTimeKeyMessage error:(NSError**)error { self = [self initWithAccount:account]; if (!self) { return nil; } NSMutableData *otk = [NSMutableData dataWithData:[oneTimeKeyMessage dataUsingEncoding:NSUTF8StringEncoding]]; size_t result = olm_create_inbound_session(_session, account.account, otk.mutableBytes, oneTimeKeyMessage.length); if (result == olm_error()) { const char *olm_error = olm_session_last_error(_session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; NSLog(@"olm_create_inbound_session error: %@", errorString); if (error && olm_error && errorString) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{ NSLocalizedDescriptionKey: errorString, NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_create_inbound_session error: %@", errorString] }]; } return nil; } return self; } - (instancetype) initInboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSString*)theirIdentityKey oneTimeKeyMessage:(NSString*)oneTimeKeyMessage error:(NSError**)error { self = [self initWithAccount:account]; if (!self) { return nil; } NSData *idKey = [theirIdentityKey dataUsingEncoding:NSUTF8StringEncoding]; NSMutableData *otk = [NSMutableData dataWithData:[oneTimeKeyMessage dataUsingEncoding:NSUTF8StringEncoding]]; size_t result = olm_create_inbound_session_from(_session, account.account, idKey.bytes, idKey.length, otk.mutableBytes, otk.length); if (result == olm_error()) { const char *olm_error = olm_session_last_error(_session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; NSLog(@"olm_create_inbound_session_from error: %@", errorString); if (error && olm_error && errorString) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{ NSLocalizedDescriptionKey: errorString, NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_create_inbound_session_from error: %@", errorString] }]; } return nil; } return self; } - (NSString*) sessionIdentifier { size_t length = olm_session_id_length(_session); NSMutableData *idData = [NSMutableData dataWithLength:length]; if (!idData) { return nil; } size_t result = olm_session_id(_session, idData.mutableBytes, idData.length); if (result == olm_error()) { const char *error = olm_session_last_error(_session); NSLog(@"olm_session_id error: %s", error); return nil; } NSString *idString = [[NSString alloc] initWithData:idData encoding:NSUTF8StringEncoding]; return idString; } - (BOOL)matchesInboundSession:(NSString *)oneTimeKeyMessage { NSMutableData *otk = [NSMutableData dataWithData:[oneTimeKeyMessage dataUsingEncoding:NSUTF8StringEncoding]]; size_t result = olm_matches_inbound_session(_session, otk.mutableBytes, otk.length); if (result == 1) { return YES; } else { if (result == olm_error()) { const char *error = olm_session_last_error(_session); NSLog(@"olm_matches_inbound_session error: %s", error); } return NO; } } - (BOOL)matchesInboundSessionFrom:(NSString *)theirIdentityKey oneTimeKeyMessage:(NSString *)oneTimeKeyMessage { NSData *idKey = [theirIdentityKey dataUsingEncoding:NSUTF8StringEncoding]; NSMutableData *otk = [NSMutableData dataWithData:[oneTimeKeyMessage dataUsingEncoding:NSUTF8StringEncoding]]; size_t result = olm_matches_inbound_session_from(_session, idKey.bytes, idKey.length, otk.mutableBytes, otk.length); if (result == 1) { return YES; } else { if (result == olm_error()) { const char *error = olm_session_last_error(_session); NSLog(@"olm_matches_inbound_session error: %s", error); } return NO; } } - (OLMMessage*) encryptMessage:(NSString*)message error:(NSError**)error { size_t messageType = olm_encrypt_message_type(_session); size_t randomLength = olm_encrypt_random_length(_session); NSMutableData *random = [OLMUtility randomBytesOfLength:randomLength]; NSData *plaintextData = [message dataUsingEncoding:NSUTF8StringEncoding]; size_t ciphertextLength = olm_encrypt_message_length(_session, plaintextData.length); NSMutableData *ciphertext = [NSMutableData dataWithLength:ciphertextLength]; if (!ciphertext) { return nil; } size_t result = olm_encrypt(_session, plaintextData.bytes, plaintextData.length, random.mutableBytes, random.length, ciphertext.mutableBytes, ciphertext.length); [random resetBytesInRange:NSMakeRange(0, random.length)]; if (result == olm_error()) { const char *olm_error = olm_session_last_error(_session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; NSLog(@"olm_encrypt error: %@", errorString); if (error && olm_error && errorString) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{ NSLocalizedDescriptionKey: errorString, NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_encrypt error: %@", errorString] }]; } return nil; } NSString *ciphertextString = [[NSString alloc] initWithData:ciphertext encoding:NSUTF8StringEncoding]; OLMMessage *encryptedMessage = [[OLMMessage alloc] initWithCiphertext:ciphertextString type:messageType]; return encryptedMessage; } - (NSString*) decryptMessage:(OLMMessage*)message error:(NSError**)error { NSParameterAssert(message != nil); NSData *messageData = [message.ciphertext dataUsingEncoding:NSUTF8StringEncoding]; if (!messageData) { return nil; } NSMutableData *mutMessage = messageData.mutableCopy; size_t maxPlaintextLength = olm_decrypt_max_plaintext_length(_session, message.type, mutMessage.mutableBytes, mutMessage.length); if (maxPlaintextLength == olm_error()) { const char *olm_error = olm_session_last_error(_session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; NSLog(@"olm_decrypt_max_plaintext_length error: %@", errorString); if (error && olm_error && errorString) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{ NSLocalizedDescriptionKey: errorString, NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_decrypt_max_plaintext_length error: %@", errorString] }]; } return nil; } // message buffer is destroyed by olm_decrypt_max_plaintext_length mutMessage = messageData.mutableCopy; NSMutableData *plaintextData = [NSMutableData dataWithLength:maxPlaintextLength]; size_t plaintextLength = olm_decrypt(_session, message.type, mutMessage.mutableBytes, mutMessage.length, plaintextData.mutableBytes, plaintextData.length); if (plaintextLength == olm_error()) { const char *olm_error = olm_session_last_error(_session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; NSLog(@"olm_decrypt error: %@", errorString); if (error && olm_error && errorString) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{ NSLocalizedDescriptionKey: errorString, NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"olm_decrypt error: %@", errorString] }]; } return nil; } plaintextData.length = plaintextLength; NSString *plaintext = [[NSString alloc] initWithData:plaintextData encoding:NSUTF8StringEncoding]; [plaintextData resetBytesInRange:NSMakeRange(0, plaintextData.length)]; return plaintext; } #pragma mark OLMSerializable /** Initializes from encrypted serialized data. Will throw error if invalid key or invalid base64. */ - (instancetype) initWithSerializedData:(NSString*)serializedData key:(NSData*)key error:(NSError**)error { self = [self init]; if (!self) { return nil; } NSParameterAssert(key.length > 0); NSParameterAssert(serializedData.length > 0); if (key.length == 0 || serializedData.length == 0) { if (error) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey: @"Bad length."}]; } return nil; } NSMutableData *pickle = [serializedData dataUsingEncoding:NSUTF8StringEncoding].mutableCopy; size_t result = olm_unpickle_session(_session, key.bytes, key.length, pickle.mutableBytes, pickle.length); if (result == olm_error()) { const char *olm_error = olm_session_last_error(_session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; if (error && errorString) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey: errorString}]; } return nil; } return self; } /** Serializes and encrypts object data, outputs base64 blob */ - (NSString*) serializeDataWithKey:(NSData*)key error:(NSError**)error { NSParameterAssert(key.length > 0); size_t length = olm_pickle_session_length(_session); NSMutableData *pickled = [NSMutableData dataWithLength:length]; size_t result = olm_pickle_session(_session, key.bytes, key.length, pickled.mutableBytes, pickled.length); if (result == olm_error()) { const char *olm_error = olm_session_last_error(_session); NSString *errorString = [NSString stringWithUTF8String:olm_error]; if (error && errorString) { *error = [NSError errorWithDomain:OLMErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey: errorString}]; } return nil; } NSString *pickleString = [[NSString alloc] initWithData:pickled encoding:NSUTF8StringEncoding]; return pickleString; } #pragma mark NSSecureCoding + (BOOL) supportsSecureCoding { return YES; } #pragma mark NSCoding - (id)initWithCoder:(NSCoder *)decoder { NSString *version = [decoder decodeObjectOfClass:[NSString class] forKey:@"version"]; NSError *error = nil; if ([version isEqualToString:@"1"]) { NSString *pickle = [decoder decodeObjectOfClass:[NSString class] forKey:@"pickle"]; NSData *key = [decoder decodeObjectOfClass:[NSData class] forKey:@"key"]; self = [self initWithSerializedData:pickle key:key error:&error]; } NSParameterAssert(error == nil); NSParameterAssert(self != nil); if (!self) { return nil; } return self; } - (void)encodeWithCoder:(NSCoder *)encoder { NSData *key = [OLMUtility randomBytesOfLength:32]; NSError *error = nil; NSString *pickle = [self serializeDataWithKey:key error:&error]; NSParameterAssert(pickle.length > 0 && error == nil); [encoder encodeObject:pickle forKey:@"pickle"]; [encoder encodeObject:key forKey:@"key"]; [encoder encodeObject:@"1" forKey:@"version"]; } @end olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit/OLMSession_Private.h000066400000000000000000000014211311755073500237570ustar00rootroot00000000000000/* Copyright 2016 Chris Ballinger 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/olm.h" @interface OLMSession() @property (nonatomic) OlmSession *session; @property (nonatomic, strong) OLMAccount *account; @end olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit/OLMUtility.h000066400000000000000000000030261311755073500223100ustar00rootroot00000000000000/* Copyright 2016 Chris Ballinger 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. */ #import FOUNDATION_EXPORT NSString *const OLMErrorDomain; @interface OLMUtility : NSObject /** Calculate the SHA-256 hash of the input and encodes it as base64. @param message the message to hash. @return the base64-encoded hash value. */ - (NSString*)sha256:(NSData*)message; /** Verify an ed25519 signature. @param signature the base64-encoded signature to be checked. @param key the ed25519 key. @param message the message which was signed. @param the result error if there is a problem with the verification. If the key was too small then the message will be "OLM.INVALID_BASE64". If the signature was invalid then the message will be "OLM.BAD_MESSAGE_MAC". @return YES if valid. */ - (BOOL)verifyEd25519Signature:(NSString*)signature key:(NSString*)key message:(NSData*)message error:(NSError**)error; + (NSMutableData*) randomBytesOfLength:(NSUInteger)length; @end olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKit/OLMUtility.m000066400000000000000000000065241311755073500223230ustar00rootroot00000000000000/* Copyright 2016 Chris Ballinger 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. */ #import "OLMUtility.h" #include "olm/olm.h" NSString *const OLMErrorDomain = @"org.matrix.olm"; @interface OLMUtility() @property (nonatomic) OlmUtility *utility; @end @implementation OLMUtility - (void) dealloc { olm_clear_utility(_utility); free(_utility); } - (BOOL) initializeUtilityMemory { size_t utilitySize = olm_utility_size(); _utility = malloc(utilitySize); NSParameterAssert(_utility != nil); if (!_utility) { return NO; } _utility = olm_utility(_utility); NSParameterAssert(_utility != nil); if (!_utility) { return NO; } return YES; } - (instancetype) init { self = [super init]; if (!self) { return nil; } BOOL success = [self initializeUtilityMemory]; if (!success) { return nil; } return self; } - (NSString *)sha256:(NSData *)message { size_t length = olm_sha256_length(_utility); NSMutableData *shaData = [NSMutableData dataWithLength:length]; if (!shaData) { return nil; } size_t result = olm_sha256(_utility, message.bytes, message.length, shaData.mutableBytes, shaData.length); if (result == olm_error()) { const char *error = olm_utility_last_error(_utility); NSLog(@"olm_sha256 error: %s", error); return nil; } NSString *sha = [[NSString alloc] initWithData:shaData encoding:NSUTF8StringEncoding]; return sha; } - (BOOL)verifyEd25519Signature:(NSString*)signature key:(NSString*)key message:(NSData*)message error:(NSError**)error { NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding]; NSData *signatureData = [signature dataUsingEncoding:NSUTF8StringEncoding]; size_t result = olm_ed25519_verify(_utility, keyData.bytes, keyData.length, message.bytes, message.length, (void*)signatureData.bytes, signatureData.length ); if (result == olm_error()) { if (error) { NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: [NSString stringWithUTF8String:olm_utility_last_error(_utility)]}; // @TODO *error = [[NSError alloc] initWithDomain:@"OLMKitErrorDomain" code:0 userInfo:userInfo]; } return NO; } else { return YES; } } + (NSMutableData*) randomBytesOfLength:(NSUInteger)length { NSMutableData *randomData = [NSMutableData dataWithLength:length]; if (!randomData) { return nil; } int result = SecRandomCopyBytes(kSecRandomDefault, randomData.length, randomData.mutableBytes); if (result != 0) { return nil; } return randomData; } @end olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKitTests/000077500000000000000000000000001311755073500211465ustar00rootroot00000000000000olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKitTests/Info.plist000066400000000000000000000012501311755073500231140ustar00rootroot00000000000000 CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleVersion 1 olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKitTests/OLMKitGroupTests.m000066400000000000000000000125001311755073500244610ustar00rootroot00000000000000/* 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. */ #import #import #include "olm/olm.h" @interface OLMKitGroupTests : XCTestCase @end @implementation OLMKitGroupTests - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. [super tearDown]; } - (void)testAliceAndBob { NSError *error; OLMOutboundGroupSession *aliceSession = [[OLMOutboundGroupSession alloc] initOutboundGroupSession]; XCTAssertGreaterThan(aliceSession.sessionIdentifier.length, 0); XCTAssertGreaterThan(aliceSession.sessionKey.length, 0); XCTAssertEqual(aliceSession.messageIndex, 0); // Store the session key before starting encrypting NSString *sessionKey = aliceSession.sessionKey; NSString *message = @"Hello!"; NSString *aliceToBobMsg = [aliceSession encryptMessage:message error:&error]; XCTAssertEqual(aliceSession.messageIndex, 1); XCTAssertGreaterThanOrEqual(aliceToBobMsg.length, 0); XCTAssertNil(error); OLMInboundGroupSession *bobSession = [[OLMInboundGroupSession alloc] initInboundGroupSessionWithSessionKey:sessionKey error:&error]; XCTAssertEqualObjects(aliceSession.sessionIdentifier, bobSession.sessionIdentifier); XCTAssertNil(error); NSUInteger messageIndex; NSString *plaintext = [bobSession decryptMessage:aliceToBobMsg messageIndex:&messageIndex error:&error]; XCTAssertEqualObjects(message, plaintext); XCTAssertEqual(messageIndex, 0); XCTAssertNil(error); } - (void)testOutboundGroupSessionSerialization { OLMOutboundGroupSession *aliceSession = [[OLMOutboundGroupSession alloc] initOutboundGroupSession]; NSData *aliceData = [NSKeyedArchiver archivedDataWithRootObject:aliceSession]; OLMOutboundGroupSession *aliceSession2 = [NSKeyedUnarchiver unarchiveObjectWithData:aliceData]; XCTAssertEqualObjects(aliceSession2.sessionKey, aliceSession.sessionKey); XCTAssertEqualObjects(aliceSession2.sessionIdentifier, aliceSession.sessionIdentifier); } - (void)testInboundGroupSessionSerialization { OLMOutboundGroupSession *aliceSession = [[OLMOutboundGroupSession alloc] initOutboundGroupSession]; OLMInboundGroupSession *bobSession = [[OLMInboundGroupSession alloc] initInboundGroupSessionWithSessionKey:aliceSession.sessionKey error:nil]; NSData *bobData = [NSKeyedArchiver archivedDataWithRootObject:bobSession]; OLMInboundGroupSession *bobSession2 = [NSKeyedUnarchiver unarchiveObjectWithData:bobData]; XCTAssertEqualObjects(bobSession2.sessionIdentifier, aliceSession.sessionIdentifier); } - (void)testInboundGroupSessionImportExport { NSError *error; NSString *sessionKey = @"AgAAAAAwMTIzNDU2Nzg5QUJERUYwMTIzNDU2Nzg5QUJDREVGMDEyMzQ1Njc4OUFCREVGM" \ "DEyMzQ1Njc4OUFCQ0RFRjAxMjM0NTY3ODlBQkRFRjAxMjM0NTY3ODlBQkNERUYwMTIzND" \ "U2Nzg5QUJERUYwMTIzNDU2Nzg5QUJDREVGMDEyMw0bdg1BDq4Px/slBow06q8n/B9WBfw" \ "WYyNOB8DlUmXGGwrFmaSb9bR/eY8xgERrxmP07hFmD9uqA2p8PMHdnV5ysmgufE6oLZ5+" \ "8/mWQOW3VVTnDIlnwd8oHUYRuk8TCQ"; NSString *message = @"AwgAEhAcbh6UpbByoyZxufQ+h2B+8XHMjhR69G8F4+qjMaFlnIXusJZX3r8LnRORG9T3D" \ "XFdbVuvIWrLyRfm4i8QRbe8VPwGRFG57B1CtmxanuP8bHtnnYqlwPsD"; // init first inbound group session, and decrypt */ OLMInboundGroupSession *session1 = [[OLMInboundGroupSession alloc] initInboundGroupSessionWithSessionKey:sessionKey error:&error]; XCTAssertNil(error); XCTAssertTrue(session1.isVerified); // decrypt the message NSUInteger messageIndex; NSString *plaintext = [session1 decryptMessage:message messageIndex:&messageIndex error:&error]; XCTAssertNil(error); XCTAssertEqualObjects(plaintext, @"Message"); XCTAssertEqual(messageIndex, 0); // export the keys NSString *export = [session1 exportSessionAtMessageIndex:0 error:&error]; XCTAssertNil(error); XCTAssertGreaterThan(export.length, 0); // free the old session to check there is no shared data session1 = nil; // import the keys into another inbound group session OLMInboundGroupSession *session2 = [[OLMInboundGroupSession alloc] initInboundGroupSessionWithImportedSession:export error:&error]; XCTAssertNil(error); XCTAssert(session2); XCTAssertFalse(session2.isVerified); // decrypt the message with the new session NSString *plaintext2 = [session2 decryptMessage:message messageIndex:&messageIndex error:&error]; XCTAssertNil(error); XCTAssertEqualObjects(plaintext2, @"Message"); XCTAssertEqual(messageIndex, 0); XCTAssertTrue(session2.isVerified); } @end olm-2.2.2+git20170526.0fd768e+dfsg/xcode/OLMKitTests/OLMKitTests.m000066400000000000000000000210311311755073500234430ustar00rootroot00000000000000/* Copyright 2016 Chris Ballinger 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. */ #import #import @interface OLMKitTests : XCTestCase @end @implementation OLMKitTests - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. [super tearDown]; } - (void)testAliceAndBob { NSError *error; OLMAccount *alice = [[OLMAccount alloc] initNewAccount]; OLMAccount *bob = [[OLMAccount alloc] initNewAccount]; [bob generateOneTimeKeys:5]; NSDictionary *bobIdKeys = bob.identityKeys; NSString *bobIdKey = bobIdKeys[@"curve25519"]; NSDictionary *bobOneTimeKeys = bob.oneTimeKeys; NSParameterAssert(bobIdKey != nil); NSParameterAssert(bobOneTimeKeys != nil); __block NSString *bobOneTimeKey = nil; NSDictionary *bobOtkCurve25519 = bobOneTimeKeys[@"curve25519"]; [bobOtkCurve25519 enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { bobOneTimeKey = obj; }]; XCTAssert([bobOneTimeKey isKindOfClass:[NSString class]]); OLMSession *aliceSession = [[OLMSession alloc] initOutboundSessionWithAccount:alice theirIdentityKey:bobIdKey theirOneTimeKey:bobOneTimeKey error:nil]; NSString *message = @"Hello!"; OLMMessage *aliceToBobMsg = [aliceSession encryptMessage:message error:&error]; XCTAssertNil(error); OLMSession *bobSession = [[OLMSession alloc] initInboundSessionWithAccount:bob oneTimeKeyMessage:aliceToBobMsg.ciphertext error:nil]; NSString *plaintext = [bobSession decryptMessage:aliceToBobMsg error:&error]; XCTAssertEqualObjects(message, plaintext); XCTAssertNil(error); XCTAssert([bobSession matchesInboundSession:aliceToBobMsg.ciphertext]); XCTAssertFalse([aliceSession matchesInboundSession:@"ARandomOtkMessage"]); NSString *aliceIdKey = alice.identityKeys[@"curve25519"]; XCTAssert([bobSession matchesInboundSessionFrom:aliceIdKey oneTimeKeyMessage:aliceToBobMsg.ciphertext]); XCTAssertFalse([bobSession matchesInboundSessionFrom:@"ARandomIdKey" oneTimeKeyMessage:aliceToBobMsg.ciphertext]); XCTAssertFalse([bobSession matchesInboundSessionFrom:aliceIdKey oneTimeKeyMessage:@"ARandomOtkMessage"]); BOOL success = [bob removeOneTimeKeysForSession:bobSession]; XCTAssertTrue(success); } - (void) testBackAndForth { OLMAccount *alice = [[OLMAccount alloc] initNewAccount]; OLMAccount *bob = [[OLMAccount alloc] initNewAccount]; [bob generateOneTimeKeys:1]; NSDictionary *bobIdKeys = bob.identityKeys; NSString *bobIdKey = bobIdKeys[@"curve25519"]; NSDictionary *bobOneTimeKeys = bob.oneTimeKeys; NSParameterAssert(bobIdKey != nil); NSParameterAssert(bobOneTimeKeys != nil); __block NSString *bobOneTimeKey = nil; NSDictionary *bobOtkCurve25519 = bobOneTimeKeys[@"curve25519"]; [bobOtkCurve25519 enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { bobOneTimeKey = obj; }]; XCTAssert([bobOneTimeKey isKindOfClass:[NSString class]]); OLMSession *aliceSession = [[OLMSession alloc] initOutboundSessionWithAccount:alice theirIdentityKey:bobIdKey theirOneTimeKey:bobOneTimeKey error:nil]; NSString *message = @"Hello I'm Alice!"; OLMMessage *aliceToBobMsg = [aliceSession encryptMessage:message error:nil]; OLMSession *bobSession = [[OLMSession alloc] initInboundSessionWithAccount:bob oneTimeKeyMessage:aliceToBobMsg.ciphertext error:nil]; NSString *plaintext = [bobSession decryptMessage:aliceToBobMsg error:nil]; XCTAssertEqualObjects(message, plaintext); BOOL success = [bob removeOneTimeKeysForSession:bobSession]; XCTAssertTrue(success); NSString *msg1 = @"Hello I'm Bob!"; NSString *msg2 = @"Isn't life grand?"; NSString *msg3 = @"Let's go to the opera."; OLMMessage *eMsg1 = [bobSession encryptMessage:msg1 error:nil]; OLMMessage *eMsg2 = [bobSession encryptMessage:msg2 error:nil]; OLMMessage *eMsg3 = [bobSession encryptMessage:msg3 error:nil]; NSString *dMsg1 = [aliceSession decryptMessage:eMsg1 error:nil]; NSString *dMsg2 = [aliceSession decryptMessage:eMsg2 error:nil]; NSString *dMsg3 = [aliceSession decryptMessage:eMsg3 error:nil]; XCTAssertEqualObjects(msg1, dMsg1); XCTAssertEqualObjects(msg2, dMsg2); XCTAssertEqualObjects(msg3, dMsg3); } - (void) testAccountSerialization { OLMAccount *bob = [[OLMAccount alloc] initNewAccount]; [bob generateOneTimeKeys:5]; NSDictionary *bobIdKeys = bob.identityKeys; NSDictionary *bobOneTimeKeys = bob.oneTimeKeys; NSData *bobData = [NSKeyedArchiver archivedDataWithRootObject:bob]; OLMAccount *bob2 = [NSKeyedUnarchiver unarchiveObjectWithData:bobData]; NSDictionary *bobIdKeys2 = bob2.identityKeys; NSDictionary *bobOneTimeKeys2 = bob2.oneTimeKeys; XCTAssertEqualObjects(bobIdKeys, bobIdKeys2); XCTAssertEqualObjects(bobOneTimeKeys, bobOneTimeKeys2); } - (void) testSessionSerialization { NSError *error; OLMAccount *alice = [[OLMAccount alloc] initNewAccount]; OLMAccount *bob = [[OLMAccount alloc] initNewAccount]; [bob generateOneTimeKeys:1]; NSDictionary *bobIdKeys = bob.identityKeys; NSString *bobIdKey = bobIdKeys[@"curve25519"]; NSDictionary *bobOneTimeKeys = bob.oneTimeKeys; NSParameterAssert(bobIdKey != nil); NSParameterAssert(bobOneTimeKeys != nil); __block NSString *bobOneTimeKey = nil; NSDictionary *bobOtkCurve25519 = bobOneTimeKeys[@"curve25519"]; [bobOtkCurve25519 enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { bobOneTimeKey = obj; }]; XCTAssert([bobOneTimeKey isKindOfClass:[NSString class]]); OLMSession *aliceSession = [[OLMSession alloc] initOutboundSessionWithAccount:alice theirIdentityKey:bobIdKey theirOneTimeKey:bobOneTimeKey error:nil]; NSString *message = @"Hello I'm Alice!"; OLMMessage *aliceToBobMsg = [aliceSession encryptMessage:message error:&error]; XCTAssertNil(error); OLMSession *bobSession = [[OLMSession alloc] initInboundSessionWithAccount:bob oneTimeKeyMessage:aliceToBobMsg.ciphertext error:nil]; NSString *plaintext = [bobSession decryptMessage:aliceToBobMsg error:nil]; XCTAssertEqualObjects(message, plaintext); BOOL success = [bob removeOneTimeKeysForSession:bobSession]; XCTAssertTrue(success); NSString *msg1 = @"Hello I'm Bob!"; NSString *msg2 = @"Isn't life grand?"; NSString *msg3 = @"Let's go to the opera."; OLMMessage *eMsg1 = [bobSession encryptMessage:msg1 error:nil]; OLMMessage *eMsg2 = [bobSession encryptMessage:msg2 error:nil]; OLMMessage *eMsg3 = [bobSession encryptMessage:msg3 error:nil]; NSData *aliceData = [NSKeyedArchiver archivedDataWithRootObject:aliceSession]; OLMSession *alice2 = [NSKeyedUnarchiver unarchiveObjectWithData:aliceData]; NSString *dMsg1 = [alice2 decryptMessage:eMsg1 error:nil]; NSString *dMsg2 = [alice2 decryptMessage:eMsg2 error:nil]; NSString *dMsg3 = [alice2 decryptMessage:eMsg3 error:nil]; XCTAssertEqualObjects(msg1, dMsg1); XCTAssertEqualObjects(msg2, dMsg2); XCTAssertEqualObjects(msg3, dMsg3); } - (void)testEd25519Signing { OLMUtility *olmUtility = [[OLMUtility alloc] init]; OLMAccount *alice = [[OLMAccount alloc] initNewAccount]; NSDictionary *aJSON = @{ @"key1": @"value1", @"key2": @"value2" }; NSData *message = [NSKeyedArchiver archivedDataWithRootObject:aJSON]; NSString *signature = [alice signMessage:message]; NSString *aliceEd25519Key = alice.identityKeys[@"ed25519"]; NSError *error; BOOL result = [olmUtility verifyEd25519Signature:signature key:aliceEd25519Key message:message error:&error]; XCTAssert(result); XCTAssertNil(error); } @end olm-2.2.2+git20170526.0fd768e+dfsg/xcode/Podfile000066400000000000000000000002111311755073500203230ustar00rootroot00000000000000target "OLMKit" do pod 'OLMKit', :path => '../OLMKit.podspec' end target "OLMKitTests" do pod 'OLMKit', :path => '../OLMKit.podspec' endolm-2.2.2+git20170526.0fd768e+dfsg/xcode/Podfile.lock000066400000000000000000000006071311755073500212630ustar00rootroot00000000000000PODS: - OLMKit (2.0.1): - OLMKit/olmc (= 2.0.1) - OLMKit/olmcpp (= 2.0.1) - OLMKit/olmc (2.0.1) - OLMKit/olmcpp (2.0.1) DEPENDENCIES: - OLMKit (from `../OLMKit.podspec`) EXTERNAL SOURCES: OLMKit: :path: ../OLMKit.podspec SPEC CHECKSUMS: OLMKit: 12a35a69f92c7facdd50b559128d1b4a17857ba7 PODFILE CHECKSUM: 4e261dae61d833ec5585ced2473023b98909fd35 COCOAPODS: 1.1.1 olm-2.2.2+git20170526.0fd768e+dfsg/xcode/README.rst000066400000000000000000000013151311755073500205130ustar00rootroot00000000000000OLMKit ====== OLMKit exposes an Objective-C wrapper to libolm. The original work by Chris Ballinger can be found at https://github.com/chrisballinger/OLMKit. Installation ------------ You can embed OLMKit to your application project with CocoaPods. The pod for the latest OLMKit release is:: pod 'OLMKit' Development ----------- Run `pod install` and open `OLMKit.xcworkspace`. The project contains only tests files. The libolm and the Objective-C wrapper source files are loaded via the OLMKit CocoaPods pod. To add a new source file, add it to the file system and run `pod update` to make CocoaPods insert it into OLMKit.xcworkspace. Release ------- See ../README.rst for the release of the CocoaPod.